From 9bfc6ae09e4418a539f45c9297ae61b535b78e46 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 19 Dec 2022 15:34:36 +0000 Subject: [PATCH 1/2] Optimize implementation of TypedDict types for **kwds The implementation copied lots of callable types even when not using the new feature, which was expensive. Now we only generate a copy if a callable actually uses TypedDict types for **kwds. This made self check 7-8% faster (when compiled with -O0). The original implementation was in #13471. --- mypy/types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 0ba0985436ed..1c7b73061296 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1976,7 +1976,7 @@ def expand_param_spec( def with_unpacked_kwargs(self) -> NormalizedCallableType: if not self.unpack_kwargs: - return NormalizedCallableType(self.copy_modified()) + return cast(NormalizedCallableType, self) last_type = get_proper_type(self.arg_types[-1]) assert isinstance(last_type, TypedDictType) extra_kinds = [ @@ -2126,7 +2126,9 @@ def get_name(self) -> str | None: return self._items[0].name def with_unpacked_kwargs(self) -> Overloaded: - return Overloaded([i.with_unpacked_kwargs() for i in self.items]) + if any(i.unpack_kwargs for i in self.items): + return Overloaded([i.with_unpacked_kwargs() for i in self.items]) + return self def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_overloaded(self) From 938df3774559924c1608e5a81f808cea8afe1859 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 19 Dec 2022 16:04:59 +0000 Subject: [PATCH 2/2] Also make copy_modified faster --- mypy/types.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index 1c7b73061296..ab2caa96e535 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1757,7 +1757,7 @@ def copy_modified( from_concatenate: Bogus[bool] = _dummy, unpack_kwargs: Bogus[bool] = _dummy, ) -> CT: - return type(self)( + modified = CallableType( arg_types=arg_types if arg_types is not _dummy else self.arg_types, arg_kinds=arg_kinds if arg_kinds is not _dummy else self.arg_kinds, arg_names=arg_names if arg_names is not _dummy else self.arg_names, @@ -1782,6 +1782,9 @@ def copy_modified( ), unpack_kwargs=unpack_kwargs if unpack_kwargs is not _dummy else self.unpack_kwargs, ) + # Optimization: Only NewTypes are supported as subtypes since + # the class is effectively final, so we can use a cast safely. + return cast(CT, modified) def var_arg(self) -> FormalArgument | None: """The formal argument for *args."""