Skip to content

Commit 3fbf62e

Browse files
Merge branch 'master' into PyObject_CallObject
2 parents 55fb87f + 13fa6c3 commit 3fbf62e

File tree

11 files changed

+186
-52
lines changed

11 files changed

+186
-52
lines changed

mypy/fastparse.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ def ast3_parse(
188188
ast_TypeVar = Any
189189
ast_TypeVarTuple = Any
190190

191+
if sys.version_info >= (3, 14):
192+
ast_TemplateStr = ast3.TemplateStr
193+
ast_Interpolation = ast3.Interpolation
194+
else:
195+
ast_TemplateStr = Any
196+
ast_Interpolation = Any
197+
191198
N = TypeVar("N", bound=Node)
192199

193200
# There is no way to create reasonable fallbacks at this stage,
@@ -1705,6 +1712,21 @@ def visit_FormattedValue(self, n: ast3.FormattedValue) -> Expression:
17051712
)
17061713
return self.set_line(result_expression, n)
17071714

1715+
# TemplateStr(expr* values)
1716+
def visit_TemplateStr(self, n: ast_TemplateStr) -> Expression:
1717+
self.fail(
1718+
ErrorMessage("PEP 750 template strings are not yet supported"),
1719+
n.lineno,
1720+
n.col_offset,
1721+
blocker=False,
1722+
)
1723+
e = TempNode(AnyType(TypeOfAny.from_error))
1724+
return self.set_line(e, n)
1725+
1726+
# Interpolation(expr value, constant str, int conversion, expr? format_spec)
1727+
def visit_Interpolation(self, n: ast_Interpolation) -> Expression:
1728+
assert False, "Unreachable"
1729+
17081730
# Attribute(expr value, identifier attr, expr_context ctx)
17091731
def visit_Attribute(self, n: Attribute) -> MemberExpr | SuperExpr:
17101732
value = n.value

mypy/stubtest.py

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,9 @@ def _verify_disjoint_base(
510510
if stub.is_final:
511511
return
512512
is_disjoint_runtime = _is_disjoint_base(runtime)
513-
if is_disjoint_runtime and not stub.is_disjoint_base:
513+
# Don't complain about missing @disjoint_base if there are __slots__, because
514+
# in that case we can infer that it's a disjoint base.
515+
if is_disjoint_runtime and not stub.is_disjoint_base and not runtime.__dict__.get("__slots__"):
514516
yield Error(
515517
object_path,
516518
"is a disjoint base at runtime, but isn't marked with @disjoint_base in the stub",
@@ -936,6 +938,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
936938
or arg.pos_only
937939
or assume_positional_only
938940
or arg.variable.name.strip("_") == "self"
941+
or (index == 0 and arg.variable.name.strip("_") == "cls")
939942
else arg.variable.name
940943
)
941944
all_args.setdefault(name, []).append((arg, index))
@@ -1006,6 +1009,7 @@ def _verify_signature(
10061009
and not stub_arg.pos_only
10071010
and not stub_arg.variable.name.startswith("__")
10081011
and stub_arg.variable.name.strip("_") != "self"
1012+
and stub_arg.variable.name.strip("_") != "cls"
10091013
and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods
10101014
):
10111015
yield (
@@ -1017,6 +1021,7 @@ def _verify_signature(
10171021
and (stub_arg.pos_only or stub_arg.variable.name.startswith("__"))
10181022
and not runtime_arg.name.startswith("__")
10191023
and stub_arg.variable.name.strip("_") != "self"
1024+
and stub_arg.variable.name.strip("_") != "cls"
10201025
and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods
10211026
):
10221027
yield (
@@ -1660,6 +1665,71 @@ def is_read_only_property(runtime: object) -> bool:
16601665

16611666

16621667
def safe_inspect_signature(runtime: Any) -> inspect.Signature | None:
1668+
if (
1669+
hasattr(runtime, "__name__")
1670+
and runtime.__name__ == "__init__"
1671+
and hasattr(runtime, "__text_signature__")
1672+
and runtime.__text_signature__ == "($self, /, *args, **kwargs)"
1673+
and hasattr(runtime, "__objclass__")
1674+
and hasattr(runtime.__objclass__, "__text_signature__")
1675+
and runtime.__objclass__.__text_signature__ is not None
1676+
):
1677+
# This is an __init__ method with the generic C-class signature.
1678+
# In this case, the underlying class often has a better signature,
1679+
# which we can convert into an __init__ signature by adding in the
1680+
# self parameter.
1681+
try:
1682+
s = inspect.signature(runtime.__objclass__)
1683+
1684+
parameter_kind: inspect._ParameterKind = inspect.Parameter.POSITIONAL_OR_KEYWORD
1685+
if s.parameters:
1686+
first_parameter = next(iter(s.parameters.values()))
1687+
if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
1688+
parameter_kind = inspect.Parameter.POSITIONAL_ONLY
1689+
return s.replace(
1690+
parameters=[inspect.Parameter("self", parameter_kind), *s.parameters.values()]
1691+
)
1692+
except Exception:
1693+
pass
1694+
1695+
if (
1696+
hasattr(runtime, "__name__")
1697+
and runtime.__name__ == "__new__"
1698+
and hasattr(runtime, "__text_signature__")
1699+
and runtime.__text_signature__ == "($type, *args, **kwargs)"
1700+
and hasattr(runtime, "__self__")
1701+
and hasattr(runtime.__self__, "__text_signature__")
1702+
and runtime.__self__.__text_signature__ is not None
1703+
):
1704+
# This is a __new__ method with the generic C-class signature.
1705+
# In this case, the underlying class often has a better signature,
1706+
# which we can convert into a __new__ signature by adding in the
1707+
# cls parameter.
1708+
1709+
# If the attached class has a valid __init__, skip recovering a
1710+
# signature for this __new__ method.
1711+
has_init = False
1712+
if (
1713+
hasattr(runtime.__self__, "__init__")
1714+
and hasattr(runtime.__self__.__init__, "__objclass__")
1715+
and runtime.__self__.__init__.__objclass__ is runtime.__self__
1716+
):
1717+
has_init = True
1718+
1719+
if not has_init:
1720+
try:
1721+
s = inspect.signature(runtime.__self__)
1722+
parameter_kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
1723+
if s.parameters:
1724+
first_parameter = next(iter(s.parameters.values()))
1725+
if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
1726+
parameter_kind = inspect.Parameter.POSITIONAL_ONLY
1727+
return s.replace(
1728+
parameters=[inspect.Parameter("cls", parameter_kind), *s.parameters.values()]
1729+
)
1730+
except Exception:
1731+
pass
1732+
16631733
try:
16641734
try:
16651735
return inspect.signature(runtime)

mypy/test/testcheck.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
typecheck_files.remove("check-python312.test")
4848
if sys.version_info < (3, 13):
4949
typecheck_files.remove("check-python313.test")
50+
if sys.version_info < (3, 14):
51+
typecheck_files.remove("check-python314.test")
5052

5153

5254
class TypeCheckSuite(DataSuite):

mypy/test/testparse.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class ParserSuite(DataSuite):
2727
files.remove("parse-python312.test")
2828
if sys.version_info < (3, 13):
2929
files.remove("parse-python313.test")
30+
if sys.version_info < (3, 14):
31+
files.remove("parse-python314.test")
3032

3133
def run_case(self, testcase: DataDrivenTestCase) -> None:
3234
test_parser(testcase)
@@ -46,6 +48,8 @@ def test_parser(testcase: DataDrivenTestCase) -> None:
4648
options.python_version = (3, 12)
4749
elif testcase.file.endswith("python313.test"):
4850
options.python_version = (3, 13)
51+
elif testcase.file.endswith("python314.test"):
52+
options.python_version = (3, 14)
4953
else:
5054
options.python_version = defaults.PYTHON3_VERSION
5155

mypy/test/teststubtest.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,31 @@ class BytesEnum(bytes, enum.Enum):
14301430
""",
14311431
error=None,
14321432
)
1433+
yield Case(
1434+
stub="""
1435+
class HasSlotsAndNothingElse:
1436+
__slots__ = ("x",)
1437+
x: int
1438+
1439+
class HasInheritedSlots(HasSlotsAndNothingElse):
1440+
pass
1441+
1442+
class HasEmptySlots:
1443+
__slots__ = ()
1444+
""",
1445+
runtime="""
1446+
class HasSlotsAndNothingElse:
1447+
__slots__ = ("x",)
1448+
x: int
1449+
1450+
class HasInheritedSlots(HasSlotsAndNothingElse):
1451+
pass
1452+
1453+
class HasEmptySlots:
1454+
__slots__ = ()
1455+
""",
1456+
error=None,
1457+
)
14331458

14341459
@collect_cases
14351460
def test_decorator(self) -> Iterator[Case]:

mypyc/irbuild/ll_builder.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@
128128
from mypyc.primitives.bytes_ops import bytes_compare
129129
from mypyc.primitives.dict_ops import (
130130
dict_build_op,
131+
dict_copy,
132+
dict_copy_op,
131133
dict_new_op,
132134
dict_ssize_t_size_op,
133135
dict_update_in_display_op,
@@ -807,19 +809,40 @@ def _construct_varargs(
807809
return value, None
808810
elif len(args) == 2 and args[1][1] == ARG_STAR2:
809811
# fn(*args, **kwargs)
812+
# TODO: extend to cover(*args, **k, **w, **a, **r, **g, **s)
810813
if is_tuple_rprimitive(value.type) or isinstance(value.type, RTuple):
811814
star_result = value
812815
elif is_list_rprimitive(value.type):
813816
star_result = self.primitive_op(list_tuple_op, [value], line)
814817
else:
815818
star_result = self.primitive_op(sequence_tuple_op, [value], line)
816-
continue
819+
820+
star2_arg = args[1]
821+
star2_value = star2_arg[0]
822+
if is_dict_rprimitive(star2_value.type):
823+
star2_fastpath_op = dict_copy_op
824+
else:
825+
star2_fastpath_op = dict_copy
826+
return star_result, self.primitive_op(
827+
star2_fastpath_op, [star2_value], line
828+
)
817829
# elif ...: TODO extend this to optimize fn(*args, k=1, **kwargs) case
818830
# TODO optimize this case using the length utils - currently in review
819831
star_result = self.new_list_op(star_values, line)
820832
self.primitive_op(list_extend_op, [star_result, value], line)
821833
elif kind == ARG_STAR2:
822834
if star2_result is None:
835+
if len(args) == 1:
836+
# early exit with fastpath if the only arg is ARG_STAR2
837+
# TODO: can we maintain an empty tuple in memory and just reuse it again and again?
838+
if is_dict_rprimitive(value.type):
839+
star2_fastpath_op = dict_copy_op
840+
else:
841+
star2_fastpath_op = dict_copy
842+
return self.new_tuple([], line), self.primitive_op(
843+
star2_fastpath_op, [value], line
844+
)
845+
823846
star2_result = self._create_dict(star2_keys, star2_values, line)
824847

825848
self.call_c(dict_update_in_display_op, [star2_result, value], line=line)

mypyc/primitives/dict_ops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
)
5454

5555
# Construct a dictionary from another dictionary.
56-
function_op(
56+
dict_copy_op = function_op(
5757
name="builtins.dict",
5858
arg_types=[dict_rprimitive],
5959
return_type=dict_rprimitive,

mypyc/test-data/irbuild-basic.test

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,12 +1735,10 @@ def g():
17351735
r6, r7 :: dict
17361736
r8 :: str
17371737
r9 :: object
1738-
r10 :: dict
1739-
r11 :: i32
1740-
r12 :: bit
1741-
r13 :: tuple
1742-
r14 :: object
1743-
r15 :: tuple[int, int, int]
1738+
r10 :: tuple
1739+
r11 :: dict
1740+
r12 :: object
1741+
r13 :: tuple[int, int, int]
17441742
L0:
17451743
r0 = 'a'
17461744
r1 = 'b'
@@ -1752,13 +1750,11 @@ L0:
17521750
r7 = __main__.globals :: static
17531751
r8 = 'f'
17541752
r9 = CPyDict_GetItem(r7, r8)
1755-
r10 = PyDict_New()
1756-
r11 = CPyDict_UpdateInDisplay(r10, r6)
1757-
r12 = r11 >= 0 :: signed
1758-
r13 = PyTuple_Pack(0)
1759-
r14 = PyObject_Call(r9, r13, r10)
1760-
r15 = unbox(tuple[int, int, int], r14)
1761-
return r15
1753+
r10 = PyTuple_Pack(0)
1754+
r11 = PyDict_Copy(r6)
1755+
r12 = PyObject_Call(r9, r10, r11)
1756+
r13 = unbox(tuple[int, int, int], r12)
1757+
return r13
17621758
def h():
17631759
r0, r1 :: str
17641760
r2, r3 :: object
@@ -3660,18 +3656,14 @@ def wrapper_deco_obj.__call__(__mypyc_self__, lst, kwargs):
36603656
r1 :: object
36613657
r2 :: tuple
36623658
r3 :: dict
3663-
r4 :: i32
3664-
r5 :: bit
3665-
r6 :: object
3659+
r4 :: object
36663660
L0:
36673661
r0 = __mypyc_self__.__mypyc_env__
36683662
r1 = r0.fn
36693663
r2 = PyList_AsTuple(lst)
3670-
r3 = PyDict_New()
3671-
r4 = CPyDict_UpdateInDisplay(r3, kwargs)
3672-
r5 = r4 >= 0 :: signed
3673-
r6 = PyObject_Call(r1, r2, r3)
3674-
return r6
3664+
r3 = PyDict_Copy(kwargs)
3665+
r4 = PyObject_Call(r1, r2, r3)
3666+
return r4
36753667
def deco(fn):
36763668
fn :: object
36773669
r0 :: __main__.deco_env
@@ -3765,18 +3757,14 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args, kwargs):
37653757
r1 :: object
37663758
r2 :: tuple
37673759
r3 :: dict
3768-
r4 :: i32
3769-
r5 :: bit
3770-
r6 :: object
3760+
r4 :: object
37713761
L0:
37723762
r0 = __mypyc_self__.__mypyc_env__
37733763
r1 = r0.fn
37743764
r2 = PySequence_Tuple(args)
3775-
r3 = PyDict_New()
3776-
r4 = CPyDict_UpdateInDisplay(r3, kwargs)
3777-
r5 = r4 >= 0 :: signed
3778-
r6 = PyObject_Call(r1, r2, r3)
3779-
return r6
3765+
r3 = PyDict_Copy(kwargs)
3766+
r4 = PyObject_Call(r1, r2, r3)
3767+
return r4
37803768
def deco(fn):
37813769
fn :: object
37823770
r0 :: __main__.deco_env

mypyc/test-data/irbuild-generics.test

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -167,17 +167,13 @@ def execute(func, args, kwargs):
167167
func :: object
168168
args :: tuple
169169
kwargs, r0 :: dict
170-
r1 :: i32
171-
r2 :: bit
172-
r3 :: object
173-
r4 :: int
170+
r1 :: object
171+
r2 :: int
174172
L0:
175-
r0 = PyDict_New()
176-
r1 = CPyDict_UpdateInDisplay(r0, kwargs)
177-
r2 = r1 >= 0 :: signed
178-
r3 = PyObject_Call(func, args, r0)
179-
r4 = unbox(int, r3)
180-
return r4
173+
r0 = PyDict_Copy(kwargs)
174+
r1 = PyObject_Call(func, args, r0)
175+
r2 = unbox(int, r1)
176+
return r2
181177
def f(x):
182178
x :: int
183179
L0:
@@ -703,10 +699,8 @@ def inner_deco_obj.__call__(__mypyc_self__, args, kwargs):
703699
r22, can_iter, r23, can_use_keys, r24, can_use_values :: list
704700
r25 :: object
705701
r26 :: dict
706-
r27 :: i32
707-
r28 :: bit
708-
r29 :: object
709-
r30 :: int
702+
r27 :: object
703+
r28 :: int
710704
L0:
711705
r0 = __mypyc_self__.__mypyc_env__
712706
r1 = var_object_size args
@@ -758,12 +752,10 @@ L9:
758752
r24 = CPyDict_Values(kwargs)
759753
can_use_values = r24
760754
r25 = r0.func
761-
r26 = PyDict_New()
762-
r27 = CPyDict_UpdateInDisplay(r26, kwargs)
763-
r28 = r27 >= 0 :: signed
764-
r29 = PyObject_Call(r25, args, r26)
765-
r30 = unbox(int, r29)
766-
return r30
755+
r26 = PyDict_Copy(kwargs)
756+
r27 = PyObject_Call(r25, args, r26)
757+
r28 = unbox(int, r27)
758+
return r28
767759
def deco(func):
768760
func :: object
769761
r0 :: __main__.deco_env

0 commit comments

Comments
 (0)