Skip to content

Commit

Permalink
Refactor TypeState into a singleton class (#14327)
Browse files Browse the repository at this point in the history
This helps mypyc, since accessing mutable attributes of singleton
instances is faster than accessing class variables. The implementation
is also arguably a bit cleaner.

This seems performance-neutral or a very minor optimization, but if we
continue to add attributes to TypeState, this can help.
  • Loading branch information
JukkaL authored Dec 21, 2022
1 parent 2d5108b commit 4e32da8
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 109 deletions.
8 changes: 4 additions & 4 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
stub_package_name,
)
from mypy.types import Type
from mypy.typestate import TypeState, reset_global_state
from mypy.typestate import reset_global_state, type_state
from mypy.version import __version__

# Switch to True to produce debug output related to fine-grained incremental
Expand Down Expand Up @@ -276,7 +276,7 @@ def _build(
try:
graph = dispatch(sources, manager, stdout)
if not options.fine_grained_incremental:
TypeState.reset_all_subtype_caches()
type_state.reset_all_subtype_caches()
if options.timing_stats is not None:
dump_timing_stats(options.timing_stats, graph)
if options.line_checking_stats is not None:
Expand Down Expand Up @@ -2459,7 +2459,7 @@ def update_fine_grained_deps(self, deps: dict[str, set[str]]) -> None:
from mypy.server.deps import merge_dependencies # Lazy import to speed up startup

merge_dependencies(self.compute_fine_grained_deps(), deps)
TypeState.update_protocol_deps(deps)
type_state.update_protocol_deps(deps)

def valid_references(self) -> set[str]:
assert self.ancestors is not None
Expand Down Expand Up @@ -2926,7 +2926,7 @@ def dispatch(sources: list[BuildSource], manager: BuildManager, stdout: TextIO)
# then we need to collect fine grained protocol dependencies.
# Since these are a global property of the program, they are calculated after we
# processed the whole graph.
TypeState.add_all_protocol_deps(manager.fg_deps)
type_state.add_all_protocol_deps(manager.fg_deps)
if not manager.options.fine_grained_incremental:
rdeps = generate_deps_for_cache(manager, graph)
write_deps_cache(rdeps, manager, graph)
Expand Down
8 changes: 4 additions & 4 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
is_self_type_like,
remove_optional,
)
from mypy.typestate import TypeState
from mypy.typestate import type_state
from mypy.typevars import fill_typevars
from mypy.util import split_module_names
from mypy.visitor import ExpressionVisitor
Expand Down Expand Up @@ -1591,13 +1591,13 @@ def allow_unions(self, type_context: Type) -> Iterator[None]:
# of joins. This is a bit arbitrary, but in practice it works for most
# cases. A cleaner alternative would be to switch to single bin type
# inference, but this is a lot of work.
old = TypeState.infer_unions
old = type_state.infer_unions
if has_recursive_types(type_context):
TypeState.infer_unions = True
type_state.infer_unions = True
try:
yield
finally:
TypeState.infer_unions = old
type_state.infer_unions = old

def infer_arg_types_in_context(
self,
Expand Down
8 changes: 4 additions & 4 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
is_named_instance,
is_union_with_any,
)
from mypy.typestate import TypeState
from mypy.typestate import type_state
from mypy.typevartuples import (
extract_unpack,
find_unpack_in_list,
Expand Down Expand Up @@ -198,7 +198,7 @@ def infer_constraints(template: Type, actual: Type, direction: int) -> list[Cons
if any(
get_proper_type(template) == get_proper_type(t)
and get_proper_type(actual) == get_proper_type(a)
for (t, a) in reversed(TypeState.inferring)
for (t, a) in reversed(type_state.inferring)
):
return []
if has_recursive_types(template) or isinstance(get_proper_type(template), Instance):
Expand All @@ -207,9 +207,9 @@ def infer_constraints(template: Type, actual: Type, direction: int) -> list[Cons
if not has_type_vars(template):
# Return early on an empty branch.
return []
TypeState.inferring.append((template, actual))
type_state.inferring.append((template, actual))
res = _infer_constraints(template, actual, direction)
TypeState.inferring.pop()
type_state.inferring.pop()
return res
return _infer_constraints(template, actual, direction)

Expand Down
4 changes: 2 additions & 2 deletions mypy/mro.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from mypy.nodes import TypeInfo
from mypy.types import Instance
from mypy.typestate import TypeState
from mypy.typestate import type_state


def calculate_mro(info: TypeInfo, obj_type: Callable[[], Instance] | None = None) -> None:
Expand All @@ -17,7 +17,7 @@ def calculate_mro(info: TypeInfo, obj_type: Callable[[], Instance] | None = None
info.mro = mro
# The property of falling back to Any is inherited.
info.fallback_to_any = any(baseinfo.fallback_to_any for baseinfo in info.mro)
TypeState.reset_all_subtype_caches_for(info)
type_state.reset_all_subtype_caches_for(info)


class MroError(Exception):
Expand Down
4 changes: 2 additions & 2 deletions mypy/server/astmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
UnionType,
UnpackType,
)
from mypy.typestate import TypeState
from mypy.typestate import type_state
from mypy.util import get_prefix, replace_object_state


Expand Down Expand Up @@ -360,7 +360,7 @@ def fixup_and_reset_typeinfo(self, node: TypeInfo) -> TypeInfo:
# The subclass relationships may change, so reset all caches relevant to the
# old MRO.
new = cast(TypeInfo, self.replacements[node])
TypeState.reset_all_subtype_caches_for(new)
type_state.reset_all_subtype_caches_for(new)
return self.fixup(node)

def fixup_type(self, typ: Type | None) -> None:
Expand Down
4 changes: 2 additions & 2 deletions mypy/server/aststrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
)
from mypy.traverser import TraverserVisitor
from mypy.types import CallableType
from mypy.typestate import TypeState
from mypy.typestate import type_state

SavedAttributes: _TypeAlias = Dict[Tuple[ClassDef, str], SymbolTableNode]

Expand Down Expand Up @@ -143,7 +143,7 @@ def visit_class_def(self, node: ClassDef) -> None:
super().visit_class_def(node)
node.defs.body.extend(node.removed_statements)
node.removed_statements = []
TypeState.reset_subtype_caches_for(node.info)
type_state.reset_subtype_caches_for(node.info)
# Kill the TypeInfo, since there is none before semantic analysis.
node.info = CLASSDEF_NO_INFO
node.analyzed = None
Expand Down
6 changes: 3 additions & 3 deletions mypy/server/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a
UnpackType,
get_proper_type,
)
from mypy.typestate import TypeState
from mypy.typestate import type_state
from mypy.util import correct_relative_import


Expand Down Expand Up @@ -344,7 +344,7 @@ def process_type_info(self, info: TypeInfo) -> None:
self.add_dependency(
make_wildcard_trigger(base_info.fullname), target=make_trigger(target)
)
# More protocol dependencies are collected in TypeState._snapshot_protocol_deps
# More protocol dependencies are collected in type_state._snapshot_protocol_deps
# after a full run or update is finished.

self.add_type_alias_deps(self.scope.current_target())
Expand Down Expand Up @@ -1123,7 +1123,7 @@ def dump_all_dependencies(
deps = get_dependencies(node, type_map, python_version, options)
for trigger, targets in deps.items():
all_deps.setdefault(trigger, set()).update(targets)
TypeState.add_all_protocol_deps(all_deps)
type_state.add_all_protocol_deps(all_deps)

for trigger, targets in sorted(all_deps.items(), key=lambda x: x[0]):
print(trigger)
Expand Down
6 changes: 3 additions & 3 deletions mypy/server/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
from mypy.server.deps import get_dependencies_of_target, merge_dependencies
from mypy.server.target import trigger_to_target
from mypy.server.trigger import WILDCARD_TAG, make_trigger
from mypy.typestate import TypeState
from mypy.typestate import type_state
from mypy.util import module_prefix, split_target

MAX_ITER: Final = 1000
Expand Down Expand Up @@ -869,7 +869,7 @@ def propagate_changes_using_dependencies(
# We need to do this to avoid false negatives if the protocol itself is
# unchanged, but was marked stale because its sub- (or super-) type changed.
for info in stale_protos:
TypeState.reset_subtype_caches_for(info)
type_state.reset_subtype_caches_for(info)
# Then fully reprocess all targets.
# TODO: Preserve order (set is not optimal)
for id, nodes in sorted(todo.items(), key=lambda x: x[0]):
Expand Down Expand Up @@ -1081,7 +1081,7 @@ def update_deps(
for trigger, targets in new_deps.items():
deps.setdefault(trigger, set()).update(targets)
# Merge also the newly added protocol deps (if any).
TypeState.update_protocol_deps(deps)
type_state.update_protocol_deps(deps)


def lookup_target(
Expand Down
4 changes: 2 additions & 2 deletions mypy/solve.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
UnionType,
get_proper_type,
)
from mypy.typestate import TypeState
from mypy.typestate import type_state


def solve_constraints(
Expand Down Expand Up @@ -54,7 +54,7 @@ def solve_constraints(
if bottom is None:
bottom = c.target
else:
if TypeState.infer_unions:
if type_state.infer_unions:
# This deviates from the general mypy semantics because
# recursive types are union-heavy in 95% of cases.
bottom = UnionType.make_union([bottom, c.target])
Expand Down
20 changes: 10 additions & 10 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
get_proper_type,
is_named_instance,
)
from mypy.typestate import SubtypeKind, TypeState
from mypy.typestate import SubtypeKind, type_state
from mypy.typevars import fill_typevars_with_any
from mypy.typevartuples import extract_unpack, fully_split_with_mapped_and_template

Expand Down Expand Up @@ -154,7 +154,7 @@ def is_subtype(
options,
}
), "Don't pass both context and individual flags"
if TypeState.is_assumed_subtype(left, right):
if type_state.is_assumed_subtype(left, right):
return True
if mypy.typeops.is_recursive_pair(left, right):
# This case requires special care because it may cause infinite recursion.
Expand All @@ -174,7 +174,7 @@ def is_subtype(
# B = Union[int, Tuple[B, ...]]
# When checking if A <: B we push pair (A, B) onto 'assuming' stack, then when after few
# steps we come back to initial call is_subtype(A, B) and immediately return True.
with pop_on_exit(TypeState.get_assumptions(is_proper=False), left, right):
with pop_on_exit(type_state.get_assumptions(is_proper=False), left, right):
return _is_subtype(left, right, subtype_context, proper_subtype=False)
return _is_subtype(left, right, subtype_context, proper_subtype=False)

Expand Down Expand Up @@ -215,11 +215,11 @@ def is_proper_subtype(
ignore_uninhabited,
}
), "Don't pass both context and individual flags"
if TypeState.is_assumed_proper_subtype(left, right):
if type_state.is_assumed_proper_subtype(left, right):
return True
if mypy.typeops.is_recursive_pair(left, right):
# Same as for non-proper subtype, see detailed comment there for explanation.
with pop_on_exit(TypeState.get_assumptions(is_proper=True), left, right):
with pop_on_exit(type_state.get_assumptions(is_proper=True), left, right):
return _is_subtype(left, right, subtype_context, proper_subtype=True)
return _is_subtype(left, right, subtype_context, proper_subtype=True)

Expand Down Expand Up @@ -445,14 +445,14 @@ def visit_instance(self, left: Instance) -> bool:
if isinstance(right, TupleType) and mypy.typeops.tuple_fallback(right).type.is_enum:
return self._is_subtype(left, mypy.typeops.tuple_fallback(right))
if isinstance(right, Instance):
if TypeState.is_cached_subtype_check(self._subtype_kind, left, right):
if type_state.is_cached_subtype_check(self._subtype_kind, left, right):
return True
if not self.subtype_context.ignore_promotions:
for base in left.type.mro:
if base._promote and any(
self._is_subtype(p, self.right) for p in base._promote
):
TypeState.record_subtype_cache_entry(self._subtype_kind, left, right)
type_state.record_subtype_cache_entry(self._subtype_kind, left, right)
return True
# Special case: Low-level integer types are compatible with 'int'. We can't
# use promotions, since 'int' is already promoted to low-level integer types,
Expand Down Expand Up @@ -589,7 +589,7 @@ def check_mixed(
):
nominal = False
if nominal:
TypeState.record_subtype_cache_entry(self._subtype_kind, left, right)
type_state.record_subtype_cache_entry(self._subtype_kind, left, right)
return nominal
if right.type.is_protocol and is_protocol_implementation(
left, right, proper_subtype=self.proper_subtype
Expand Down Expand Up @@ -978,7 +978,7 @@ def f(self) -> A: ...
if skip is None:
skip = []
# We need to record this check to generate protocol fine-grained dependencies.
TypeState.record_protocol_subtype_check(left.type, right.type)
type_state.record_protocol_subtype_check(left.type, right.type)
# nominal subtyping currently ignores '__init__' and '__new__' signatures
members_not_to_check = {"__init__", "__new__"}
members_not_to_check.update(skip)
Expand Down Expand Up @@ -1078,7 +1078,7 @@ def named_type(fullname: str) -> Instance:
subtype_context=SubtypeContext(ignore_pos_arg_names=ignore_names),
proper_subtype=proper_subtype,
)
TypeState.record_subtype_cache_entry(subtype_kind, left, right)
type_state.record_subtype_cache_entry(subtype_kind, left, right)
return True


Expand Down
4 changes: 2 additions & 2 deletions mypy/test/testdeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from mypy.test.data import DataDrivenTestCase, DataSuite
from mypy.test.helpers import assert_string_arrays_equal, find_test_files, parse_options
from mypy.types import Type
from mypy.typestate import TypeState
from mypy.typestate import type_state

# Only dependencies in these modules are dumped
dumped_modules = ["__main__", "pkg", "pkg.mod"]
Expand Down Expand Up @@ -54,7 +54,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
for source in new_deps:
deps[source].update(new_deps[source])

TypeState.add_all_protocol_deps(deps)
type_state.add_all_protocol_deps(deps)

for source, targets in sorted(deps.items()):
if source.startswith(("<enum", "<typing", "<mypy")):
Expand Down
Loading

0 comments on commit 4e32da8

Please sign in to comment.