Skip to content

Commit 0d5aff3

Browse files
Merge branch 'master' into patch-22
2 parents 8c6166a + bec8cba commit 0d5aff3

210 files changed

Lines changed: 4406 additions & 2700 deletions

File tree

Some content is hidden

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

CHANGELOG.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@ Support for this will be dropped in the first half of 2026!
1111

1212
Contributed by Marc Mueller (PR [20156](https://github.com/python/mypy/pull/20156)).
1313

14+
### Mypyc Accelerated Mypy Wheels for ARM Windows and Free Threading
15+
16+
For best performance, mypy can be compiled to C extension modules using mypyc. This makes
17+
mypy 3-5x faster than when interpreted with pure Python. We now build and upload mypyc
18+
accelerated mypy wheels for `win_arm64` and `cp314t-...` to PyPI, making it easy for Windows
19+
users on ARM and those using the free theading builds for Python 3.14 to realise this speedup
20+
-- just `pip install` the latest mypy.
21+
22+
Contributed by Marc Mueller
23+
(PR [mypy_mypyc-wheels#106](https://github.com/mypyc/mypy_mypyc-wheels/pull/106),
24+
PR [mypy_mypyc-wheels#110](https://github.com/mypyc/mypy_mypyc-wheels/pull/110)).
25+
26+
### Removed flags `--force-uppercase-builtins` and `--force-union-syntax`
27+
28+
The `--force-uppercase-builtins` flag was deprecated and has been a no-op since mypy 1.17.0.
29+
Since mypy has dropped support for Python 3.9, the `--force-union-syntax` flag is no longer
30+
necessary.
31+
32+
Contributed by Marc Mueller (PR [20410](https://github.com/python/mypy/pull/20410))
33+
and (PR [20405](https://github.com/python/mypy/pull/20405)).
34+
1435
## Mypy 1.19
1536

1637
We’ve just uploaded mypy 1.19.0 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
@@ -216,6 +237,17 @@ Related PRs:
216237

217238
Please see [git log](https://github.com/python/typeshed/commits/main?after=ebce8d766b41fbf4d83cf47c1297563a9508ff60+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes.
218239

240+
### Mypy 1.19.1
241+
242+
- Fix noncommutative joins with bounded TypeVars (Shantanu, PR [20345](https://github.com/python/mypy/pull/20345))
243+
- Respect output format for cached runs by serializing raw errors in cache metas (Ivan Levkivskyi, PR [20372](https://github.com/python/mypy/pull/20372))
244+
- Allow `types.NoneType` in match cases (A5rocks, PR [20383](https://github.com/python/mypy/pull/20383))
245+
- Fix mypyc generator regression with empty tuple (BobTheBuidler, PR [20371](https://github.com/python/mypy/pull/20371))
246+
- Fix crash involving Unpack-ed TypeVarTuple (Shantanu, PR [20323](https://github.com/python/mypy/pull/20323))
247+
- Fix crash on star import of redefinition (Ivan Levkivskyi, PR [20333](https://github.com/python/mypy/pull/20333))
248+
- Fix crash on typevar with forward ref used in other module (Ivan Levkivskyi, PR [20334](https://github.com/python/mypy/pull/20334))
249+
- Fail with an explicit error on PyPy (Ivan Levkivskyi, PR [20389](https://github.com/python/mypy/pull/20389))
250+
219251
### Acknowledgements
220252

221253
Thanks to all mypy contributors who contributed to this release:
@@ -251,7 +283,7 @@ Thanks to all mypy contributors who contributed to this release:
251283

252284
I’d also like to thank my employer, Dropbox, for supporting mypy development.
253285

254-
## Mypy 1.18.1
286+
## Mypy 1.18
255287

256288
We’ve just uploaded mypy 1.18.1 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
257289
Mypy is a static type checker for Python. This release includes new features, performance

docs/source/additional_features.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Type annotations can be added as follows:
135135

136136
.. code-block:: python
137137
138-
import attr
138+
import attrs
139139
140140
@attrs.define
141141
class A:

docs/source/command_line.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -958,12 +958,6 @@ in error messages.
958958
useful or they may be overly noisy. If ``N`` is negative, there is
959959
no limit. The default limit is -1.
960960

961-
.. option:: --force-union-syntax
962-
963-
Always use ``Union[]`` and ``Optional[]`` for union types
964-
in error messages (instead of the ``|`` operator),
965-
even on Python 3.10+.
966-
967961

968962
.. _incremental:
969963

docs/source/config_file.rst

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -930,15 +930,6 @@ These options may only be set in the global section (``[mypy]``).
930930

931931
Show absolute paths to files.
932932

933-
.. confval:: force_union_syntax
934-
935-
:type: boolean
936-
:default: False
937-
938-
Always use ``Union[]`` and ``Optional[]`` for union types
939-
in error messages (instead of the ``|`` operator),
940-
even on Python 3.10+.
941-
942933
Incremental mode
943934
****************
944935

docs/source/error_code_list2.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Example:
3232
3333
# mypy: disallow-any-generics
3434
35-
# Error: Missing type parameters for generic type "list" [type-arg]
35+
# Error: Missing type arguments for generic type "list" [type-arg]
3636
def remove_dups(items: list) -> list:
3737
...
3838

mypy-requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ typing_extensions>=4.6.0
44
mypy_extensions>=1.0.0
55
pathspec>=0.9.0
66
tomli>=1.1.0; python_version<'3.11'
7-
librt>=0.6.2
7+
librt>=0.6.2; platform_python_implementation != 'PyPy'

mypy/build.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,14 @@
4343
from librt.internal import cache_version
4444

4545
import mypy.semanal_main
46-
from mypy.cache import CACHE_VERSION, CacheMeta, ReadBuffer, WriteBuffer, write_json
46+
from mypy.cache import (
47+
CACHE_VERSION,
48+
CacheMeta,
49+
ReadBuffer,
50+
SerializedError,
51+
WriteBuffer,
52+
write_json,
53+
)
4754
from mypy.checker import TypeChecker
4855
from mypy.defaults import (
4956
WORKER_CONNECTION_TIMEOUT,
@@ -52,7 +59,7 @@
5259
WORKER_START_TIMEOUT,
5360
)
5461
from mypy.error_formatter import OUTPUT_CHOICES, ErrorFormatter
55-
from mypy.errors import CompileError, ErrorInfo, Errors, report_internal_error
62+
from mypy.errors import CompileError, ErrorInfo, Errors, ErrorTuple, report_internal_error
5663
from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort
5764
from mypy.indirection import TypeIndirectionVisitor
5865
from mypy.ipc import BadStatus, IPCClient, read_status, ready_to_read, receive, send
@@ -2046,7 +2053,7 @@ class State:
20462053
dep_hashes: dict[str, bytes] = {}
20472054

20482055
# List of errors reported for this file last time.
2049-
error_lines: list[str] = []
2056+
error_lines: list[SerializedError] = []
20502057

20512058
# Parent package, its parent, etc.
20522059
ancestors: list[str] | None = None
@@ -2173,7 +2180,8 @@ def __init__(
21732180
self.dep_hashes = {
21742181
k: v for (k, v) in zip(self.meta.dependencies, self.meta.dep_hashes)
21752182
}
2176-
self.error_lines = self.meta.error_lines
2183+
# Only copy `error_lines` if the module is not silently imported.
2184+
self.error_lines = [] if self.ignore_all else self.meta.error_lines
21772185
if temporary:
21782186
self.load_tree(temporary=True)
21792187
if not manager.use_fine_grained_cache():
@@ -3511,9 +3519,13 @@ def find_stale_sccs(
35113519
scc = order_ascc_ex(graph, ascc)
35123520
for id in scc:
35133521
if graph[id].error_lines:
3514-
manager.flush_errors(
3515-
manager.errors.simplify_path(graph[id].xpath), graph[id].error_lines, False
3522+
path = manager.errors.simplify_path(graph[id].xpath)
3523+
formatted = manager.errors.format_messages(
3524+
path,
3525+
deserialize_codes(graph[id].error_lines),
3526+
formatter=manager.error_formatter,
35163527
)
3528+
manager.flush_errors(path, formatted, False)
35173529
fresh_sccs.append(ascc)
35183530
else:
35193531
size = len(ascc.mod_ids)
@@ -3759,21 +3771,24 @@ def process_stale_scc(
37593771
# Flush errors, and write cache in two phases: first data files, then meta files.
37603772
meta_tuples = {}
37613773
errors_by_id = {}
3774+
formatted_by_id = {}
37623775
for id in stale:
37633776
if graph[id].xpath not in manager.errors.ignored_files:
3764-
errors = manager.errors.file_messages(
3765-
graph[id].xpath, formatter=manager.error_formatter
3777+
errors = manager.errors.file_messages(graph[id].xpath)
3778+
formatted = manager.errors.format_messages(
3779+
graph[id].xpath, errors, formatter=manager.error_formatter
37663780
)
3767-
manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False)
3781+
manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), formatted, False)
37683782
errors_by_id[id] = errors
3783+
formatted_by_id[id] = formatted
37693784
meta_tuples[id] = graph[id].write_cache()
37703785
for id in stale:
37713786
meta_tuple = meta_tuples[id]
37723787
if meta_tuple is None:
37733788
continue
37743789
meta, meta_file = meta_tuple
37753790
meta.dep_hashes = [graph[dep].interface_hash for dep in graph[id].dependencies]
3776-
meta.error_lines = errors_by_id.get(id, [])
3791+
meta.error_lines = serialize_codes(errors_by_id.get(id, []))
37773792
write_cache_meta(meta, manager, meta_file)
37783793
manager.done_sccs.add(ascc.id)
37793794
manager.add_stats(
@@ -3785,7 +3800,7 @@ def process_stale_scc(
37853800
)
37863801
scc_result = {}
37873802
for id in scc:
3788-
scc_result[id] = graph[id].interface_hash.hex(), errors_by_id.get(id, [])
3803+
scc_result[id] = graph[id].interface_hash.hex(), formatted_by_id.get(id, [])
37893804
return scc_result
37903805

37913806

@@ -3932,3 +3947,26 @@ def sccs_to_bytes(sccs: list[SCC]) -> bytes:
39323947
buf = WriteBuffer()
39333948
write_json(buf, {"sccs": scc_tuples})
39343949
return buf.getvalue()
3950+
3951+
3952+
def serialize_codes(errs: list[ErrorTuple]) -> list[SerializedError]:
3953+
return [
3954+
(path, line, column, end_line, end_column, severity, message, code.code if code else None)
3955+
for path, line, column, end_line, end_column, severity, message, code in errs
3956+
]
3957+
3958+
3959+
def deserialize_codes(errs: list[SerializedError]) -> list[ErrorTuple]:
3960+
return [
3961+
(
3962+
path,
3963+
line,
3964+
column,
3965+
end_line,
3966+
end_column,
3967+
severity,
3968+
message,
3969+
codes.error_codes.get(code) if code else None,
3970+
)
3971+
for path, line, column, end_line, end_column, severity, message, code in errs
3972+
]

mypy/cache.py

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@
6969
from mypy_extensions import u8
7070

7171
# High-level cache layout format
72-
CACHE_VERSION: Final = 0
72+
CACHE_VERSION: Final = 1
73+
74+
SerializedError: _TypeAlias = tuple[str | None, int, int, int, int, str, str, str | None]
7375

7476

7577
class CacheMeta:
@@ -92,7 +94,7 @@ def __init__(
9294
dep_lines: list[int],
9395
dep_hashes: list[bytes],
9496
interface_hash: bytes,
95-
error_lines: list[str],
97+
error_lines: list[SerializedError],
9698
version_id: str,
9799
ignore_all: bool,
98100
plugin_data: Any,
@@ -157,7 +159,7 @@ def deserialize(cls, meta: dict[str, Any], data_file: str) -> CacheMeta | None:
157159
dep_lines=meta["dep_lines"],
158160
dep_hashes=[bytes.fromhex(dep) for dep in meta["dep_hashes"]],
159161
interface_hash=bytes.fromhex(meta["interface_hash"]),
160-
error_lines=meta["error_lines"],
162+
error_lines=[tuple(err) for err in meta["error_lines"]],
161163
version_id=meta["version_id"],
162164
ignore_all=meta["ignore_all"],
163165
plugin_data=meta["plugin_data"],
@@ -179,7 +181,7 @@ def write(self, data: WriteBuffer) -> None:
179181
write_int_list(data, self.dep_lines)
180182
write_bytes_list(data, self.dep_hashes)
181183
write_bytes(data, self.interface_hash)
182-
write_str_list(data, self.error_lines)
184+
write_errors(data, self.error_lines)
183185
write_str(data, self.version_id)
184186
write_bool(data, self.ignore_all)
185187
# Plugin data may be not a dictionary, so we use
@@ -204,7 +206,7 @@ def read(cls, data: ReadBuffer, data_file: str) -> CacheMeta | None:
204206
dep_lines=read_int_list(data),
205207
dep_hashes=read_bytes_list(data),
206208
interface_hash=read_bytes(data),
207-
error_lines=read_str_list(data),
209+
error_lines=read_errors(data),
208210
version_id=read_str(data),
209211
ignore_all=read_bool(data),
210212
plugin_data=read_json_value(data),
@@ -231,6 +233,7 @@ def read(cls, data: ReadBuffer, data_file: str) -> CacheMeta | None:
231233
LIST_INT: Final[Tag] = 21
232234
LIST_STR: Final[Tag] = 22
233235
LIST_BYTES: Final[Tag] = 23
236+
TUPLE_GEN: Final[Tag] = 24
234237
DICT_STR_GEN: Final[Tag] = 30
235238

236239
# Misc classes.
@@ -391,12 +394,11 @@ def write_str_opt_list(data: WriteBuffer, value: list[str | None]) -> None:
391394

392395

393396
Value: _TypeAlias = None | int | str | bool
394-
JsonValue: _TypeAlias = Value | list["JsonValue"] | dict[str, "JsonValue"]
395397

396-
# Currently tuples are used by mypyc plugin. They will be normalized to
397-
# JSON lists after a roundtrip.
398-
JsonValueEx: _TypeAlias = (
399-
Value | list["JsonValueEx"] | dict[str, "JsonValueEx"] | tuple["JsonValueEx", ...]
398+
# Our JSON format is somewhat non-standard as we distinguish lists and tuples.
399+
# This is convenient for some internal things, like mypyc plugin and error serialization.
400+
JsonValue: _TypeAlias = (
401+
Value | list["JsonValue"] | dict[str, "JsonValue"] | tuple["JsonValue", ...]
400402
)
401403

402404

@@ -415,13 +417,16 @@ def read_json_value(data: ReadBuffer) -> JsonValue:
415417
if tag == LIST_GEN:
416418
size = read_int_bare(data)
417419
return [read_json_value(data) for _ in range(size)]
420+
if tag == TUPLE_GEN:
421+
size = read_int_bare(data)
422+
return tuple(read_json_value(data) for _ in range(size))
418423
if tag == DICT_STR_GEN:
419424
size = read_int_bare(data)
420425
return {read_str_bare(data): read_json_value(data) for _ in range(size)}
421426
assert False, f"Invalid JSON tag: {tag}"
422427

423428

424-
def write_json_value(data: WriteBuffer, value: JsonValueEx) -> None:
429+
def write_json_value(data: WriteBuffer, value: JsonValue) -> None:
425430
if value is None:
426431
write_tag(data, LITERAL_NONE)
427432
elif isinstance(value, bool):
@@ -432,11 +437,16 @@ def write_json_value(data: WriteBuffer, value: JsonValueEx) -> None:
432437
elif isinstance(value, str):
433438
write_tag(data, LITERAL_STR)
434439
write_str_bare(data, value)
435-
elif isinstance(value, (list, tuple)):
440+
elif isinstance(value, list):
436441
write_tag(data, LIST_GEN)
437442
write_int_bare(data, len(value))
438443
for val in value:
439444
write_json_value(data, val)
445+
elif isinstance(value, tuple):
446+
write_tag(data, TUPLE_GEN)
447+
write_int_bare(data, len(value))
448+
for val in value:
449+
write_json_value(data, val)
440450
elif isinstance(value, dict):
441451
write_tag(data, DICT_STR_GEN)
442452
write_int_bare(data, len(value))
@@ -461,3 +471,38 @@ def write_json(data: WriteBuffer, value: dict[str, Any]) -> None:
461471
for key in sorted(value):
462472
write_str_bare(data, key)
463473
write_json_value(data, value[key])
474+
475+
476+
def write_errors(data: WriteBuffer, errs: list[SerializedError]) -> None:
477+
write_tag(data, LIST_GEN)
478+
write_int_bare(data, len(errs))
479+
for path, line, column, end_line, end_column, severity, message, code in errs:
480+
write_tag(data, TUPLE_GEN)
481+
write_str_opt(data, path)
482+
write_int(data, line)
483+
write_int(data, column)
484+
write_int(data, end_line)
485+
write_int(data, end_column)
486+
write_str(data, severity)
487+
write_str(data, message)
488+
write_str_opt(data, code)
489+
490+
491+
def read_errors(data: ReadBuffer) -> list[SerializedError]:
492+
assert read_tag(data) == LIST_GEN
493+
result = []
494+
for _ in range(read_int_bare(data)):
495+
assert read_tag(data) == TUPLE_GEN
496+
result.append(
497+
(
498+
read_str_opt(data),
499+
read_int(data),
500+
read_int(data),
501+
read_int(data),
502+
read_int(data),
503+
read_str(data),
504+
read_str(data),
505+
read_str_opt(data),
506+
)
507+
)
508+
return result

0 commit comments

Comments
 (0)