netaddr / netaddr

A network address manipulation library for Python

Home Page:https://netaddr.readthedocs.io/en/latest/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

valid_ip4 behaves differently for None

kajinamit opened this issue · comments

Before 1.0.0 release, both valid_ipv4 and vlaid_ipv6 return False in case None is passed.

>>> netaddr.valid_ipv4('192.168.0.1')
True
>>> netaddr.valid_ipv4(None)
False
>>> netaddr.valid_ipv6(None)
False

However this behavior has been changed in 1.0.0 and now valid_ipv4 raises raises AttributeError.
valid_ipv6 still returns False so the change is inconsistently made.

>>> netaddr.valid_ipv4('192.168.0.1')
True
>>> netaddr.valid_ipv4(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/tkajinam/venv/lib/python3.11/site-packages/netaddr/strategy/ipv4.py", line 99, in valid_str
    str_to_int(addr, flags)
  File "/home/tkajinam/venv/lib/python3.11/site-packages/netaddr/strategy/ipv4.py", line 119, in str_to_int
    elif pton_mode and any(len(p) > 1 and p.startswith('0') for p in addr.split('.')):
                                                                     ^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'split'
>>> 
>>> netaddr.valid_ipv6(None)
False

Hey @kajinamit, thank you for the report.

valid_ipv4 and valid_ipv6 have been defined for and expected to be called with strings:

:param addr: An IPv4 address in presentation (string) format.

:param addr: An IPv6 address in presentation (string) format.

Giving them anything other than a string should be considered an error – in the code that calls them – and there's no guarantee what happens when that happens.

I don't consider this a bug – if anything I'm rather unhappy with valid_ipv6(None) returning False.

Thanks @jstasiak for the thoughts.
I understand that interface documented, but as you know python does not enforce types and returning False for unexpected types could make sense to me. If this is not a desired behavior then I'm wondering if it should raise an explicit exception like TypeError, instead of raising the internal exception.

I understand that interface documented, but as you know python does not enforce types (...)

That depends on what do we mean by "enforce types".

Runtime enforcement? It very much does in many cases. One can't add a str to an int (not easily), one can't index a list with another list etc. 99 cases out of 100 as a user of an API I want an exception instead of a result if I provide a bad type somewhere.

Lint-time enforcement (static type checking)? I'd argue using Mypy or similar is table stakes these days. The library doesn't publish type definitions yet but that's planned.

If this is not a desired behavior then I'm wondering if it should raise an explicit exception like TypeError, instead of raising the internal exception.

Maybe, although here's why I'm against it:

  • An explicit exception is likely to encourage the client code to keep providing bad types and catch that exception, while the client code shouldn't be providing bad types in the first place.
  • I plan to add static types to the library which can eliminate this issue before the code can even be executed (as long as the client code uses Mypy or similar).

I'm not strongly opposed to raising TypeError for the time being at least, if you create a PR I'll merge it.

What I meant by type enforcement is runtime one at interface level. It may be reasonable to get exception in case an unacceptable type is given, but if an exception is directly raised from an internal logic, it requires some effort to dig into the logic and find out at which point, at which layer they made mistake.

In the mean time, until type annotation is added, #371 may help people aware of the situation.

That's fair, we're on the same page.