pycodestyle icon indicating copy to clipboard operation
pycodestyle copied to clipboard

E721: `Do not compare types, use 'isinstance()'` is overzealous with `type(None)`

Open ex-nerd opened this issue 5 years ago • 4 comments

Note: May be a dupe of (or at least related to) #882.

I discovered this while writing a decorator to parse the values of typing.get_type_hints(). This isn't exactly a bug in the flake8 feature, but rather an inconsistency in how NoneType is handled in the python3 typing system. This definitely feels like a bug, given that isinstance(none_type, type(None)) != none_type is type(None). If nothing else it's worth documenting the behavior because I couldn't find anything written up about it:

Setup:

# Setup:

from typing import Union

my_type = Union[str, None]
# --> typing.Union[str, NoneType]
none_arg = my_type.__args__[1]
# --> <class 'NoneType'>

Examples/behavior:

# Expected output based on recommendations for validating NoneType in python 3,
# but this triggers e721:
none_arg is type(None)
# --> True

# Just for example:
none_arg is None
# --> False

# Recommended by E721 but not actually valid
isinstance(none_arg, type(None))
# --> False

isinstance(none_arg, None)
# --> TypeError: isinstance() arg 2 must be a type or tuple of types

isinstance(none_arg, (None, ))
# --> TypeError: isinstance() arg 2 must be a type or tuple of types

Current workaround:

# Redefine local copy of NoneType no longer present in Python3
NoneType = type(None)

none_arg is NoneType
# --> True

# However, this fails to pass `mypy` inspection, so I'm now back to:
none_arg is type(None)  # noqa: E721
# --> True

ex-nerd avatar Jun 12 '20 21:06 ex-nerd