Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
from mypy.renaming import LimitedVariableRenameVisitor, VariableRenameVisitor
from mypy.stats import dump_type_stats
from mypy.stubinfo import is_module_from_legacy_bundled_package, stub_distribution_name
from mypy.types import Type
from mypy.types import Type, instance_cache
from mypy.typestate import reset_global_state, type_state
from mypy.util import json_dumps, json_loads
from mypy.version import __version__
Expand Down Expand Up @@ -180,6 +180,9 @@ def build(
# fields for callers that want the traditional API.
messages = []

# This is mostly for the benefit of tests that use builtins fixtures.
instance_cache.reset()

def default_flush_errors(
filename: str | None, new_messages: list[str], is_serious: bool
) -> None:
Expand Down
37 changes: 16 additions & 21 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@
flatten_nested_unions,
get_proper_type,
get_proper_types,
instance_cache,
is_literal_type,
is_named_instance,
)
Expand Down Expand Up @@ -465,12 +466,6 @@ def __init__(
self, self.msg, self.plugin, per_line_checking_time_ns
)

self._str_type: Instance | None = None
self._function_type: Instance | None = None
self._int_type: Instance | None = None
self._bool_type: Instance | None = None
self._object_type: Instance | None = None

self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options)
self._unique_id = 0

Expand Down Expand Up @@ -7415,25 +7410,25 @@ def named_type(self, name: str) -> Instance:
For example, named_type('builtins.object') produces the 'object' type.
"""
if name == "builtins.str":
if self._str_type is None:
self._str_type = self._named_type(name)
return self._str_type
if instance_cache.str_type is None:
instance_cache.str_type = self._named_type(name)
return instance_cache.str_type
if name == "builtins.function":
if self._function_type is None:
self._function_type = self._named_type(name)
return self._function_type
if instance_cache.function_type is None:
instance_cache.function_type = self._named_type(name)
return instance_cache.function_type
if name == "builtins.int":
if self._int_type is None:
self._int_type = self._named_type(name)
return self._int_type
if instance_cache.int_type is None:
instance_cache.int_type = self._named_type(name)
return instance_cache.int_type
if name == "builtins.bool":
if self._bool_type is None:
self._bool_type = self._named_type(name)
return self._bool_type
if instance_cache.bool_type is None:
instance_cache.bool_type = self._named_type(name)
return instance_cache.bool_type
if name == "builtins.object":
if self._object_type is None:
self._object_type = self._named_type(name)
return self._object_type
if instance_cache.object_type is None:
instance_cache.object_type = self._named_type(name)
return instance_cache.object_type
return self._named_type(name)

def _named_type(self, name: str) -> Instance:
Expand Down
3 changes: 2 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,8 @@ def module_type(self, node: MypyFile) -> Instance:
# In test cases might 'types' may not be available.
# Fall back to a dummy 'object' type instead to
# avoid a crash.
result = self.named_type("builtins.object")
# Make a copy so that we don't set extra_attrs (below) on a shared instance.
result = self.named_type("builtins.object").copy_modified()
module_attrs: dict[str, Type] = {}
immutable = set()
for name, n in node.names.items():
Expand Down
77 changes: 77 additions & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1708,6 +1708,23 @@ def deserialize(cls, data: JsonDict | str) -> Instance:

def write(self, data: Buffer) -> None:
write_tag(data, INSTANCE)
if not self.args and not self.last_known_value and not self.extra_attrs:
type_ref = self.type.fullname
if type_ref == "builtins.str":
write_tag(data, INSTANCE_STR)
elif type_ref == "builtins.function":
write_tag(data, INSTANCE_FUNCTION)
elif type_ref == "builtins.int":
write_tag(data, INSTANCE_INT)
elif type_ref == "builtins.bool":
write_tag(data, INSTANCE_BOOL)
elif type_ref == "builtins.object":
write_tag(data, INSTANCE_OBJECT)
else:
write_tag(data, INSTANCE_SIMPLE)
write_str(data, type_ref)
return
write_tag(data, INSTANCE_GENERIC)
write_str(data, self.type.fullname)
write_type_list(data, self.args)
write_type_opt(data, self.last_known_value)
Expand All @@ -1719,6 +1736,39 @@ def write(self, data: Buffer) -> None:

@classmethod
def read(cls, data: Buffer) -> Instance:
tag = read_tag(data)
# This is quite verbose, but this is very hot code, so we are not
# using dictionary lookups here.
if tag == INSTANCE_STR:
if instance_cache.str_type is None:
instance_cache.str_type = Instance(NOT_READY, [])
instance_cache.str_type.type_ref = "builtins.str"
return instance_cache.str_type
if tag == INSTANCE_FUNCTION:
if instance_cache.function_type is None:
instance_cache.function_type = Instance(NOT_READY, [])
instance_cache.function_type.type_ref = "builtins.function"
return instance_cache.function_type
if tag == INSTANCE_INT:
if instance_cache.int_type is None:
instance_cache.int_type = Instance(NOT_READY, [])
instance_cache.int_type.type_ref = "builtins.int"
return instance_cache.int_type
if tag == INSTANCE_BOOL:
if instance_cache.bool_type is None:
instance_cache.bool_type = Instance(NOT_READY, [])
instance_cache.bool_type.type_ref = "builtins.bool"
return instance_cache.bool_type
if tag == INSTANCE_OBJECT:
if instance_cache.object_type is None:
instance_cache.object_type = Instance(NOT_READY, [])
instance_cache.object_type.type_ref = "builtins.object"
return instance_cache.object_type
if tag == INSTANCE_SIMPLE:
inst = Instance(NOT_READY, [])
inst.type_ref = read_str(data)
return inst
assert tag == INSTANCE_GENERIC
type_ref = read_str(data)
inst = Instance(NOT_READY, read_type_list(data))
inst.type_ref = type_ref
Expand Down Expand Up @@ -1769,6 +1819,25 @@ def is_singleton_type(self) -> bool:
)


class InstanceCache:
def __init__(self) -> None:
self.str_type: Instance | None = None
self.function_type: Instance | None = None
self.int_type: Instance | None = None
self.bool_type: Instance | None = None
self.object_type: Instance | None = None

def reset(self) -> None:
self.str_type = None
self.function_type = None
self.int_type = None
self.bool_type = None
self.object_type = None


instance_cache: Final = InstanceCache()


class FunctionLike(ProperType):
"""Abstract base class for function types."""

Expand Down Expand Up @@ -4140,6 +4209,14 @@ def type_vars_as_args(type_vars: Sequence[TypeVarLikeType]) -> tuple[Type, ...]:
TYPE_TYPE: Final[Tag] = 18
PARAMETERS: Final[Tag] = 19

INSTANCE_STR: Final[Tag] = 101
INSTANCE_FUNCTION: Final[Tag] = 102
INSTANCE_INT: Final[Tag] = 103
INSTANCE_BOOL: Final[Tag] = 104
INSTANCE_OBJECT: Final[Tag] = 105
INSTANCE_SIMPLE: Final[Tag] = 106
INSTANCE_GENERIC: Final[Tag] = 107


def read_type(data: Buffer) -> Type:
tag = read_tag(data)
Expand Down
Loading