Skip to content

Commit 71025a7

Browse files
Merge branch 'master' into for-map
2 parents 8247ca6 + 0d23c61 commit 71025a7

File tree

94 files changed

+2690
-1170
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+2690
-1170
lines changed

misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
From e6995c91231e1915eba43a29a22dd4cbfaf9e08e Mon Sep 17 00:00:00 2001
1+
From 805d7fc06a8bee350959512e0908a18a87b7f8c2 Mon Sep 17 00:00:00 2001
22
From: Shantanu <[email protected]>
33
Date: Mon, 26 Sep 2022 12:55:07 -0700
44
Subject: [PATCH] Remove use of LiteralString in builtins (#13743)
@@ -8,7 +8,7 @@ Subject: [PATCH] Remove use of LiteralString in builtins (#13743)
88
1 file changed, 1 insertion(+), 99 deletions(-)
99

1010
diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
11-
index 00728f42d..ea77a730f 100644
11+
index c7ab95482..3e93da36e 100644
1212
--- a/mypy/typeshed/stdlib/builtins.pyi
1313
+++ b/mypy/typeshed/stdlib/builtins.pyi
1414
@@ -63,7 +63,6 @@ from typing import ( # noqa: Y022,UP035
@@ -19,7 +19,7 @@ index 00728f42d..ea77a730f 100644
1919
ParamSpec,
2020
Self,
2121
TypeAlias,
22-
@@ -453,31 +452,16 @@ class str(Sequence[str]):
22+
@@ -468,31 +467,16 @@ class str(Sequence[str]):
2323
def __new__(cls, object: object = ...) -> Self: ...
2424
@overload
2525
def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
@@ -51,7 +51,7 @@ index 00728f42d..ea77a730f 100644
5151
def format(self, *args: object, **kwargs: object) -> str: ...
5252
def format_map(self, mapping: _FormatMapMapping, /) -> str: ...
5353
def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
54-
@@ -493,98 +477,34 @@ class str(Sequence[str]):
54+
@@ -508,98 +492,34 @@ class str(Sequence[str]):
5555
def isspace(self) -> bool: ...
5656
def istitle(self) -> bool: ...
5757
def isupper(self) -> bool: ...
@@ -150,7 +150,7 @@ index 00728f42d..ea77a730f 100644
150150
def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc]
151151
@staticmethod
152152
@overload
153-
@@ -595,39 +515,21 @@ class str(Sequence[str]):
153+
@@ -610,39 +530,21 @@ class str(Sequence[str]):
154154
@staticmethod
155155
@overload
156156
def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ...
@@ -190,7 +190,7 @@ index 00728f42d..ea77a730f 100644
190190
- @overload
191191
def __rmul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc]
192192
def __getnewargs__(self) -> tuple[str]: ...
193-
193+
def __format__(self, format_spec: str, /) -> str: ...
194194
--
195-
2.49.0
195+
2.50.1
196196

mypy/build.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@
116116
"abc",
117117
}
118118

119+
# We are careful now, we can increase this in future if safe/useful.
120+
MAX_GC_FREEZE_CYCLES = 1
119121

120122
Graph: _TypeAlias = dict[str, "State"]
121123

@@ -707,6 +709,8 @@ def __init__(
707709
# new file can be processed O(n**2) times. This cache
708710
# avoids most of this redundant work.
709711
self.ast_cache: dict[str, tuple[MypyFile, list[ErrorInfo]]] = {}
712+
# Number of times we used GC optimization hack for fresh SCCs.
713+
self.gc_freeze_cycles = 0
710714

711715
def dump_stats(self) -> None:
712716
if self.options.dump_build_stats:
@@ -3326,8 +3330,29 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
33263330
#
33273331
# TODO: see if it's possible to determine if we need to process only a
33283332
# _subset_ of the past SCCs instead of having to process them all.
3333+
if (
3334+
platform.python_implementation() == "CPython"
3335+
and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES
3336+
):
3337+
# When deserializing cache we create huge amount of new objects, so even
3338+
# with our generous GC thresholds, GC is still doing a lot of pointless
3339+
# work searching for garbage. So, we temporarily disable it when
3340+
# processing fresh SCCs, and then move all the new objects to the oldest
3341+
# generation with the freeze()/unfreeze() trick below. This is arguably
3342+
# a hack, but it gives huge performance wins for large third-party
3343+
# libraries, like torch.
3344+
gc.collect()
3345+
gc.disable()
33293346
for prev_scc in fresh_scc_queue:
33303347
process_fresh_modules(graph, prev_scc, manager)
3348+
if (
3349+
platform.python_implementation() == "CPython"
3350+
and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES
3351+
):
3352+
manager.gc_freeze_cycles += 1
3353+
gc.freeze()
3354+
gc.unfreeze()
3355+
gc.enable()
33313356
fresh_scc_queue = []
33323357
size = len(scc)
33333358
if size == 1:

mypy/checker.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@
171171
from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type, make_optional_type
172172
from mypy.typeops import (
173173
bind_self,
174+
can_have_shared_disjoint_base,
174175
coerce_to_literal,
175176
custom_special_method,
176177
erase_def_to_union_or_bound,
@@ -2658,6 +2659,8 @@ def visit_class_def(self, defn: ClassDef) -> None:
26582659
for base in typ.mro[1:]:
26592660
if base.is_final:
26602661
self.fail(message_registry.CANNOT_INHERIT_FROM_FINAL.format(base.name), defn)
2662+
if not can_have_shared_disjoint_base(typ.bases):
2663+
self.fail(message_registry.INCOMPATIBLE_DISJOINT_BASES.format(typ.name), defn)
26612664
with self.tscope.class_scope(defn.info), self.enter_partial_types(is_class=True):
26622665
old_binder = self.binder
26632666
self.binder = ConditionalTypeBinder(self.options)
@@ -5502,7 +5505,7 @@ def visit_with_stmt(self, s: WithStmt) -> None:
55025505
self.accept(s.body)
55035506

55045507
def check_untyped_after_decorator(self, typ: Type, func: FuncDef) -> None:
5505-
if not self.options.disallow_any_decorated or self.is_stub:
5508+
if not self.options.disallow_any_decorated or self.is_stub or self.current_node_deferred:
55065509
return
55075510

55085511
if mypy.checkexpr.has_any_type(typ):
@@ -5826,6 +5829,10 @@ def _make_fake_typeinfo_and_full_name(
58265829
format_type_distinctly(*base_classes, options=self.options, bare=True), "and"
58275830
)
58285831

5832+
if not can_have_shared_disjoint_base(base_classes):
5833+
errors.append((pretty_names_list, "have distinct disjoint bases"))
5834+
return None
5835+
58295836
new_errors = []
58305837
for base in base_classes:
58315838
if base.type.is_final:

mypy/checkmember.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,13 +1484,7 @@ def analyze_decorator_or_funcbase_access(
14841484
if isinstance(defn, Decorator):
14851485
return analyze_var(name, defn.var, itype, mx)
14861486
typ = function_type(defn, mx.chk.named_type("builtins.function"))
1487-
is_trivial_self = False
1488-
if isinstance(defn, Decorator):
1489-
# Use fast path if there are trivial decorators like @classmethod or @property
1490-
is_trivial_self = defn.func.is_trivial_self and not defn.decorators
1491-
elif isinstance(defn, (FuncDef, OverloadedFuncDef)):
1492-
is_trivial_self = defn.is_trivial_self
1493-
if is_trivial_self:
1487+
if isinstance(defn, (FuncDef, OverloadedFuncDef)) and defn.is_trivial_self:
14941488
return bind_self_fast(typ, mx.self_type)
14951489
typ = check_self_arg(typ, mx.self_type, defn.is_class, mx.context, name, mx.msg)
14961490
return bind_self(typ, original_type=mx.self_type, is_classmethod=defn.is_class)

mypy/message_registry.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
239239
)
240240
CANNOT_MAKE_DELETABLE_FINAL: Final = ErrorMessage("Deletable attribute cannot be final")
241241

242+
# Disjoint bases
243+
INCOMPATIBLE_DISJOINT_BASES: Final = ErrorMessage('Class "{}" has incompatible disjoint bases')
244+
242245
# Enum
243246
ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDDEN: Final = ErrorMessage(
244247
'Assigned "__members__" will be overridden by "Enum" internally'

mypy/nodes.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3004,6 +3004,7 @@ class is generic then it will be a type constructor of higher kind.
30043004
"_mro_refs",
30053005
"bad_mro",
30063006
"is_final",
3007+
"is_disjoint_base",
30073008
"declared_metaclass",
30083009
"metaclass_type",
30093010
"names",
@@ -3055,6 +3056,7 @@ class is generic then it will be a type constructor of higher kind.
30553056
_mro_refs: list[str] | None
30563057
bad_mro: bool # Could not construct full MRO
30573058
is_final: bool
3059+
is_disjoint_base: bool
30583060

30593061
declared_metaclass: mypy.types.Instance | None
30603062
metaclass_type: mypy.types.Instance | None
@@ -3209,6 +3211,7 @@ class is generic then it will be a type constructor of higher kind.
32093211
"is_protocol",
32103212
"runtime_protocol",
32113213
"is_final",
3214+
"is_disjoint_base",
32123215
"is_intersection",
32133216
]
32143217

@@ -3241,6 +3244,7 @@ def __init__(self, names: SymbolTable, defn: ClassDef, module_name: str) -> None
32413244
self.type_var_tuple_suffix: int | None = None
32423245
self.add_type_vars()
32433246
self.is_final = False
3247+
self.is_disjoint_base = False
32443248
self.is_enum = False
32453249
self.fallback_to_any = False
32463250
self.meta_fallback_to_any = False

mypy/semanal.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@
254254
ASSERT_TYPE_NAMES,
255255
DATACLASS_TRANSFORM_NAMES,
256256
DEPRECATED_TYPE_NAMES,
257+
DISJOINT_BASE_DECORATOR_NAMES,
257258
FINAL_DECORATOR_NAMES,
258259
FINAL_TYPE_NAMES,
259260
IMPORTED_REVEAL_TYPE_NAMES,
@@ -1059,7 +1060,7 @@ def remove_unpack_kwargs(self, defn: FuncDef, typ: CallableType) -> CallableType
10591060
# It is OK for TypedDict to have a key named 'kwargs'.
10601061
overlap.discard(typ.arg_names[-1])
10611062
if overlap:
1062-
overlapped = ", ".join([f'"{name}"' for name in overlap])
1063+
overlapped = ", ".join([f'"{name}"' for name in sorted(filter(None, overlap))])
10631064
self.fail(f"Overlap between argument names and ** TypedDict items: {overlapped}", defn)
10641065
new_arg_types = typ.arg_types[:-1] + [AnyType(TypeOfAny.from_error)]
10651066
return typ.copy_modified(arg_types=new_arg_types)
@@ -2188,6 +2189,8 @@ def analyze_class_decorator_common(
21882189
"""
21892190
if refers_to_fullname(decorator, FINAL_DECORATOR_NAMES):
21902191
info.is_final = True
2192+
elif refers_to_fullname(decorator, DISJOINT_BASE_DECORATOR_NAMES):
2193+
info.is_disjoint_base = True
21912194
elif refers_to_fullname(decorator, TYPE_CHECK_ONLY_NAMES):
21922195
info.is_type_check_only = True
21932196
elif (deprecated := self.get_deprecated(decorator)) is not None:
@@ -5332,13 +5335,15 @@ def visit_expression_stmt(self, s: ExpressionStmt) -> None:
53325335
s.expr.accept(self)
53335336

53345337
def visit_return_stmt(self, s: ReturnStmt) -> None:
5338+
old = self.statement
53355339
self.statement = s
53365340
if not self.is_func_scope():
53375341
self.fail('"return" outside function', s)
53385342
if self.return_stmt_inside_except_star_block:
53395343
self.fail('"return" not allowed in except* block', s, serious=True)
53405344
if s.expr:
53415345
s.expr.accept(self)
5346+
self.statement = old
53425347

53435348
def visit_raise_stmt(self, s: RaiseStmt) -> None:
53445349
self.statement = s

mypy/stubtest.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import os
1818
import pkgutil
1919
import re
20+
import struct
2021
import symtable
2122
import sys
2223
import traceback
@@ -466,6 +467,67 @@ class SubClass(runtime): # type: ignore[misc]
466467
)
467468

468469

470+
SIZEOF_PYOBJECT = struct.calcsize("P")
471+
472+
473+
def _shape_differs(t1: type[object], t2: type[object]) -> bool:
474+
"""Check whether two types differ in shape.
475+
476+
Mirrors the shape_differs() function in typeobject.c in CPython."""
477+
if sys.version_info >= (3, 12):
478+
return t1.__basicsize__ != t2.__basicsize__ or t1.__itemsize__ != t2.__itemsize__
479+
else:
480+
# CPython had more complicated logic before 3.12:
481+
# https://github.com/python/cpython/blob/f3c6f882cddc8dc30320d2e73edf019e201394fc/Objects/typeobject.c#L2224
482+
# We attempt to mirror it here well enough to support the most common cases.
483+
if t1.__itemsize__ or t2.__itemsize__:
484+
return t1.__basicsize__ != t2.__basicsize__ or t1.__itemsize__ != t2.__itemsize__
485+
t_size = t1.__basicsize__
486+
if not t2.__weakrefoffset__ and t1.__weakrefoffset__ + SIZEOF_PYOBJECT == t_size:
487+
t_size -= SIZEOF_PYOBJECT
488+
if not t2.__dictoffset__ and t1.__dictoffset__ + SIZEOF_PYOBJECT == t_size:
489+
t_size -= SIZEOF_PYOBJECT
490+
if not t2.__weakrefoffset__ and t2.__weakrefoffset__ == t_size:
491+
t_size -= SIZEOF_PYOBJECT
492+
return t_size != t2.__basicsize__
493+
494+
495+
def _is_disjoint_base(typ: type[object]) -> bool:
496+
"""Return whether a type is a disjoint base at runtime, mirroring CPython's logic in typeobject.c.
497+
498+
See PEP 800."""
499+
if typ is object:
500+
return True
501+
base = typ.__base__
502+
assert base is not None, f"Type {typ} has no base"
503+
return _shape_differs(typ, base)
504+
505+
506+
def _verify_disjoint_base(
507+
stub: nodes.TypeInfo, runtime: type[object], object_path: list[str]
508+
) -> Iterator[Error]:
509+
# If it's final, doesn't matter whether it's a disjoint base or not
510+
if stub.is_final:
511+
return
512+
is_disjoint_runtime = _is_disjoint_base(runtime)
513+
if is_disjoint_runtime and not stub.is_disjoint_base:
514+
yield Error(
515+
object_path,
516+
"is a disjoint base at runtime, but isn't marked with @disjoint_base in the stub",
517+
stub,
518+
runtime,
519+
stub_desc=repr(stub),
520+
)
521+
elif not is_disjoint_runtime and stub.is_disjoint_base:
522+
yield Error(
523+
object_path,
524+
"is marked with @disjoint_base in the stub, but isn't a disjoint base at runtime",
525+
stub,
526+
runtime,
527+
stub_desc=repr(stub),
528+
)
529+
530+
469531
def _verify_metaclass(
470532
stub: nodes.TypeInfo, runtime: type[Any], object_path: list[str], *, is_runtime_typeddict: bool
471533
) -> Iterator[Error]:
@@ -534,6 +596,7 @@ def verify_typeinfo(
534596
return
535597

536598
yield from _verify_final(stub, runtime, object_path)
599+
yield from _verify_disjoint_base(stub, runtime, object_path)
537600
is_runtime_typeddict = stub.typeddict_type is not None and is_typeddict(runtime)
538601
yield from _verify_metaclass(
539602
stub, runtime, object_path, is_runtime_typeddict=is_runtime_typeddict

mypy/test/testsolve.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,14 @@ def test_multiple_variables(self) -> None:
6464
)
6565

6666
def test_no_constraints_for_var(self) -> None:
67-
self.assert_solve([self.fx.t], [], [self.fx.uninhabited])
68-
self.assert_solve([self.fx.t, self.fx.s], [], [self.fx.uninhabited, self.fx.uninhabited])
67+
self.assert_solve([self.fx.t], [], [self.fx.a_uninhabited])
68+
self.assert_solve(
69+
[self.fx.t, self.fx.s], [], [self.fx.a_uninhabited, self.fx.a_uninhabited]
70+
)
6971
self.assert_solve(
7072
[self.fx.t, self.fx.s],
7173
[self.supc(self.fx.s, self.fx.a)],
72-
[self.fx.uninhabited, self.fx.a],
74+
[self.fx.a_uninhabited, self.fx.a],
7375
)
7476

7577
def test_simple_constraints_with_dynamic_type(self) -> None:
@@ -116,7 +118,7 @@ def test_poly_no_constraints(self) -> None:
116118
self.assert_solve(
117119
[self.fx.t, self.fx.u],
118120
[],
119-
[self.fx.uninhabited, self.fx.uninhabited],
121+
[self.fx.a_uninhabited, self.fx.a_uninhabited],
120122
allow_polymorphic=True,
121123
)
122124

@@ -152,7 +154,7 @@ def test_poly_free_pair_with_bounds_uninhabited(self) -> None:
152154
self.assert_solve(
153155
[self.fx.ub, self.fx.uc],
154156
[self.subc(self.fx.ub, self.fx.uc)],
155-
[self.fx.uninhabited, self.fx.uninhabited],
157+
[self.fx.a_uninhabited, self.fx.a_uninhabited],
156158
[],
157159
allow_polymorphic=True,
158160
)

0 commit comments

Comments
 (0)