Skip to content

Commit e3d94ef

Browse files
Merge branch 'master' into dict-literals
2 parents db9884c + 2c6c395 commit e3d94ef

37 files changed

+1035
-177
lines changed

docs/source/generics.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1165,7 +1165,7 @@ This example correctly uses a covariant type variable:
11651165
11661166
See :ref:`variance-of-generics` for more about variance.
11671167

1168-
Generic protocols can also be recursive. Example (Python 3.12 synta):
1168+
Generic protocols can also be recursive. Example (Python 3.12 syntax):
11691169

11701170
.. code-block:: python
11711171

mypy/checkexpr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1502,7 +1502,7 @@ def check_call_expr_with_callee_type(
15021502
def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str) -> Type:
15031503
"""Type check calling a member expression where the base type is a union."""
15041504
res: list[Type] = []
1505-
for typ in object_type.relevant_items():
1505+
for typ in flatten_nested_unions(object_type.relevant_items()):
15061506
# Member access errors are already reported when visiting the member expression.
15071507
with self.msg.filter_errors():
15081508
item = analyze_member_access(

mypy/errors.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,9 @@ def format_messages(
10031003
marker = "^"
10041004
if end_line == line and end_column > column:
10051005
marker = f'^{"~" * (end_column - column - 1)}'
1006+
elif end_line != line:
1007+
# just highlight the first line instead
1008+
marker = f'^{"~" * (len(source_line_expanded) - column - 1)}'
10061009
a.append(" " * (DEFAULT_SOURCE_OFFSET + column) + marker)
10071010
return a
10081011

mypy/plugins/default.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import mypy.errorcodes as codes
77
from mypy import message_registry
8-
from mypy.nodes import DictExpr, IntExpr, StrExpr, UnaryExpr
8+
from mypy.nodes import DictExpr, Expression, IntExpr, StrExpr, UnaryExpr
99
from mypy.plugin import (
1010
AttributeContext,
1111
ClassDefContext,
@@ -263,30 +263,40 @@ def typed_dict_get_callback(ctx: MethodContext) -> Type:
263263
if keys is None:
264264
return ctx.default_return_type
265265

266+
default_type: Type
267+
default_arg: Expression | None
268+
if len(ctx.arg_types) <= 1 or not ctx.arg_types[1]:
269+
default_arg = None
270+
default_type = NoneType()
271+
elif len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1:
272+
default_arg = ctx.args[1][0]
273+
default_type = ctx.arg_types[1][0]
274+
else:
275+
return ctx.default_return_type
276+
266277
output_types: list[Type] = []
267278
for key in keys:
268-
value_type = get_proper_type(ctx.type.items.get(key))
279+
value_type: Type | None = ctx.type.items.get(key)
269280
if value_type is None:
270281
return ctx.default_return_type
271282

272-
if len(ctx.arg_types) == 1:
283+
if key in ctx.type.required_keys:
273284
output_types.append(value_type)
274-
elif len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1:
275-
default_arg = ctx.args[1][0]
285+
else:
286+
# HACK to deal with get(key, {})
276287
if (
277288
isinstance(default_arg, DictExpr)
278289
and len(default_arg.items) == 0
279-
and isinstance(value_type, TypedDictType)
290+
and isinstance(vt := get_proper_type(value_type), TypedDictType)
280291
):
281-
# Special case '{}' as the default for a typed dict type.
282-
output_types.append(value_type.copy_modified(required_keys=set()))
292+
output_types.append(vt.copy_modified(required_keys=set()))
283293
else:
284294
output_types.append(value_type)
285-
output_types.append(ctx.arg_types[1][0])
286-
287-
if len(ctx.arg_types) == 1:
288-
output_types.append(NoneType())
295+
output_types.append(default_type)
289296

297+
# for nicer reveal_type, put default at the end, if it is present
298+
if default_type in output_types:
299+
output_types = [t for t in output_types if t != default_type] + [default_type]
290300
return make_simplified_union(output_types)
291301
return ctx.default_return_type
292302

mypy/stubtest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
968968

969969
is_arg_pos_only: defaultdict[str, set[bool]] = defaultdict(set)
970970
for func in map(_resolve_funcitem_from_decorator, stub.items):
971-
assert func is not None, "Failed to resolve decorated overload"
971+
assert func is not None, f"Failed to resolve decorated overload of {stub.fullname!r}"
972972
args = maybe_strip_cls(stub.name, func.arguments)
973973
for index, arg in enumerate(args):
974974
if (
@@ -984,7 +984,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
984984

985985
all_args: dict[str, list[tuple[nodes.Argument, int]]] = {}
986986
for func in map(_resolve_funcitem_from_decorator, stub.items):
987-
assert func is not None, "Failed to resolve decorated overload"
987+
assert func is not None, f"Failed to resolve decorated overload of {stub.fullname!r}"
988988
args = maybe_strip_cls(stub.name, func.arguments)
989989
for index, arg in enumerate(args):
990990
# For positional-only args, we allow overloads to have different names for the same

mypyc/codegen/emitclass.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ def setter_name(cl: ClassIR, attribute: str, names: NameGenerator) -> str:
410410

411411

412412
def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None:
413-
seen_attrs: set[tuple[str, RType]] = set()
413+
seen_attrs: set[str] = set()
414414
lines: list[str] = []
415415
lines += ["typedef struct {", "PyObject_HEAD", "CPyVTableItem *vtable;"]
416416
if cl.has_method("__call__"):
@@ -427,9 +427,11 @@ def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None:
427427
lines.append(f"{BITMAP_TYPE} {attr};")
428428
bitmap_attrs.append(attr)
429429
for attr, rtype in base.attributes.items():
430-
if (attr, rtype) not in seen_attrs:
430+
# Generated class may redefine certain attributes with different
431+
# types in subclasses (this would be unsafe for user-defined classes).
432+
if attr not in seen_attrs:
431433
lines.append(f"{emitter.ctype_spaced(rtype)}{emitter.attr(attr)};")
432-
seen_attrs.add((attr, rtype))
434+
seen_attrs.add(attr)
433435

434436
if isinstance(rtype, RTuple):
435437
emitter.declare_tuple_struct(rtype)

mypyc/codegen/emitmodule.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,8 @@ def emit_module_exec_func(
10671067
"(PyObject *){t}_template, NULL, modname);".format(t=type_struct)
10681068
)
10691069
emitter.emit_lines(f"if (unlikely(!{type_struct}))", " goto fail;")
1070+
name_prefix = cl.name_prefix(emitter.names)
1071+
emitter.emit_line(f"CPyDef_{name_prefix}_trait_vtable_setup();")
10701072

10711073
emitter.emit_lines("if (CPyGlobalsInit() < 0)", " goto fail;")
10721074

mypyc/ir/func_ir.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,11 @@ def __init__(
149149
module_name: str,
150150
sig: FuncSignature,
151151
kind: int = FUNC_NORMAL,
152+
*,
152153
is_prop_setter: bool = False,
153154
is_prop_getter: bool = False,
155+
is_generator: bool = False,
156+
is_coroutine: bool = False,
154157
implicit: bool = False,
155158
internal: bool = False,
156159
) -> None:
@@ -161,6 +164,8 @@ def __init__(
161164
self.kind = kind
162165
self.is_prop_setter = is_prop_setter
163166
self.is_prop_getter = is_prop_getter
167+
self.is_generator = is_generator
168+
self.is_coroutine = is_coroutine
164169
if class_name is None:
165170
self.bound_sig: FuncSignature | None = None
166171
else:
@@ -219,6 +224,8 @@ def serialize(self) -> JsonDict:
219224
"kind": self.kind,
220225
"is_prop_setter": self.is_prop_setter,
221226
"is_prop_getter": self.is_prop_getter,
227+
"is_generator": self.is_generator,
228+
"is_coroutine": self.is_coroutine,
222229
"implicit": self.implicit,
223230
"internal": self.internal,
224231
}
@@ -240,10 +247,12 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> FuncDecl:
240247
data["module_name"],
241248
FuncSignature.deserialize(data["sig"], ctx),
242249
data["kind"],
243-
data["is_prop_setter"],
244-
data["is_prop_getter"],
245-
data["implicit"],
246-
data["internal"],
250+
is_prop_setter=data["is_prop_setter"],
251+
is_prop_getter=data["is_prop_getter"],
252+
is_generator=data["is_generator"],
253+
is_coroutine=data["is_coroutine"],
254+
implicit=data["implicit"],
255+
internal=data["internal"],
247256
)
248257

249258

mypyc/irbuild/context.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,11 @@ def curr_env_reg(self) -> Value:
9898
def can_merge_generator_and_env_classes(self) -> bool:
9999
# In simple cases we can place the environment into the generator class,
100100
# instead of having two separate classes.
101-
return self.is_generator and not self.is_nested and not self.contains_nested
101+
if self._generator_class and not self._generator_class.ir.is_final_class:
102+
result = False
103+
else:
104+
result = self.is_generator and not self.is_nested and not self.contains_nested
105+
return result
102106

103107

104108
class ImplicitClass:

mypyc/irbuild/for_helpers.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,18 +1203,18 @@ def gen_cleanup(self) -> None:
12031203
gen.gen_cleanup()
12041204

12051205

1206-
def get_expr_length(expr: Expression) -> int | None:
1206+
def get_expr_length(builder: IRBuilder, expr: Expression) -> int | None:
12071207
if isinstance(expr, (StrExpr, BytesExpr)):
12081208
return len(expr.value)
12091209
elif isinstance(expr, (ListExpr, TupleExpr)):
12101210
# if there are no star expressions, or we know the length of them,
12111211
# we know the length of the expression
1212-
stars = [get_expr_length(i) for i in expr.items if isinstance(i, StarExpr)]
1212+
stars = [get_expr_length(builder, i) for i in expr.items if isinstance(i, StarExpr)]
12131213
if None not in stars:
12141214
other = sum(not isinstance(i, StarExpr) for i in expr.items)
12151215
return other + sum(stars) # type: ignore [arg-type]
12161216
elif isinstance(expr, StarExpr):
1217-
return get_expr_length(expr.expr)
1217+
return get_expr_length(builder, expr.expr)
12181218
elif (
12191219
isinstance(expr, RefExpr)
12201220
and isinstance(expr.node, Var)
@@ -1227,6 +1227,11 @@ def get_expr_length(expr: Expression) -> int | None:
12271227
# performance boost and can be (sometimes) figured out pretty easily. set and dict
12281228
# comps *can* be done as well but will need special logic to consider the possibility
12291229
# of key conflicts. Range, enumerate, zip are all simple logic.
1230+
1231+
# we might still be able to get the length directly from the type
1232+
rtype = builder.node_type(expr)
1233+
if isinstance(rtype, RTuple):
1234+
return len(rtype.types)
12301235
return None
12311236

12321237

@@ -1235,7 +1240,7 @@ def get_expr_length_value(
12351240
) -> Value:
12361241
rtype = builder.node_type(expr)
12371242
assert is_sequence_rprimitive(rtype) or isinstance(rtype, RTuple), rtype
1238-
length = get_expr_length(expr)
1243+
length = get_expr_length(builder, expr)
12391244
if length is None:
12401245
# We cannot compute the length at compile time, so we will fetch it.
12411246
return builder.builder.builtin_len(expr_reg, line, use_pyssize_t=use_pyssize_t)

0 commit comments

Comments
 (0)