Skip to content

reveal_type(type(None)) is object #11550

Closed

Description

@sobolevn

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.

Activity

  1. changed the title [-]`reveal_type(type(None))` is object[/-] [+]`reveal_type(type(None))` is `object`[/+] on Nov 14, 2021
  2. sobolevn commented on Nov 14, 2021

    @sobolevn
    MemberAuthor

    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]"
  3. sobolevn commented on Nov 14, 2021

    @sobolevn
    MemberAuthor

    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
  4. sobolevn commented on Nov 14, 2021

    @sobolevn
    MemberAuthor

    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

  5. sobolevn commented on Nov 14, 2021

    @sobolevn
    MemberAuthor

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

  6. ilevkivskyi commented on Feb 8, 2025

    @ilevkivskyi
    Member

    This is much better now:

    Revealed type is "type[None]"
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions