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

Add support for __spec__ #14739

Merged
merged 6 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def set_line(
"__file__": "__builtins__.str",
"__package__": "__builtins__.str",
"__annotations__": None, # dict[str, Any] bounded in add_implicit_module_attrs()
"__spec__": None, # importlib.machinery.ModuleSpec bounded in add_implicit_module_attrs()
}


Expand Down
18 changes: 18 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@
TypeVarTupleType,
TypeVarType,
UnboundType,
UnionType,
UnpackType,
get_proper_type,
get_proper_types,
Expand Down Expand Up @@ -635,6 +636,7 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None:
str_type: Type | None = self.named_type_or_none("builtins.str")
if str_type is None:
str_type = UnboundType("builtins.str")
inst: Type | None
for name, t in implicit_module_attrs.items():
if name == "__doc__":
typ: Type = str_type
Expand All @@ -660,6 +662,22 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None:
self.defer()
return
typ = inst
elif name == "__spec__":
if self.options.use_builtins_fixtures:
inst = self.named_type_or_none("builtins.object")
else:
inst = self.named_type_or_none("importlib.machinery.ModuleSpec")
if inst is None:
if self.final_iteration:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this may down slow down tests significantly, since every module top level will have to be processed many times? Maybe instead check options.use_builtins_fixtures and fall back to builtins.object immediately if it's true.

inst = self.named_type_or_none("builtins.object")
assert inst is not None, "Cannot find builtins.object"
else:
self.defer()
return
if file_node.name == "__main__":
# https://docs.python.org/3/reference/import.html#main-spec
inst = UnionType.make_union([inst, NoneType()])
typ = inst
else:
assert t is not None, f"type should be specified for {name}"
typ = UnboundType(t)
Expand Down
8 changes: 8 additions & 0 deletions test-data/unit/check-basic.test
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,14 @@ 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]"
# This will actually reveal Union[importlib.machinery.ModuleSpec, None]
reveal_type(__spec__) # N: Revealed type is "Union[builtins.object, None]"

import module
reveal_type(module.__name__) # N: Revealed type is "builtins.str"
# This will actually reveal importlib.machinery.ModuleSpec
reveal_type(module.__spec__) # N: Revealed type is "builtins.object"
[file module.py]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider also adding a pythoneval test that uses full stubs.

[builtins fixtures/primitives.pyi]


Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/fine-grained-inspect.test
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ class C: ...
[builtins fixtures/module.pyi]
[out]
==
{"<pack.bar>": ["C", "__annotations__", "__doc__", "__file__", "__name__", "__package__", "bar", "x"], "ModuleType": ["__file__", "__getattr__"]}
{"<pack.bar>": ["C", "__annotations__", "__doc__", "__file__", "__name__", "__package__", "__spec__", "bar", "x"], "ModuleType": ["__file__", "__getattr__"]}

[case testInspectModuleDef]
# inspect2: --show=definition --include-kind tmp/foo.py:2:1
Expand Down
8 changes: 8 additions & 0 deletions test-data/unit/pythoneval.test
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,22 @@ f
[case testModuleAttributes]
import math
import typing
print(type(__spec__))
print(math.__name__)
print(math.__spec__.name)
print(type(math.__spec__.loader))
print(type(math.__dict__))
print(type(math.__doc__ or ''))
print(type(math.__spec__).__name__)
print(math.__class__)
[out]
<class 'NoneType'>
math
math
<class '_frozen_importlib_external.ExtensionFileLoader'>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks in Python 3.8... 😥

<class 'dict'>
<class 'str'>
ModuleSpec
<class 'module'>

[case testSpecialAttributes]
Expand Down
Loading