Skip to content

Commit ab8d33b

Browse files
committed
Merge remote-tracking branch 'origin/master' into 2023-01-25-attrs-evolve
2 parents 0976f86 + b2cf9d1 commit ab8d33b

Some content is hidden

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

58 files changed

+412
-87
lines changed

mypy/checkexpr.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,8 @@ def try_infer_partial_type(self, e: CallExpr) -> None:
899899
return
900900
var, partial_types = ret
901901
typ = self.try_infer_partial_value_type_from_call(e, callee.name, var)
902-
if typ is not None:
902+
# Var may be deleted from partial_types in try_infer_partial_value_type_from_call
903+
if typ is not None and var in partial_types:
903904
var.type = typ
904905
del partial_types[var]
905906
elif isinstance(callee.expr, IndexExpr) and isinstance(callee.expr.base, RefExpr):

mypy/constraints.py

+11
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,17 @@ def visit_instance(self, template: Instance) -> list[Constraint]:
619619
actual.item, template, subtype, template, class_obj=True
620620
)
621621
)
622+
if self.direction == SUPERTYPE_OF:
623+
# Infer constraints for Type[T] via metaclass of T when it makes sense.
624+
a_item = actual.item
625+
if isinstance(a_item, TypeVarType):
626+
a_item = get_proper_type(a_item.upper_bound)
627+
if isinstance(a_item, Instance) and a_item.type.metaclass_type:
628+
res.extend(
629+
self.infer_constraints_from_protocol_members(
630+
a_item.type.metaclass_type, template, actual, template
631+
)
632+
)
622633

623634
if isinstance(actual, Overloaded) and actual.fallback is not None:
624635
actual = actual.fallback

mypy/messages.py

-2
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,6 @@
122122
# test-data/unit/fixtures/) that provides the definition. This is used for
123123
# generating better error messages when running mypy tests only.
124124
SUGGESTED_TEST_FIXTURES: Final = {
125-
"builtins.list": "list.pyi",
126-
"builtins.dict": "dict.pyi",
127125
"builtins.set": "set.pyi",
128126
"builtins.tuple": "tuple.pyi",
129127
"builtins.bool": "bool.pyi",

mypy/plugins/attrs.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ def _add_attrs_magic_attribute(
831831
ctx.cls,
832832
MAGIC_ATTR_NAME,
833833
TupleType(attributes_types, fallback=attributes_type),
834-
fullname=f"{ctx.cls.fullname}.{attr_name}",
834+
fullname=f"{ctx.cls.fullname}.{MAGIC_ATTR_NAME}",
835835
override_allow_incompatible=True,
836836
is_classvar=True,
837837
)

mypy/semanal.py

+39-14
Original file line numberDiff line numberDiff line change
@@ -625,23 +625,23 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None:
625625
continue
626626
# Need to construct the type ourselves, to avoid issues with __builtins__.list
627627
# not being subscriptable or typing.List not getting bound
628-
sym = self.lookup_qualified("__builtins__.list", Context())
629-
if not sym:
630-
continue
631-
node = sym.node
632-
if not isinstance(node, TypeInfo):
633-
self.defer(node)
628+
inst = self.named_type_or_none("builtins.list", [str_type])
629+
if inst is None:
630+
assert not self.final_iteration, "Cannot find builtins.list to add __path__"
631+
self.defer()
634632
return
635-
typ = Instance(node, [str_type])
633+
typ = inst
636634
elif name == "__annotations__":
637-
sym = self.lookup_qualified("__builtins__.dict", Context(), suppress_errors=True)
638-
if not sym:
639-
continue
640-
node = sym.node
641-
if not isinstance(node, TypeInfo):
642-
self.defer(node)
635+
inst = self.named_type_or_none(
636+
"builtins.dict", [str_type, AnyType(TypeOfAny.special_form)]
637+
)
638+
if inst is None:
639+
assert (
640+
not self.final_iteration
641+
), "Cannot find builtins.dict to add __annotations__"
642+
self.defer()
643643
return
644-
typ = Instance(node, [str_type, AnyType(TypeOfAny.special_form)])
644+
typ = inst
645645
else:
646646
assert t is not None, f"type should be specified for {name}"
647647
typ = UnboundType(t)
@@ -2658,8 +2658,33 @@ def visit_import_all(self, i: ImportAll) -> None:
26582658

26592659
def visit_assignment_expr(self, s: AssignmentExpr) -> None:
26602660
s.value.accept(self)
2661+
if self.is_func_scope():
2662+
if not self.check_valid_comprehension(s):
2663+
return
26612664
self.analyze_lvalue(s.target, escape_comprehensions=True, has_explicit_value=True)
26622665

2666+
def check_valid_comprehension(self, s: AssignmentExpr) -> bool:
2667+
"""Check that assignment expression is not nested within comprehension at class scope.
2668+
2669+
class C:
2670+
[(j := i) for i in [1, 2, 3]]
2671+
is a syntax error that is not enforced by Python parser, but at later steps.
2672+
"""
2673+
for i, is_comprehension in enumerate(reversed(self.is_comprehension_stack)):
2674+
if not is_comprehension and i < len(self.locals) - 1:
2675+
if self.locals[-1 - i] is None:
2676+
self.fail(
2677+
"Assignment expression within a comprehension"
2678+
" cannot be used in a class body",
2679+
s,
2680+
code=codes.SYNTAX,
2681+
serious=True,
2682+
blocker=True,
2683+
)
2684+
return False
2685+
break
2686+
return True
2687+
26632688
def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
26642689
self.statement = s
26652690

mypy/semanal_namedtuple.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -481,13 +481,9 @@ def build_namedtuple_typeinfo(
481481
strtype = self.api.named_type("builtins.str")
482482
implicit_any = AnyType(TypeOfAny.special_form)
483483
basetuple_type = self.api.named_type("builtins.tuple", [implicit_any])
484-
dictype = self.api.named_type_or_none(
485-
"builtins.dict", [strtype, implicit_any]
486-
) or self.api.named_type("builtins.object")
484+
dictype = self.api.named_type("builtins.dict", [strtype, implicit_any])
487485
# Actual signature should return OrderedDict[str, Union[types]]
488-
ordereddictype = self.api.named_type_or_none(
489-
"builtins.dict", [strtype, implicit_any]
490-
) or self.api.named_type("builtins.object")
486+
ordereddictype = self.api.named_type("builtins.dict", [strtype, implicit_any])
491487
fallback = self.api.named_type("builtins.tuple", [implicit_any])
492488
# Note: actual signature should accept an invariant version of Iterable[UnionType[types]].
493489
# but it can't be expressed. 'new' and 'len' should be callable types.

mypy/stubgen.py

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
MemberExpr,
9696
MypyFile,
9797
NameExpr,
98+
OpExpr,
9899
OverloadedFuncDef,
99100
Statement,
100101
StrExpr,
@@ -402,6 +403,9 @@ def visit_list_expr(self, node: ListExpr) -> str:
402403
def visit_ellipsis(self, node: EllipsisExpr) -> str:
403404
return "..."
404405

406+
def visit_op_expr(self, o: OpExpr) -> str:
407+
return f"{o.left.accept(self)} {o.op} {o.right.accept(self)}"
408+
405409

406410
class ImportTracker:
407411
"""Record necessary imports during stub generation."""

mypy/test/data.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,12 @@ def parse_test_case(case: DataDrivenTestCase) -> None:
8181
output_files.append((file_entry[0], re.compile(file_entry[1].rstrip(), re.S)))
8282
else:
8383
output_files.append(file_entry)
84-
elif item.id in ("builtins", "builtins_py2"):
84+
elif item.id == "builtins":
8585
# Use an alternative stub file for the builtins module.
8686
assert item.arg is not None
8787
mpath = join(os.path.dirname(case.file), item.arg)
88-
fnam = "builtins.pyi" if item.id == "builtins" else "__builtin__.pyi"
8988
with open(mpath, encoding="utf8") as f:
90-
files.append((join(base_path, fnam), f.read()))
89+
files.append((join(base_path, "builtins.pyi"), f.read()))
9190
elif item.id == "typing":
9291
# Use an alternative stub file for the typing module.
9392
assert item.arg is not None

mypyc/irbuild/statement.py

+38-16
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
Integer,
5151
LoadAddress,
5252
LoadErrorValue,
53+
MethodCall,
5354
RaiseStandardError,
5455
Register,
5556
Return,
@@ -61,6 +62,7 @@
6162
RInstance,
6263
exc_rtuple,
6364
is_tagged,
65+
none_rprimitive,
6466
object_pointer_rprimitive,
6567
object_rprimitive,
6668
)
@@ -657,14 +659,45 @@ def transform_with(
657659
al = "a" if is_async else ""
658660

659661
mgr_v = builder.accept(expr)
660-
typ = builder.call_c(type_op, [mgr_v], line)
661-
exit_ = builder.maybe_spill(builder.py_get_attr(typ, f"__{al}exit__", line))
662-
value = builder.py_call(builder.py_get_attr(typ, f"__{al}enter__", line), [mgr_v], line)
662+
is_native = isinstance(mgr_v.type, RInstance)
663+
if is_native:
664+
value = builder.add(MethodCall(mgr_v, f"__{al}enter__", args=[], line=line))
665+
exit_ = None
666+
else:
667+
typ = builder.call_c(type_op, [mgr_v], line)
668+
exit_ = builder.maybe_spill(builder.py_get_attr(typ, f"__{al}exit__", line))
669+
value = builder.py_call(builder.py_get_attr(typ, f"__{al}enter__", line), [mgr_v], line)
670+
663671
mgr = builder.maybe_spill(mgr_v)
664672
exc = builder.maybe_spill_assignable(builder.true())
665673
if is_async:
666674
value = emit_await(builder, value, line)
667675

676+
def maybe_natively_call_exit(exc_info: bool) -> Value:
677+
if exc_info:
678+
args = get_sys_exc_info(builder)
679+
else:
680+
none = builder.none_object()
681+
args = [none, none, none]
682+
683+
if is_native:
684+
assert isinstance(mgr_v.type, RInstance)
685+
exit_val = builder.gen_method_call(
686+
builder.read(mgr),
687+
f"__{al}exit__",
688+
arg_values=args,
689+
line=line,
690+
result_type=none_rprimitive,
691+
)
692+
else:
693+
assert exit_ is not None
694+
exit_val = builder.py_call(builder.read(exit_), [builder.read(mgr)] + args, line)
695+
696+
if is_async:
697+
return emit_await(builder, exit_val, line)
698+
else:
699+
return exit_val
700+
668701
def try_body() -> None:
669702
if target:
670703
builder.assign(builder.get_assignment_target(target), value, line)
@@ -673,13 +706,7 @@ def try_body() -> None:
673706
def except_body() -> None:
674707
builder.assign(exc, builder.false(), line)
675708
out_block, reraise_block = BasicBlock(), BasicBlock()
676-
exit_val = builder.py_call(
677-
builder.read(exit_), [builder.read(mgr)] + get_sys_exc_info(builder), line
678-
)
679-
if is_async:
680-
exit_val = emit_await(builder, exit_val, line)
681-
682-
builder.add_bool_branch(exit_val, out_block, reraise_block)
709+
builder.add_bool_branch(maybe_natively_call_exit(exc_info=True), out_block, reraise_block)
683710
builder.activate_block(reraise_block)
684711
builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
685712
builder.add(Unreachable())
@@ -689,13 +716,8 @@ def finally_body() -> None:
689716
out_block, exit_block = BasicBlock(), BasicBlock()
690717
builder.add(Branch(builder.read(exc), exit_block, out_block, Branch.BOOL))
691718
builder.activate_block(exit_block)
692-
none = builder.none_object()
693-
exit_val = builder.py_call(
694-
builder.read(exit_), [builder.read(mgr), none, none, none], line
695-
)
696-
if is_async:
697-
emit_await(builder, exit_val, line)
698719

720+
maybe_natively_call_exit(exc_info=False)
699721
builder.goto_and_activate(out_block)
700722

701723
transform_try_finally_stmt(

mypyc/test-data/irbuild-try.test

+105
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,108 @@ L19:
416416
L20:
417417
return 1
418418

419+
[case testWithNativeSimple]
420+
class DummyContext:
421+
def __enter__(self) -> None:
422+
pass
423+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
424+
pass
425+
426+
def foo(x: DummyContext) -> None:
427+
with x:
428+
print('hello')
429+
[out]
430+
def DummyContext.__enter__(self):
431+
self :: __main__.DummyContext
432+
L0:
433+
return 1
434+
def DummyContext.__exit__(self, exc_type, exc_val, exc_tb):
435+
self :: __main__.DummyContext
436+
exc_type, exc_val, exc_tb :: object
437+
L0:
438+
return 1
439+
def foo(x):
440+
x :: __main__.DummyContext
441+
r0 :: None
442+
r1 :: bool
443+
r2 :: str
444+
r3 :: object
445+
r4 :: str
446+
r5, r6 :: object
447+
r7, r8 :: tuple[object, object, object]
448+
r9, r10, r11 :: object
449+
r12 :: None
450+
r13 :: object
451+
r14 :: int32
452+
r15 :: bit
453+
r16 :: bool
454+
r17 :: bit
455+
r18, r19, r20 :: tuple[object, object, object]
456+
r21 :: object
457+
r22 :: None
458+
r23 :: bit
459+
L0:
460+
r0 = x.__enter__()
461+
r1 = 1
462+
L1:
463+
L2:
464+
r2 = 'hello'
465+
r3 = builtins :: module
466+
r4 = 'print'
467+
r5 = CPyObject_GetAttr(r3, r4)
468+
r6 = PyObject_CallFunctionObjArgs(r5, r2, 0)
469+
goto L8
470+
L3: (handler for L2)
471+
r7 = CPy_CatchError()
472+
r1 = 0
473+
r8 = CPy_GetExcInfo()
474+
r9 = r8[0]
475+
r10 = r8[1]
476+
r11 = r8[2]
477+
r12 = x.__exit__(r9, r10, r11)
478+
r13 = box(None, r12)
479+
r14 = PyObject_IsTrue(r13)
480+
r15 = r14 >= 0 :: signed
481+
r16 = truncate r14: int32 to builtins.bool
482+
if r16 goto L5 else goto L4 :: bool
483+
L4:
484+
CPy_Reraise()
485+
unreachable
486+
L5:
487+
L6:
488+
CPy_RestoreExcInfo(r7)
489+
goto L8
490+
L7: (handler for L3, L4, L5)
491+
CPy_RestoreExcInfo(r7)
492+
r17 = CPy_KeepPropagating()
493+
unreachable
494+
L8:
495+
L9:
496+
L10:
497+
r18 = <error> :: tuple[object, object, object]
498+
r19 = r18
499+
goto L12
500+
L11: (handler for L1, L6, L7, L8)
501+
r20 = CPy_CatchError()
502+
r19 = r20
503+
L12:
504+
if r1 goto L13 else goto L14 :: bool
505+
L13:
506+
r21 = load_address _Py_NoneStruct
507+
r22 = x.__exit__(r21, r21, r21)
508+
L14:
509+
if is_error(r19) goto L16 else goto L15
510+
L15:
511+
CPy_Reraise()
512+
unreachable
513+
L16:
514+
goto L20
515+
L17: (handler for L12, L13, L14, L15)
516+
if is_error(r19) goto L19 else goto L18
517+
L18:
518+
CPy_RestoreExcInfo(r19)
519+
L19:
520+
r23 = CPy_KeepPropagating()
521+
unreachable
522+
L20:
523+
return 1

mypyc/test-data/run-generators.test

+17
Original file line numberDiff line numberDiff line change
@@ -662,3 +662,20 @@ def list_comp() -> List[int]:
662662
[file driver.py]
663663
from native import list_comp
664664
assert list_comp() == [5]
665+
666+
[case testWithNative]
667+
class DummyContext:
668+
def __init__(self) -> None:
669+
self.x = 0
670+
671+
def __enter__(self) -> None:
672+
self.x += 1
673+
674+
def __exit__(self, exc_type, exc_value, exc_tb) -> None:
675+
self.x -= 1
676+
677+
def test_basic() -> None:
678+
context = DummyContext()
679+
with context:
680+
assert context.x == 1
681+
assert context.x == 0

0 commit comments

Comments
 (0)