Repository: python/mypy

Issue:
Module-level `__annotations__` variable is flagged as undefined
To repro, run mypy on the following:

```python
print(__annotations__)
```

```
test.py:1: error: Name '__annotations__' is not defined
Found 1 error in 1 file (checked 1 source file)
```

A pyright user recently noted [the same issue in pyright](https://github.com/microsoft/pylance-release/issues/1161), which I have fixed. 


Discussion:
Thanks, fixable along the lines of https://github.com/python/mypy/pull/9454
I can take a look at this.
Seems like the previous PR was closed. I can give this a shot!

Resolving patch:
diff --git a/mypy/nodes.py b/mypy/nodes.py
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -106,7 +106,8 @@ def get_column(self) -> int:
     '__doc__': None,  # depends on Python version, see semanal.py
     '__path__': None,  # depends on if the module is a package
     '__file__': '__builtins__.str',
-    '__package__': '__builtins__.str'
+    '__package__': '__builtins__.str',
+    '__annotations__': None,  # dict[str, Any] bounded in add_implicit_module_attrs()
 }
 
 
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -447,6 +447,13 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None:
                 node = sym.node
                 assert isinstance(node, TypeInfo)
                 typ = Instance(node, [self.str_type()])
+            elif name == '__annotations__':
+                sym = self.lookup_qualified("__builtins__.dict", Context(), suppress_errors=True)
+                if not sym:
+                    continue
+                node = sym.node
+                assert isinstance(node, TypeInfo)
+                typ = Instance(node, [self.str_type(), AnyType(TypeOfAny.special_form)])
             else:
                 assert t is not None, 'type should be specified for {}'.format(name)
                 typ = UnboundType(t)
diff --git a/mypy/stubtest.py b/mypy/stubtest.py
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -978,6 +978,7 @@ def verify_typealias(
         "__cached__",
         "__loader__",
         "__spec__",
+        "__annotations__",
         "__path__",  # mypy adds __path__ to packages, but C packages don't have it
         "__getattr__",  # resulting behaviour might be typed explicitly
         # TODO: remove the following from this list


Test patch:
diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test
--- a/test-data/unit/check-basic.test
+++ b/test-data/unit/check-basic.test
@@ -236,32 +236,16 @@ from typing import Any
 def f() -> Any:
   yield
 
-[case testModule__name__]
+[case testModuleImplicitAttributes]
 import typing
-x = __name__ # type: str
-a = __name__ # type: A  # E: Incompatible types in assignment (expression has type "str", variable has type "A")
 class A: pass
+reveal_type(__name__)         # N: Revealed type is "builtins.str"
+reveal_type(__doc__)          # N: Revealed type is "builtins.str"
+reveal_type(__file__)         # N: Revealed type is "builtins.str"
+reveal_type(__package__)      # N: Revealed type is "builtins.str"
+reveal_type(__annotations__)  # N: Revealed type is "builtins.dict[builtins.str, Any]"
 [builtins fixtures/primitives.pyi]
 
-[case testModule__doc__]
-import typing
-x = __doc__ # type: str
-a = __doc__ # type: A  # E: Incompatible types in assignment (expression has type "str", variable has type "A")
-class A: pass
-[builtins fixtures/primitives.pyi]
-
-[case testModule__file__]
-import typing
-x = __file__ # type: str
-a = __file__ # type: A  # E: Incompatible types in assignment (expression has type "str", variable has type "A")
-class A: pass
-[builtins fixtures/primitives.pyi]
-
-[case test__package__]
-import typing
-x = __package__ # type: str
-a = __file__ # type: int  # E: Incompatible types in assignment (expression has type "str", variable has type "int")
-
 
 -- Scoping and shadowing
 -- ---------------------