Skip to content
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

reveal_type(type(None)) is object #11550

Open
sobolevn opened this issue Nov 14, 2021 · 4 comments
Open

reveal_type(type(None)) is object #11550

sobolevn opened this issue Nov 14, 2021 · 4 comments
Labels
bug mypy got something wrong

Comments

@sobolevn
Copy link
Member

It should be type, or even better NoneType.

Code sample:

reveal_type(type)
none = type(None)
reveal_type(none)

Output:

» mypy out/ex.py --show-traceback --warn-unreachable 
out/ex.py:1: note: Revealed type is "Overload(def (o: builtins.object) -> builtins.type, def (name: builtins.str, bases: builtins.tuple[builtins.type], dict: builtins.dict[builtins.str, Any], **kwds: Any) -> builtins.type)"
out/ex.py:3: note: Revealed type is "builtins.object"

Refs #11539

I will fix this. Not sure where yet: in typeshed or here.

@sobolevn sobolevn added the bug mypy got something wrong label Nov 14, 2021
@sobolevn sobolevn changed the title reveal_type(type(None)) is object reveal_type(type(None)) is object Nov 14, 2021
@sobolevn
Copy link
Member Author

sobolevn commented Nov 14, 2021

Fun thing:

none = type(None)
reveal_type(none)  # N: Revealed type is "builtins.object"

# VS:

reveal_type(type(None))  # N: Revealed type is "Type[None]"

@sobolevn
Copy link
Member Author

This difference happens because the inner mypy AST is different:

MypyFile:1(
  out/ex.py
  AssignmentStmt:1(
    NameExpr(none* [ex.none])
    TypeAliasExpr(None))

  # reveal_type(none)

  ExpressionStmt:2(
    RevealExpr:2(
      NameExpr(none [ex.none])))

  # reveal_type(type(None))

  ExpressionStmt:4(
    RevealExpr:4(
      CallExpr:4(
        NameExpr(type [builtins.type])
        Args(
          NameExpr(None [builtins.None]))))))

For some reason type(None) is turned into TypeAlias to None, which is not 100% correct. Here are some examples, where these two types are different:

>>> NoneType = type(None)

>>> NoneType()
>>> None()
<stdin>:1: SyntaxWarning: 'NoneType' object is not callable; perhaps you missed a comma?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

>>> NoneType.__bool__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor '__bool__' of 'NoneType' object needs an argument
>>> None.__bool__()
False

@sobolevn
Copy link
Member Author

Here's what we need to change:

mypy/mypy/semanal.py

Lines 2246 to 2260 in f9bf649

def is_none_alias(self, node: Expression) -> bool:
"""Is this a r.h.s. for a None alias?
We special case the assignments like Void = type(None), to allow using
Void in type annotations.
"""
if isinstance(node, CallExpr):
if (isinstance(node.callee, NameExpr) and len(node.args) == 1 and
isinstance(node.args[0], NameExpr)):
call = self.lookup_qualified(node.callee.name, node.callee)
arg = self.lookup_qualified(node.args[0].name, node.args[0])
if (call is not None and call.node and call.node.fullname == 'builtins.type' and
arg is not None and arg.node and arg.node.fullname == 'builtins.None'):
return True
return False

@sobolevn
Copy link
Member Author

This can be a breaking change, but I am going to try this anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant