Skip to content

Commit 4c2e98b

Browse files
committed
Merge remote-tracking branch 'upstream/master' into conditional-overloads
2 parents b36a5f8 + 61f0543 commit 4c2e98b

Some content is hidden

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

45 files changed

+209
-253
lines changed

misc/cherry-pick-typeshed.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""Cherry-pick a commit from typeshed.
2+
3+
Usage:
4+
5+
python3 misc/cherry-pick-typeshed.py --typeshed-dir dir hash
6+
"""
7+
8+
import argparse
9+
import os.path
10+
import re
11+
import subprocess
12+
import sys
13+
import tempfile
14+
15+
16+
def parse_commit_title(diff: str) -> str:
17+
m = re.search("\n ([^ ].*)", diff)
18+
assert m is not None, "Could not parse diff"
19+
return m.group(1)
20+
21+
22+
def main() -> None:
23+
parser = argparse.ArgumentParser()
24+
parser.add_argument(
25+
"--typeshed-dir", help="location of typeshed", metavar="dir", required=True
26+
)
27+
parser.add_argument(
28+
"commit", help="typeshed commit hash to cherry-pick"
29+
)
30+
args = parser.parse_args()
31+
typeshed_dir = args.typeshed_dir
32+
commit = args.commit
33+
34+
if not os.path.isdir(typeshed_dir):
35+
sys.exit(f"error: {typeshed_dir} does not exist")
36+
if not re.match("[0-9a-fA-F]+$", commit):
37+
sys.exit(f"error: Invalid commit {commit!r}")
38+
39+
if not os.path.exists("mypy") or not os.path.exists("mypyc"):
40+
sys.exit(f"error: This script must be run at the mypy repository root directory")
41+
42+
with tempfile.TemporaryDirectory() as d:
43+
diff_file = os.path.join(d, "diff")
44+
out = subprocess.run(["git", "show", commit],
45+
capture_output=True,
46+
text=True,
47+
check=True,
48+
cwd=typeshed_dir)
49+
with open(diff_file, "w") as f:
50+
f.write(out.stdout)
51+
subprocess.run(["git",
52+
"apply",
53+
"--index",
54+
"--directory=mypy/typeshed",
55+
"--exclude=**/tests/**",
56+
diff_file],
57+
check=True)
58+
59+
title = parse_commit_title(out.stdout)
60+
subprocess.run(["git", "commit", "-m", f"Typeshed cherry-pick: {title}"], check=True)
61+
62+
print()
63+
print(f"Cherry-picked commit {commit} from {typeshed_dir}")
64+
65+
66+
if __name__ == '__main__':
67+
main()

mypy/build.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,11 +2188,8 @@ def type_checker(self) -> TypeChecker:
21882188
if not self._type_checker:
21892189
assert self.tree is not None, "Internal error: must be called on parsed file only"
21902190
manager = self.manager
2191-
self._type_checker = TypeChecker(
2192-
manager.errors, manager.modules, self.options,
2193-
self.tree, self.xpath, manager.plugin,
2194-
self.manager.semantic_analyzer.future_import_flags,
2195-
)
2191+
self._type_checker = TypeChecker(manager.errors, manager.modules, self.options,
2192+
self.tree, self.xpath, manager.plugin)
21962193
return self._type_checker
21972194

21982195
def type_map(self) -> Dict[Expression, Type]:

mypy/checker.py

Lines changed: 13 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,8 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
221221
# functions such as open(), etc.
222222
plugin: Plugin
223223

224-
# Future flags that we get from semantic analyzer.
225-
future_import_flags: Set[str]
226-
227224
def __init__(self, errors: Errors, modules: Dict[str, MypyFile], options: Options,
228-
tree: MypyFile, path: str, plugin: Plugin,
229-
future_import_flags: Set[str]) -> None:
225+
tree: MypyFile, path: str, plugin: Plugin) -> None:
230226
"""Construct a type checker.
231227
232228
Use errors to report type check errors.
@@ -272,8 +268,6 @@ def __init__(self, errors: Errors, modules: Dict[str, MypyFile], options: Option
272268
# argument through various `checker` and `checkmember` functions.
273269
self._is_final_def = False
274270

275-
self.future_import_flags = future_import_flags
276-
277271
@property
278272
def type_context(self) -> List[Optional[Type]]:
279273
return self.expr_checker.type_context
@@ -3492,7 +3486,7 @@ def visit_raise_stmt(self, s: RaiseStmt) -> None:
34923486
if s.expr:
34933487
self.type_check_raise(s.expr, s)
34943488
if s.from_expr:
3495-
self.type_check_raise(s.from_expr, s, optional=True)
3489+
self.type_check_raise(s.from_expr, s, True)
34963490
self.binder.unreachable()
34973491

34983492
def type_check_raise(self, e: Expression, s: RaiseStmt,
@@ -3501,88 +3495,24 @@ def type_check_raise(self, e: Expression, s: RaiseStmt,
35013495
if isinstance(typ, DeletedType):
35023496
self.msg.deleted_as_rvalue(typ, e)
35033497
return
3504-
3505-
if self.options.python_version[0] == 2:
3506-
# Since `raise` has very different rule on python2, we use a different helper.
3507-
# https://github.com/python/mypy/pull/11289
3508-
self._type_check_raise_python2(e, s, typ)
3509-
return
3510-
3511-
# Python3 case:
35123498
exc_type = self.named_type('builtins.BaseException')
3513-
expected_type_items = [exc_type, TypeType(exc_type)]
3499+
expected_type = UnionType([exc_type, TypeType(exc_type)])
35143500
if optional:
3515-
# This is used for `x` part in a case like `raise e from x`,
3516-
# where we allow `raise e from None`.
3517-
expected_type_items.append(NoneType())
3518-
3519-
self.check_subtype(
3520-
typ, UnionType.make_union(expected_type_items), s,
3521-
message_registry.INVALID_EXCEPTION,
3522-
)
3501+
expected_type.items.append(NoneType())
3502+
if self.options.python_version[0] == 2:
3503+
# allow `raise type, value, traceback`
3504+
# https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement
3505+
# TODO: Also check tuple item types.
3506+
any_type = AnyType(TypeOfAny.implementation_artifact)
3507+
tuple_type = self.named_type('builtins.tuple')
3508+
expected_type.items.append(TupleType([any_type, any_type], tuple_type))
3509+
expected_type.items.append(TupleType([any_type, any_type, any_type], tuple_type))
3510+
self.check_subtype(typ, expected_type, s, message_registry.INVALID_EXCEPTION)
35233511

35243512
if isinstance(typ, FunctionLike):
35253513
# https://github.com/python/mypy/issues/11089
35263514
self.expr_checker.check_call(typ, [], [], e)
35273515

3528-
def _type_check_raise_python2(self, e: Expression, s: RaiseStmt, typ: ProperType) -> None:
3529-
# Python2 has two possible major cases:
3530-
# 1. `raise expr`, where `expr` is some expression, it can be:
3531-
# - Exception typ
3532-
# - Exception instance
3533-
# - Old style class (not supported)
3534-
# - Tuple, where 0th item is exception type or instance
3535-
# 2. `raise exc, msg, traceback`, where:
3536-
# - `exc` is exception type (not instance!)
3537-
# - `traceback` is `types.TracebackType | None`
3538-
# Important note: `raise exc, msg` is not the same as `raise (exc, msg)`
3539-
# We call `raise exc, msg, traceback` - legacy mode.
3540-
exc_type = self.named_type('builtins.BaseException')
3541-
3542-
if (not s.legacy_mode and (isinstance(typ, TupleType) and typ.items
3543-
or (isinstance(typ, Instance) and typ.args
3544-
and typ.type.fullname == 'builtins.tuple'))):
3545-
# `raise (exc, ...)` case:
3546-
item = typ.items[0] if isinstance(typ, TupleType) else typ.args[0]
3547-
self.check_subtype(
3548-
item, UnionType([exc_type, TypeType(exc_type)]), s,
3549-
'When raising a tuple, first element must by derived from BaseException',
3550-
)
3551-
return
3552-
elif s.legacy_mode:
3553-
# `raise Exception, msg` case
3554-
# `raise Exception, msg, traceback` case
3555-
# https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement
3556-
assert isinstance(typ, TupleType) # Is set in fastparse2.py
3557-
self.check_subtype(
3558-
typ.items[0], TypeType(exc_type), s,
3559-
'First argument must be BaseException subtype',
3560-
)
3561-
3562-
# Typecheck `traceback` part:
3563-
if len(typ.items) == 3:
3564-
# Now, we typecheck `traceback` argument if it is present.
3565-
# We do this after the main check for better error message
3566-
# and better ordering: first about `BaseException` subtype,
3567-
# then about `traceback` type.
3568-
traceback_type = UnionType.make_union([
3569-
self.named_type('types.TracebackType'),
3570-
NoneType(),
3571-
])
3572-
self.check_subtype(
3573-
typ.items[2], traceback_type, s,
3574-
'Third argument to raise must have "{}" type'.format(traceback_type),
3575-
)
3576-
else:
3577-
expected_type_items = [
3578-
# `raise Exception` and `raise Exception()` cases:
3579-
exc_type, TypeType(exc_type),
3580-
]
3581-
self.check_subtype(
3582-
typ, UnionType.make_union(expected_type_items),
3583-
s, message_registry.INVALID_EXCEPTION,
3584-
)
3585-
35863516
def visit_try_stmt(self, s: TryStmt) -> None:
35873517
"""Type check a try statement."""
35883518
# Our enclosing frame will get the result if the try/except falls through.

mypy/checkexpr.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2414,7 +2414,8 @@ def dangerous_comparison(self, left: Type, right: Type,
24142414

24152415
def get_operator_method(self, op: str) -> str:
24162416
if op == '/' and self.chk.options.python_version[0] == 2:
2417-
return '__truediv__' if 'division' in self.chk.future_import_flags else '__div__'
2417+
# TODO also check for "from __future__ import division"
2418+
return '__div__'
24182419
else:
24192420
return operators.op_methods[op]
24202421

mypy/fastparse.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,12 @@ def visit_Index(self, n: Index) -> Node:
14451445
# cast for mypyc's benefit on Python 3.9
14461446
return self.visit(cast(Any, n).value)
14471447

1448+
def visit_Match(self, n: Any) -> Node:
1449+
self.fail("Match statement is not supported",
1450+
line=n.lineno, column=n.col_offset, blocker=True)
1451+
# Just return some valid node
1452+
return PassStmt()
1453+
14481454

14491455
class TypeConverter:
14501456
def __init__(self,

mypy/fastparse2.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -664,23 +664,19 @@ def visit_With(self, n: ast27.With) -> WithStmt:
664664
typ)
665665
return self.set_line(stmt, n)
666666

667-
# 'raise' [test [',' test [',' test]]]
668667
def visit_Raise(self, n: ast27.Raise) -> RaiseStmt:
669-
legacy_mode = False
670668
if n.type is None:
671669
e = None
672670
else:
673671
if n.inst is None:
674672
e = self.visit(n.type)
675673
else:
676-
legacy_mode = True
677674
if n.tback is None:
678675
e = TupleExpr([self.visit(n.type), self.visit(n.inst)])
679676
else:
680677
e = TupleExpr([self.visit(n.type), self.visit(n.inst), self.visit(n.tback)])
681678

682679
stmt = RaiseStmt(e, None)
683-
stmt.legacy_mode = legacy_mode
684680
return self.set_line(stmt, n)
685681

686682
# TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)

mypy/nodes.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,19 +1294,16 @@ def accept(self, visitor: StatementVisitor[T]) -> T:
12941294

12951295

12961296
class RaiseStmt(Statement):
1297-
__slots__ = ('expr', 'from_expr', 'legacy_mode')
1297+
__slots__ = ('expr', 'from_expr')
12981298

12991299
# Plain 'raise' is a valid statement.
13001300
expr: Optional[Expression]
13011301
from_expr: Optional[Expression]
1302-
# Is set when python2 has `raise exc, msg, traceback`.
1303-
legacy_mode: bool
13041302

13051303
def __init__(self, expr: Optional[Expression], from_expr: Optional[Expression]) -> None:
13061304
super().__init__()
13071305
self.expr = expr
13081306
self.from_expr = from_expr
1309-
self.legacy_mode = False
13101307

13111308
def accept(self, visitor: StatementVisitor[T]) -> T:
13121309
return visitor.visit_raise_stmt(self)

mypy/test/data.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,13 @@ def split_test_cases(parent: 'DataFileCollector', suite: 'DataSuite',
578578
data,
579579
flags=re.DOTALL | re.MULTILINE)
580580
line_no = cases[0].count('\n') + 1
581+
test_names = set()
581582
for i in range(1, len(cases), NUM_GROUPS):
582583
name, writescache, only_when, platform_flag, skip, xfail, data = cases[i:i + NUM_GROUPS]
584+
if name in test_names:
585+
raise RuntimeError('Found a duplicate test name "{}" in {} on line {}'.format(
586+
name, parent.name, line_no,
587+
))
583588
platform = platform_flag[1:] if platform_flag else None
584589
yield DataDrivenTestCase.from_parent(
585590
parent=parent,
@@ -596,6 +601,9 @@ def split_test_cases(parent: 'DataFileCollector', suite: 'DataSuite',
596601
)
597602
line_no += data.count('\n') + 1
598603

604+
# Record existing tests to prevent duplicates:
605+
test_names.update({name})
606+
599607

600608
class DataSuiteCollector(pytest.Class):
601609
def collect(self) -> Iterator['DataFileCollector']:

mypy/test/testcheck.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@
104104
typecheck_files.append('check-python38.test')
105105
if sys.version_info >= (3, 9):
106106
typecheck_files.append('check-python39.test')
107+
if sys.version_info >= (3, 10):
108+
typecheck_files.append('check-python310.test')
107109

108110
# Special tests for platforms with case-insensitive filesystems.
109111
if sys.platform in ('darwin', 'win32'):

mypy/typeshed/stdlib/_typeshed/__init__.pyi

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,26 @@ class SupportsGreaterThan(Protocol):
4545

4646
SupportsGreaterThanT = TypeVar("SupportsGreaterThanT", bound=SupportsGreaterThan) # noqa: Y001
4747

48+
# Comparison protocols
49+
50+
class SupportsDunderLT(Protocol):
51+
def __lt__(self, __other: Any) -> Any: ...
52+
53+
class SupportsDunderGT(Protocol):
54+
def __gt__(self, __other: Any) -> Any: ...
55+
56+
class SupportsDunderLE(Protocol):
57+
def __le__(self, __other: Any) -> Any: ...
58+
59+
class SupportsDunderGE(Protocol):
60+
def __ge__(self, __other: Any) -> Any: ...
61+
62+
class SupportsAllComparisons(SupportsDunderLT, SupportsDunderGT, SupportsDunderLE, SupportsDunderGE, Protocol): ...
63+
64+
SupportsRichComparison = Union[SupportsDunderLT, SupportsDunderGT]
65+
SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison) # noqa: Y001
66+
SupportsAnyComparison = Union[SupportsDunderLE, SupportsDunderGE, SupportsDunderGT, SupportsDunderLT]
67+
4868
class SupportsDivMod(Protocol[_T_contra, _T_co]):
4969
def __divmod__(self, __other: _T_contra) -> _T_co: ...
5070

0 commit comments

Comments
 (0)