-
Hello, not sure I'm at the right place with this, didn't want to pollute the issues: I've recently bumped into a problem that I don't seem to be able to correct the way I want to. I've put together a minimal rudimentary example with the error, perhaps you could advise as to what I'm thinking/doing wrong: Code sample in pyright playground from ipaddress import IPv4Network, IPv6Network, ip_network
from typing import NamedTuple
class _WhitelistType(NamedTuple):
v4: dict[IPv4Network, bool]
v6: dict[IPv6Network, bool]
net_str = ['127.0.0.0/24', '192.168.0.0/16']
nets = [ip_network(address=x) for x in net_str]
wl = _WhitelistType(
v4={IPv4Network(address='127.0.0.0/24'): True},
v6={IPv6Network(address='127.0.0./32'): True},
)
def test(net: IPv4Network | IPv6Network):
searched_wl = wl.v4 if isinstance(net, IPv4Network) else wl.v6
for ipnet_item in searched_wl:
if net.subnet_of(other=ipnet_item):
print('Yes') The problem is with the second to last line (see the linked example). Pyright complains,
I would think that the variable type of ipnet_item should be detected properly. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
Static type checkers like pyright apply a technique called "type narrowing". You can read more about this here. In your example above, the type of the expression It is theoretically possible to narrow types conditionally — for example, "the type of You'll see that if you duplicate your code, the type error disappears. In this version, type narrowing does apply. The type of def test(net: IPv4Network | IPv6Network):
if isinstance(net, IPv4Network):
searched_wl = wl.v4
for ipnet_item in searched_wl:
if net.subnet_of(other=ipnet_item):
print("Yes")
else:
searched_wl = wl.v6
for ipnet_item in searched_wl:
if net.subnet_of(other=ipnet_item):
print("Yes") Duplicating code like this is not something I'd recommend, so you may want to consider different ways of accomplishing your goal through restructuring. If you don't want to change your code, you could suppress the type error using a |
Beta Was this translation helpful? Give feedback.
-
Hey, thanks for the swift response! I've actually arrived to my pasted code after having it deduplicated. It looked the same like you've expanded now. The error came after the deduplication, since I noticed it's the same logic, only with different variable types. I've refactored it already to have only one type of the variable passed from the outside into the function, and the function signature uses a _NetType = TypeVar('_NetType', IPv4Network, IPv6Network) variable. This way it works, however it doesn't look elegant to my eyes. The full code is at https://git.ksol.io/karolyi/abuseipdb-fetcher/src/branch/master/src/abuseipdb_fetcher/iplist_merge.py. One would think that the type narrowing would work as it's only logical to have those variables narrowed to their respective types. I've linked the code for more context, since I forgot the iteration in the example, that calls the Thanks for the help again! |
Beta Was this translation helpful? Give feedback.
Static type checkers like pyright apply a technique called "type narrowing". You can read more about this here.
In your example above, the type of the expression
net
cannot be narrowed by theisinstance
check; prior to the check, its type isIPv4Network | IPv6Network
and after the check, its type remains the same. The type ofipnet_item
is also a union and cannot be narrowed further by thein
operator.It is theoretically possible to narrow types conditionally — for example, "the type of
searched_wl
isIPv4Network
if the type ofnet
is alsoIPv4Network
but itIPv6Network
otherwise. However, tracking such conditional types is not computationally feasible in practice. I don't know of any st…