Skip to content

Commit 1138448

Browse files
Merge branch 'master' into to-bytes
2 parents d2994e1 + 116b92b commit 1138448

File tree

16 files changed

+527
-132
lines changed

16 files changed

+527
-132
lines changed

mypy/checkexpr.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,10 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) ->
582582
and not node.node.no_args
583583
and not (
584584
isinstance(union_target := get_proper_type(node.node.target), UnionType)
585-
and union_target.uses_pep604_syntax
585+
and (
586+
union_target.uses_pep604_syntax
587+
or self.chk.options.python_version >= (3, 10)
588+
)
586589
)
587590
):
588591
self.msg.type_arguments_not_allowed(e)

mypy/checkpattern.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def visit_or_pattern(self, o: OrPattern) -> PatternType:
192192
for capture_list in capture_types.values():
193193
typ = UninhabitedType()
194194
for _, other in capture_list:
195-
typ = join_types(typ, other)
195+
typ = make_simplified_union([typ, other])
196196

197197
captures[capture_list[0][0]] = typ
198198

@@ -539,12 +539,12 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
539539
#
540540
type_info = o.class_ref.node
541541
if type_info is None:
542-
return PatternType(AnyType(TypeOfAny.from_error), AnyType(TypeOfAny.from_error), {})
543-
if isinstance(type_info, TypeAlias) and not type_info.no_args:
542+
typ: Type = AnyType(TypeOfAny.from_error)
543+
elif isinstance(type_info, TypeAlias) and not type_info.no_args:
544544
self.msg.fail(message_registry.CLASS_PATTERN_GENERIC_TYPE_ALIAS, o)
545545
return self.early_non_match()
546-
if isinstance(type_info, TypeInfo):
547-
typ: Type = fill_typevars_with_any(type_info)
546+
elif isinstance(type_info, TypeInfo):
547+
typ = fill_typevars_with_any(type_info)
548548
elif isinstance(type_info, TypeAlias):
549549
typ = type_info.target
550550
elif (

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: 218 additions & 56 deletions
Large diffs are not rendered by default.

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: 148 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
from typing import Any, Callable
1414

1515
import mypy.stubtest
16+
from mypy import build, nodes
17+
from mypy.modulefinder import BuildSource
18+
from mypy.options import Options
1619
from mypy.stubtest import parse_options, test_stubs
20+
from mypy.test.config import test_temp_dir
1721
from mypy.test.data import root_dir
1822

1923

@@ -158,6 +162,14 @@ def __invert__(self: _T) -> _T: pass
158162
"""
159163

160164

165+
def build_helper(source: str) -> build.BuildResult:
166+
return build.build(
167+
sources=[BuildSource("main.pyi", None, textwrap.dedent(source))],
168+
options=Options(),
169+
alt_lib_path=test_temp_dir,
170+
)
171+
172+
161173
def run_stubtest_with_stderr(
162174
stub: str, runtime: str, options: list[str], config_file: str | None = None
163175
) -> tuple[str, str]:
@@ -842,6 +854,18 @@ def f2(self, *a) -> int: ...
842854
""",
843855
error=None,
844856
)
857+
yield Case(
858+
stub="""
859+
@overload
860+
def f(a: int) -> int: ...
861+
@overload
862+
def f(a: int, b: str, /) -> str: ...
863+
""",
864+
runtime="""
865+
def f(a, *args): ...
866+
""",
867+
error=None,
868+
)
845869

846870
@collect_cases
847871
def test_property(self) -> Iterator[Case]:
@@ -1407,14 +1431,9 @@ def spam(x=Flags4(0)): pass
14071431
stub="""
14081432
import sys
14091433
from typing import Final, Literal
1410-
from typing_extensions import disjoint_base
1411-
if sys.version_info >= (3, 12):
1412-
class BytesEnum(bytes, enum.Enum):
1413-
a = b'foo'
1414-
else:
1415-
@disjoint_base
1416-
class BytesEnum(bytes, enum.Enum):
1417-
a = b'foo'
1434+
class BytesEnum(bytes, enum.Enum):
1435+
a = b'foo'
1436+
14181437
FOO: Literal[BytesEnum.a]
14191438
BAR: Final = BytesEnum.a
14201439
BAZ: BytesEnum
@@ -1430,6 +1449,31 @@ class BytesEnum(bytes, enum.Enum):
14301449
""",
14311450
error=None,
14321451
)
1452+
yield Case(
1453+
stub="""
1454+
class HasSlotsAndNothingElse:
1455+
__slots__ = ("x",)
1456+
x: int
1457+
1458+
class HasInheritedSlots(HasSlotsAndNothingElse):
1459+
pass
1460+
1461+
class HasEmptySlots:
1462+
__slots__ = ()
1463+
""",
1464+
runtime="""
1465+
class HasSlotsAndNothingElse:
1466+
__slots__ = ("x",)
1467+
x: int
1468+
1469+
class HasInheritedSlots(HasSlotsAndNothingElse):
1470+
pass
1471+
1472+
class HasEmptySlots:
1473+
__slots__ = ()
1474+
""",
1475+
error=None,
1476+
)
14331477

14341478
@collect_cases
14351479
def test_decorator(self) -> Iterator[Case]:
@@ -1673,6 +1717,53 @@ def __next__(self) -> object: ...
16731717
""",
16741718
error=None,
16751719
)
1720+
yield Case(
1721+
runtime="""
1722+
class IsDisjointBaseBecauseItHasSlots:
1723+
__slots__ = ("a",)
1724+
a: int
1725+
""",
1726+
stub="""
1727+
from typing_extensions import disjoint_base
1728+
1729+
@disjoint_base
1730+
class IsDisjointBaseBecauseItHasSlots:
1731+
a: int
1732+
""",
1733+
error="test_module.IsDisjointBaseBecauseItHasSlots",
1734+
)
1735+
yield Case(
1736+
runtime="""
1737+
class IsFinalSoDisjointBaseIsRedundant: ...
1738+
""",
1739+
stub="""
1740+
from typing_extensions import disjoint_base, final
1741+
1742+
@final
1743+
@disjoint_base
1744+
class IsFinalSoDisjointBaseIsRedundant: ...
1745+
""",
1746+
error="test_module.IsFinalSoDisjointBaseIsRedundant",
1747+
)
1748+
yield Case(
1749+
runtime="""
1750+
import enum
1751+
1752+
class IsEnumWithMembersSoDisjointBaseIsRedundant(enum.Enum):
1753+
A = 1
1754+
B = 2
1755+
""",
1756+
stub="""
1757+
from typing_extensions import disjoint_base
1758+
import enum
1759+
1760+
@disjoint_base
1761+
class IsEnumWithMembersSoDisjointBaseIsRedundant(enum.Enum):
1762+
A = 1
1763+
B = 2
1764+
""",
1765+
error="test_module.IsEnumWithMembersSoDisjointBaseIsRedundant",
1766+
)
16761767

16771768
@collect_cases
16781769
def test_has_runtime_final_decorator(self) -> Iterator[Case]:
@@ -2545,6 +2636,31 @@ class _X1: ...
25452636
error=None,
25462637
)
25472638

2639+
@collect_cases
2640+
def test_type_default_protocol(self) -> Iterator[Case]:
2641+
yield Case(
2642+
stub="""
2643+
from typing import Protocol
2644+
2645+
class _FormatterClass(Protocol):
2646+
def __call__(self, *, prog: str) -> HelpFormatter: ...
2647+
2648+
class ArgumentParser:
2649+
def __init__(self, formatter_class: _FormatterClass = ...) -> None: ...
2650+
2651+
class HelpFormatter:
2652+
def __init__(self, prog: str, indent_increment: int = 2) -> None: ...
2653+
""",
2654+
runtime="""
2655+
class HelpFormatter:
2656+
def __init__(self, prog, indent_increment=2) -> None: ...
2657+
2658+
class ArgumentParser:
2659+
def __init__(self, formatter_class=HelpFormatter): ...
2660+
""",
2661+
error=None,
2662+
)
2663+
25482664

25492665
def remove_color_code(s: str) -> str:
25502666
return re.sub("\\x1b.*?m", "", s) # this works!
@@ -2558,8 +2674,8 @@ def test_output(self) -> None:
25582674
options=[],
25592675
)
25602676
expected = (
2561-
f'error: {TEST_MODULE_NAME}.bad is inconsistent, stub argument "number" differs '
2562-
'from runtime argument "num"\n'
2677+
f'error: {TEST_MODULE_NAME}.bad is inconsistent, stub parameter "number" differs '
2678+
'from runtime parameter "num"\n'
25632679
f"Stub: in file {TEST_MODULE_NAME}.pyi:1\n"
25642680
"def (number: builtins.int, text: builtins.str)\n"
25652681
f"Runtime: in file {TEST_MODULE_NAME}.py:1\ndef (num, text)\n\n"
@@ -2574,7 +2690,9 @@ def test_output(self) -> None:
25742690
)
25752691
expected = (
25762692
"{}.bad is inconsistent, "
2577-
'stub argument "number" differs from runtime argument "num"\n'.format(TEST_MODULE_NAME)
2693+
'stub parameter "number" differs from runtime parameter "num"\n'.format(
2694+
TEST_MODULE_NAME
2695+
)
25782696
)
25792697
assert output == expected
25802698

@@ -2721,6 +2839,25 @@ def test_builtin_signature_with_unrepresentable_default(self) -> None:
27212839
== "def (self, sep = ..., bytes_per_sep = ...)"
27222840
)
27232841

2842+
def test_overload_signature(self) -> None:
2843+
# The same argument as both positional-only and pos-or-kw in
2844+
# different overloads previously produced incorrect signatures
2845+
source = """
2846+
from typing import overload
2847+
@overload
2848+
def myfunction(arg: int) -> None: ...
2849+
@overload
2850+
def myfunction(arg: str, /) -> None: ...
2851+
"""
2852+
result = build_helper(source)
2853+
stub = result.files["__main__"].names["myfunction"].node
2854+
assert isinstance(stub, nodes.OverloadedFuncDef)
2855+
sig = mypy.stubtest.Signature.from_overloadedfuncdef(stub)
2856+
if sys.version_info >= (3, 10):
2857+
assert str(sig) == "def (arg: builtins.int | builtins.str)"
2858+
else:
2859+
assert str(sig) == "def (arg: Union[builtins.int, builtins.str])"
2860+
27242861
def test_config_file(self) -> None:
27252862
runtime = "temp = 5\n"
27262863
stub = "from decimal import Decimal\ntemp: Decimal\n"

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,
@@ -806,19 +808,40 @@ def _construct_varargs(
806808
return value, self._create_dict([], [], line)
807809
elif len(args) == 2 and args[1][1] == ARG_STAR2:
808810
# fn(*args, **kwargs)
811+
# TODO: extend to cover(*args, **k, **w, **a, **r, **g, **s)
809812
if is_tuple_rprimitive(value.type) or isinstance(value.type, RTuple):
810813
star_result = value
811814
elif is_list_rprimitive(value.type):
812815
star_result = self.primitive_op(list_tuple_op, [value], line)
813816
else:
814817
star_result = self.primitive_op(sequence_tuple_op, [value], line)
815-
continue
818+
819+
star2_arg = args[1]
820+
star2_value = star2_arg[0]
821+
if is_dict_rprimitive(star2_value.type):
822+
star2_fastpath_op = dict_copy_op
823+
else:
824+
star2_fastpath_op = dict_copy
825+
return star_result, self.primitive_op(
826+
star2_fastpath_op, [star2_value], line
827+
)
816828
# elif ...: TODO extend this to optimize fn(*args, k=1, **kwargs) case
817829
# TODO optimize this case using the length utils - currently in review
818830
star_result = self.new_list_op(star_values, line)
819831
self.primitive_op(list_extend_op, [star_result, value], line)
820832
elif kind == ARG_STAR2:
821833
if star2_result is None:
834+
if len(args) == 1:
835+
# early exit with fastpath if the only arg is ARG_STAR2
836+
# TODO: can we maintain an empty tuple in memory and just reuse it again and again?
837+
if is_dict_rprimitive(value.type):
838+
star2_fastpath_op = dict_copy_op
839+
else:
840+
star2_fastpath_op = dict_copy
841+
return self.new_tuple([], line), self.primitive_op(
842+
star2_fastpath_op, [value], line
843+
)
844+
822845
star2_result = self._create_dict(star2_keys, star2_values, line)
823846

824847
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,

0 commit comments

Comments
 (0)