Skip to content

Commit 57799f1

Browse files
committed
Merge remote-tracking branch 'upstream/master' into bugfix/pythongh-18862-typevar-default-alias
2 parents a871b87 + 454989f commit 57799f1

39 files changed

+680
-62
lines changed

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ repos:
1111
- id: black
1212
exclude: '^(test-data/)'
1313
- repo: https://github.com/astral-sh/ruff-pre-commit
14-
rev: v0.9.10
14+
rev: v0.11.4
1515
hooks:
1616
- id: ruff
1717
args: [--exit-non-zero-on-fix]
1818
- repo: https://github.com/python-jsonschema/check-jsonschema
19-
rev: 0.31.0
19+
rev: 0.32.1
2020
hooks:
2121
- id: check-github-workflows
2222
- id: check-github-actions
@@ -43,7 +43,7 @@ repos:
4343
# but the integration only works if shellcheck is installed
4444
- "github.com/wasilibs/go-shellcheck/cmd/[email protected]"
4545
- repo: https://github.com/woodruffw/zizmor-pre-commit
46-
rev: v1.0.1
46+
rev: v1.5.2
4747
hooks:
4848
- id: zizmor
4949
- repo: local

docs/source/command_line.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -749,8 +749,19 @@ of the above sections.
749749

750750
.. option:: --strict
751751

752-
This flag mode enables all optional error checking flags. You can see the
753-
list of flags enabled by strict mode in the full :option:`mypy --help` output.
752+
This flag mode enables a defined subset of optional error-checking flags.
753+
This subset primarily includes checks for inadvertent type unsoundness (i.e
754+
strict will catch type errors as long as intentional methods like type ignore
755+
or casting were not used.)
756+
757+
Note: the :option:`--warn-unreachable` flag
758+
is not automatically enabled by the strict flag.
759+
760+
The strict flag does not take precedence over other strict-related flags.
761+
Directly specifying a flag of alternate behavior will override the
762+
behavior of strict, regardless of the order in which they are passed.
763+
You can see the list of flags enabled by strict mode in the full
764+
:option:`mypy --help` output.
754765

755766
Note: the exact list of flags enabled by running :option:`--strict` may change
756767
over time.

docs/source/runtime_troubles.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ version of Python considers legal code. This section describes these scenarios
88
and explains how to get your code running again. Generally speaking, we have
99
three tools at our disposal:
1010

11-
* Use of ``from __future__ import annotations`` (:pep:`563`)
12-
(this behaviour may eventually be made the default in a future Python version)
1311
* Use of string literal types or type comments
1412
* Use of ``typing.TYPE_CHECKING``
13+
* Use of ``from __future__ import annotations`` (:pep:`563`)
1514

1615
We provide a description of these before moving onto discussion of specific
1716
problems you may encounter.

mypy/dmypy/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from mypy.dmypy_os import alive, kill
2121
from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive, send
2222
from mypy.ipc import IPCClient, IPCException
23+
from mypy.main import RECURSION_LIMIT
2324
from mypy.util import check_python_version, get_terminal_width, should_force_color
2425
from mypy.version import __version__
2526

@@ -268,6 +269,10 @@ class BadStatus(Exception):
268269
def main(argv: list[str]) -> None:
269270
"""The code is top-down."""
270271
check_python_version("dmypy")
272+
273+
# set recursion limit consistent with mypy/main.py
274+
sys.setrecursionlimit(RECURSION_LIMIT)
275+
271276
args = parser.parse_args(argv)
272277
if not args.action:
273278
parser.print_usage()

mypy/dmypy_server.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,9 @@ def fine_grained_increment_follow_imports(
620620
t1 = time.time()
621621
manager.log(f"fine-grained increment: find_changed: {t1 - t0:.3f}s")
622622

623+
# Track all modules encountered so far. New entries for all dependencies
624+
# are added below by other module finding methods below. All dependencies
625+
# in graph but not in `seen` are considered deleted at the end of this method.
623626
seen = {source.module for source in sources}
624627

625628
# Find changed modules reachable from roots (or in roots) already in graph.
@@ -736,7 +739,9 @@ def find_reachable_changed_modules(
736739
Args:
737740
roots: modules where to start search from
738741
graph: module graph to use for the search
739-
seen: modules we've seen before that won't be visited (mutated here!!)
742+
seen: modules we've seen before that won't be visited (mutated here!!).
743+
Needed to accumulate all modules encountered during update and remove
744+
everything that no longer exists.
740745
changed_paths: which paths have changed (stop search here and return any found)
741746
742747
Return (encountered reachable changed modules,
@@ -756,7 +761,8 @@ def find_reachable_changed_modules(
756761
changed.append((nxt.module, nxt.path))
757762
elif nxt.module in graph:
758763
state = graph[nxt.module]
759-
for dep in state.dependencies:
764+
ancestors = state.ancestors or []
765+
for dep in state.dependencies + ancestors:
760766
if dep not in seen:
761767
seen.add(dep)
762768
worklist.append(BuildSource(graph[dep].path, graph[dep].id, followed=True))
@@ -775,7 +781,9 @@ def find_added_suppressed(
775781
"""Find suppressed modules that have been added (and not included in seen).
776782
777783
Args:
778-
seen: reachable modules we've seen before (mutated here!!)
784+
seen: reachable modules we've seen before (mutated here!!).
785+
Needed to accumulate all modules encountered during update and remove
786+
everything that no longer exists.
779787
780788
Return suppressed, added modules.
781789
"""

mypy/fastparse.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,29 @@ def parse(
239239
strip_function_bodies=strip_function_bodies,
240240
path=fnam,
241241
).visit(ast)
242+
243+
except RecursionError as e:
244+
# For very complex expressions it is possible to hit recursion limit
245+
# before reaching a leaf node.
246+
# Should reject at top level instead at bottom, since bottom would already
247+
# be at the threshold of the recursion limit, and may fail again later.
248+
# E.G. x1+x2+x3+...+xn -> BinOp(left=BinOp(left=BinOp(left=...
249+
try:
250+
# But to prove that is the cause of this particular recursion error,
251+
# try to walk the tree using builtin visitor
252+
ast3.NodeVisitor().visit(ast)
253+
except RecursionError:
254+
errors.report(
255+
-1, -1, "Source expression too complex to parse", blocker=False, code=codes.MISC
256+
)
257+
258+
tree = MypyFile([], [], False, {})
259+
260+
else:
261+
# re-raise original recursion error if it *can* be unparsed,
262+
# maybe this is some other issue that shouldn't be silenced/misdirected
263+
raise e
264+
242265
except SyntaxError as e:
243266
message = e.msg
244267
if feature_version > sys.version_info.minor and message.startswith("invalid syntax"):
@@ -406,6 +429,7 @@ def visit(self, node: AST | None) -> Any:
406429
method = "visit_" + node.__class__.__name__
407430
visitor = getattr(self, method)
408431
self.visitor_cache[typeobj] = visitor
432+
409433
return visitor(node)
410434

411435
def set_line(self, node: N, n: AstNode) -> N:

mypy/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
orig_stat: Final = os.stat
4444
MEM_PROFILE: Final = False # If True, dump memory profile
45+
RECURSION_LIMIT: Final = 2**14
4546

4647

4748
def stat_proxy(path: str) -> os.stat_result:
@@ -76,7 +77,7 @@ def main(
7677
util.check_python_version("mypy")
7778
t0 = time.time()
7879
# To log stat() calls: os.stat = stat_proxy
79-
sys.setrecursionlimit(2**14)
80+
sys.setrecursionlimit(RECURSION_LIMIT)
8081
if args is None:
8182
args = sys.argv[1:]
8283

mypy/meet.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,12 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
143143
]
144144
)
145145
if is_enum_overlapping_union(declared, narrowed):
146-
return original_narrowed
146+
# Quick check before reaching `is_overlapping_types`. If it's enum/literal overlap,
147+
# avoid full expansion and make it faster.
148+
assert isinstance(narrowed, UnionType)
149+
return make_simplified_union(
150+
[narrow_declared_type(declared, x) for x in narrowed.relevant_items()]
151+
)
147152
elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True):
148153
if state.strict_optional:
149154
return UninhabitedType()

mypy/messages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2439,7 +2439,7 @@ def generate_incompatible_tuple_error(
24392439
error_cnt = 0
24402440
notes: list[str] = []
24412441
for i, (lhs_t, rhs_t) in enumerate(zip(lhs_types, rhs_types)):
2442-
if not is_subtype(lhs_t, rhs_t):
2442+
if not is_subtype(rhs_t, lhs_t):
24432443
if error_cnt < 3:
24442444
notes.append(
24452445
"Expression tuple item {} has type {}; {} expected; ".format(

mypy/plugins/dataclasses.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,12 +359,12 @@ def transform(self) -> bool:
359359

360360
if decorator_arguments["frozen"]:
361361
if any(not parent["frozen"] for parent in parent_decorator_arguments):
362-
self._api.fail("Cannot inherit frozen dataclass from a non-frozen one", info)
362+
self._api.fail("Frozen dataclass cannot inherit from a non-frozen dataclass", info)
363363
self._propertize_callables(attributes, settable=False)
364364
self._freeze(attributes)
365365
else:
366366
if any(parent["frozen"] for parent in parent_decorator_arguments):
367-
self._api.fail("Cannot inherit non-frozen dataclass from a frozen one", info)
367+
self._api.fail("Non-frozen dataclass cannot inherit from a frozen dataclass", info)
368368
self._propertize_callables(attributes)
369369

370370
if decorator_arguments["slots"]:
@@ -381,7 +381,9 @@ def transform(self) -> bool:
381381
):
382382
str_type = self._api.named_type("builtins.str")
383383
literals: list[Type] = [
384-
LiteralType(attr.name, str_type) for attr in attributes if attr.is_in_init
384+
LiteralType(attr.name, str_type)
385+
for attr in attributes
386+
if attr.is_in_init and not attr.kw_only
385387
]
386388
match_args_type = TupleType(literals, self._api.named_type("builtins.tuple"))
387389
add_attribute_to_class(self._api, self._cls, "__match_args__", match_args_type)

0 commit comments

Comments
 (0)