-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
ipaddress subnet slicing iterator malfunction #71870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Between Python 3.4 (3.4.5 I've tested with) and Python 3.5 (3.5.2), the behavior of ipaddress's subnet() method changed. This code: $ cat iat.py
import ipaddress p = ipaddress.IPv4Network("172.0.0.4/30")
subnets = p.subnets(new_prefix=31)
n = next(subnets)
print(list(n.hosts())) has two different outcomes on 3.4 vs. 3.5: $ python3.4 iat.py
[IPv4Address('172.0.0.4'), IPv4Address('172.0.0.5')]
$ python3.5 iat.py
[] AIUI, the 3.4 behavior is the correct one (or the docs need to be fixed). |
If someone wants to bisect to find out which changeset introduced the behavior change that would be helpful. |
The change was introduced in bpo-21487. |
I just bisected it and the specific commit is this one: |
Since this is a regression, I'm going to claim this has higher than normal priority, for whatever good that will do :) |
Here's a patch that fixes this bug, test included. I made this against the 3.5 branch, but should apply cleanly on default. |
The bug appears to be in the new form of the constructor. Here's a more minimal reproduction: In python3.5:
>>> list(ipaddress.IPv4Network(("127.0.0.4", 31)).hosts())
[]
In python3.4
>>> list(ipaddress.IPv4Network("127.0.0.4/31").hosts())
[IPv4Address('127.0.0.4'), IPv4Address('127.0.0.5')] |
As Stephen notes, the underlying problem appears to be a behavioural difference between two theoretically equivalent ways of defining a network: >>> list(ipaddress.IPv4Network(('127.0.0.4', 31)).hosts())
[]
>>> list(ipaddress.IPv4Network(('127.0.0.4/31')).hosts())
[IPv4Address('127.0.0.4'), IPv4Address('127.0.0.5')] Now, the first case is the documented behaviour: hosts() is *supposed to* exclude the network and broadcast address, and those are the only addresses in a /31. If you want to iterate over all the *addresses* in a network (including the network and broadcast addresses) then you need to iterate over the network object directly: >>> list(ipaddress.IPv4Network(('127.0.0.4', 31)))
[IPv4Address('127.0.0.4'), IPv4Address('127.0.0.5')]
>>> list(ipaddress.IPv4Network(('127.0.0.4/31')))
[IPv4Address('127.0.0.4'), IPv4Address('127.0.0.5')] However, as Emanuel found when writing his patch, there's currently an undocumented special case for /31 networks: the definition of "hosts" is *implicitly changed* for such instances to include the nominal network and broadcast address (by setting "self.hosts = self.__iter__"), presumably on the assumption that such networks represent a point-to-point link between two hosts, so the concepts of "network address" and "broadcast address" don't really apply. That special case seems pragmatically useful, so I think the right fix would be to:
|
Ack, special cases! I can look into making a patch, but I'm not really acquainted with ipaddress or the relevant protocols, so it might not be optimal. I'll give it a shot next week if nobody else does. |
bpo-27683.patch tries to fix this. It alters the doc and refactors the constructor, so there is no difference between different arguments As for IPv6Network, it has the same problem. |
ping :) |
Ping. ;-) |
bpo-28577 requests a similar special case for /32 |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: