Skip to content

Commit d4a1ddf

Browse files
committed
Refactor to avoid method-level import in hot code
1 parent ccc03a4 commit d4a1ddf

5 files changed

Lines changed: 37 additions & 21 deletions

File tree

mypy/build.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@
143143

144144
from mypy import errorcodes as codes
145145
from mypy.config_parser import get_config_module_names, parse_mypy_comments
146-
from mypy.fixup import node_fixer
146+
from mypy.fixer_state import fixer_state
147+
from mypy.fixup import NodeFixer
147148
from mypy.freetree import free_tree
148149
from mypy.fscache import FileSystemCache
149150
from mypy.known_modules import get_known_modules, reset_known_modules_cache
@@ -813,11 +814,9 @@ def __init__(
813814
self.version_id = version_id
814815
self.modules: dict[str, MypyFile] = {}
815816
# Share same modules dictionary with the global fixer state.
816-
node_fixer.modules = node_fixer.type_fixer.modules = self.modules
817817
# We need to set allow_missing when doing a fine-grained cache
818818
# load because we need to gracefully handle missing modules.
819-
allow_missing = self.options.use_fine_grained_cache
820-
node_fixer.allow_missing = node_fixer.type_fixer.allow_missing = allow_missing
819+
fixer_state.node_fixer = NodeFixer(self.modules, self.options.use_fine_grained_cache)
821820
self.import_map: dict[str, set[str]] = {}
822821
self.missing_modules: dict[str, int] = {}
823822
self.fg_deps_meta: dict[str, FgDepMeta] = {}
@@ -2820,8 +2819,9 @@ def load_tree(self, temporary: bool = False) -> None:
28202819
def fix_cross_refs(self) -> None:
28212820
assert self.tree is not None, "Internal error: method must be called on parsed file only"
28222821
# Do initial lightweight pass fixing TypeInfos and module cross-references.
2823-
node_fixer.visit_symbol_table(self.tree.names)
2824-
type_fixer = node_fixer.type_fixer
2822+
assert fixer_state.node_fixer is not None
2823+
fixer_state.node_fixer.visit_symbol_table(self.tree.names)
2824+
type_fixer = fixer_state.node_fixer.type_fixer
28252825
if instance_cache.str_type is not None:
28262826
instance_cache.str_type.accept(type_fixer)
28272827
if instance_cache.function_type is not None:

mypy/fixer_state.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Final
4+
5+
if TYPE_CHECKING:
6+
from mypy.fixup import NodeFixer
7+
8+
# This is global mutable state. Don't add anything here unless there's a very
9+
# good reason. This exists as a separate file to avoid method-level import in
10+
# hot code in SymbolTableNode.node().
11+
12+
13+
class FixerState:
14+
def __init__(self) -> None:
15+
self.node_fixer: NodeFixer | None = None
16+
17+
18+
fixer_state: Final = FixerState()

mypy/fixup.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@
4949
class NodeFixer(NodeVisitor[None]):
5050
current_info: TypeInfo | None = None
5151

52-
def __init__(self) -> None:
53-
self.modules: dict[str, MypyFile] = {}
52+
def __init__(self, modules: dict[str, MypyFile], allow_missing: bool) -> None:
53+
self.modules = modules
5454
# N.B: we do an allow_missing fixup when fixing up a fine-grained
5555
# incremental cache load (since there may be cross-refs into deleted
5656
# modules)
57-
self.allow_missing = False
58-
self.type_fixer = TypeFixer()
57+
self.allow_missing = allow_missing
58+
self.type_fixer = TypeFixer(modules, allow_missing)
5959

6060
# NOTE: This method isn't (yet) part of the NodeVisitor API.
6161
def visit_type_info(self, info: TypeInfo) -> None:
@@ -225,9 +225,9 @@ def visit_type_alias(self, a: TypeAlias) -> None:
225225

226226

227227
class TypeFixer(TypeVisitor[None]):
228-
def __init__(self) -> None:
229-
self.modules: dict[str, MypyFile] = {}
230-
self.allow_missing = False
228+
def __init__(self, modules: dict[str, MypyFile], allow_missing: bool) -> None:
229+
self.modules = modules
230+
self.allow_missing = allow_missing
231231

232232
def visit_instance(self, inst: Instance) -> None:
233233
type_ref = inst.type_ref
@@ -434,6 +434,3 @@ def missing_info(modules: dict[str, MypyFile]) -> TypeInfo:
434434
def missing_alias() -> TypeAlias:
435435
suggestion = _SUGGESTION.format("alias")
436436
return TypeAlias(AnyType(TypeOfAny.special_form), suggestion, "<missing>", line=-1, column=-1)
437-
438-
439-
node_fixer: Final = NodeFixer()

mypy/nodes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
write_str_opt_list,
6868
write_tag,
6969
)
70+
from mypy.fixer_state import fixer_state
7071
from mypy.options import Options
7172
from mypy.util import is_sunder, is_typeshed_file, short_type
7273
from mypy.visitor import ExpressionVisitor, NodeVisitor, StatementVisitor
@@ -4820,10 +4821,9 @@ def type(self) -> mypy.types.Type | None:
48204821

48214822
@property
48224823
def node(self) -> SymbolNode | None:
4823-
# Late import because of a circular dependency.
4824-
from mypy.fixup import node_fixer
4825-
48264824
if self.unfixed:
4825+
node_fixer = fixer_state.node_fixer
4826+
assert node_fixer is not None
48274827
if self.cross_ref is not None:
48284828
node_fixer.resolve_cross_ref(self)
48294829
else:

mypy/plugins/common.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import NamedTuple
44

55
from mypy.argmap import map_actuals_to_formals
6-
from mypy.fixup import node_fixer
6+
from mypy.fixer_state import fixer_state
77
from mypy.nodes import (
88
ARG_POS,
99
MDEF,
@@ -435,5 +435,6 @@ def add_attribute_to_class(
435435

436436
def deserialize_and_fixup_type(data: str | JsonDict, api: SemanticAnalyzerPluginInterface) -> Type:
437437
typ = deserialize_type(data)
438-
typ.accept(node_fixer.type_fixer)
438+
assert fixer_state.node_fixer is not None
439+
typ.accept(fixer_state.node_fixer.type_fixer)
439440
return typ

0 commit comments

Comments
 (0)