Skip to content

Commit fc00f9c

Browse files
committed
Merge branch 'master' into fix_strict_quality_in_loops
2 parents d1ba2c6 + 8e2ce96 commit fc00f9c

File tree

192 files changed

+8956
-1963
lines changed

Some content is hidden

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

192 files changed

+8956
-1963
lines changed

.github/workflows/test.yml

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,38 @@ jobs:
5959
toxenv: py
6060
tox_extra_args: "-n 4"
6161
test_mypyc: true
62-
- name: Test suite with py313-windows-64
63-
python: '3.13'
62+
- name: Test suite with py314-ubuntu, mypyc-compiled
63+
python: '3.14'
64+
os: ubuntu-24.04-arm
65+
toxenv: py
66+
tox_extra_args: "-n 4"
67+
test_mypyc: true
68+
- name: Test suite with py314-windows-64
69+
python: '3.14'
6470
os: windows-latest
6571
toxenv: py
6672
tox_extra_args: "-n 4"
6773

68-
- name: Test suite with py314-dev-ubuntu
69-
python: '3.14-dev'
70-
os: ubuntu-24.04-arm
74+
# - name: Test suite with py315-dev-ubuntu
75+
# python: '3.15-dev'
76+
# os: ubuntu-24.04-arm
77+
# toxenv: py
78+
# tox_extra_args: "-n 4"
79+
# # allow_failure: true
80+
# test_mypyc: true
81+
82+
- name: mypyc runtime tests with py313-macos
83+
python: '3.13'
84+
os: macos-latest
7185
toxenv: py
72-
tox_extra_args: "-n 4"
73-
# allow_failure: true
74-
test_mypyc: true
86+
tox_extra_args: "-n 3 mypyc/test/test_run.py mypyc/test/test_external.py"
7587

76-
- name: mypyc runtime tests with py39-macos
77-
python: '3.9.21'
78-
# TODO: macos-13 is the last one to support Python 3.9, change it to macos-latest when updating the Python version
79-
os: macos-13
88+
- name: mypyc runtime tests with py310-ubuntu
89+
python: '3.10'
90+
os: ubuntu-latest
8091
toxenv: py
8192
tox_extra_args: "-n 3 mypyc/test/test_run.py mypyc/test/test_external.py"
93+
8294
# This is broken. See
8395
# - https://github.com/python/mypy/issues/17819
8496
# - https://github.com/python/mypy/pull/17822

.github/workflows/test_stubgenc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ on:
1111
- 'mypy/stubgenc.py'
1212
- 'mypy/stubdoc.py'
1313
- 'mypy/stubutil.py'
14-
- 'test-data/stubgen/**'
14+
- 'test-data/pybind11_fixtures/**'
1515

1616
permissions:
1717
contents: read

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ repos:
66
- id: trailing-whitespace
77
- id: end-of-file-fixer
88
- repo: https://github.com/psf/black-pre-commit-mirror
9-
rev: 25.1.0
9+
rev: 25.9.0
1010
hooks:
1111
- id: black
1212
exclude: '^(test-data/)'
1313
- repo: https://github.com/astral-sh/ruff-pre-commit
14-
rev: v0.11.4
14+
rev: v0.14.3
1515
hooks:
16-
- id: ruff
16+
- id: ruff-check
1717
args: [--exit-non-zero-on-fix]
1818
- repo: https://github.com/python-jsonschema/check-jsonschema
1919
rev: 0.32.1
@@ -41,7 +41,7 @@ repos:
4141
# actionlint has a shellcheck integration which extracts shell scripts in `run:` steps from GitHub Actions
4242
# and checks these with shellcheck. This is arguably its most useful feature,
4343
# but the integration only works if shellcheck is installed
44-
- "github.com/wasilibs/go-shellcheck/cmd/[email protected].0"
44+
- "github.com/wasilibs/go-shellcheck/cmd/[email protected].1"
4545
- repo: https://github.com/woodruffw/zizmor-pre-commit
4646
rev: v1.5.2
4747
hooks:

docs/source/command_line.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,7 +1159,7 @@ format into the specified directory.
11591159
Enabling incomplete/experimental features
11601160
*****************************************
11611161

1162-
.. option:: --enable-incomplete-feature {PreciseTupleTypes, InlineTypedDict}
1162+
.. option:: --enable-incomplete-feature {PreciseTupleTypes,InlineTypedDict,TypeForm}
11631163

11641164
Some features may require several mypy releases to implement, for example
11651165
due to their complexity, potential for backwards incompatibility, or
@@ -1211,8 +1211,11 @@ List of currently incomplete/experimental features:
12111211

12121212
.. code-block:: python
12131213
1214-
def test_values() -> {"int": int, "str": str}:
1215-
return {"int": 42, "str": "test"}
1214+
def test_values() -> {"width": int, "description": str}:
1215+
return {"width": 42, "description": "test"}
1216+
1217+
* ``TypeForm``: this feature enables ``TypeForm``, as described in
1218+
`PEP 747 – Annotating Type Forms <https://peps.python.org/pep-0747/>_`.
12161219

12171220

12181221
Miscellaneous

docs/source/duck_type_compatibility.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ supported for a small set of built-in types:
99
* ``int`` is duck type compatible with ``float`` and ``complex``.
1010
* ``float`` is duck type compatible with ``complex``.
1111
* ``bytearray`` and ``memoryview`` are duck type compatible with ``bytes``.
12+
(this will be disabled by default in **mypy 2.0**, and currently can be
13+
disabled with :option:`--strict-bytes <mypy --strict-bytes>`.)
1214

1315
For example, mypy considers an ``int`` object to be valid whenever a
1416
``float`` object is expected. Thus code like this is nice and clean

docs/source/error_code_list.rst

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,6 +1286,65 @@ type must be a subtype of the original type::
12861286
def g(x: object) -> TypeIs[str]: # OK
12871287
...
12881288

1289+
.. _code-maybe-unrecognized-str-typeform:
1290+
1291+
String appears in a context which expects a TypeForm [maybe-unrecognized-str-typeform]
1292+
--------------------------------------------------------------------------------------
1293+
1294+
TypeForm literals may contain string annotations:
1295+
1296+
.. code-block:: python
1297+
1298+
typx1: TypeForm = str | None
1299+
typx2: TypeForm = 'str | None' # OK
1300+
typx3: TypeForm = 'str' | None # OK
1301+
1302+
However TypeForm literals containing a string annotation can only be recognized
1303+
by mypy in the following locations:
1304+
1305+
.. code-block:: python
1306+
1307+
typx_var: TypeForm = 'str | None' # assignment r-value
1308+
1309+
def func(typx_param: TypeForm) -> TypeForm:
1310+
return 'str | None' # returned expression
1311+
1312+
func('str | None') # callable's argument
1313+
1314+
If you try to use a string annotation in some other location
1315+
which expects a TypeForm, the string value will always be treated as a ``str``
1316+
even if a ``TypeForm`` would be more appropriate and this error code
1317+
will be generated:
1318+
1319+
.. code-block:: python
1320+
1321+
# Error: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. [maybe-unrecognized-str-typeform]
1322+
# Error: List item 0 has incompatible type "str"; expected "TypeForm[Any]" [list-item]
1323+
list_of_typx: list[TypeForm] = ['str | None', float]
1324+
1325+
Fix the error by surrounding the entire type with ``TypeForm(...)``:
1326+
1327+
.. code-block:: python
1328+
1329+
list_of_typx: list[TypeForm] = [TypeForm('str | None'), float] # OK
1330+
1331+
Similarly, if you try to use a string literal in a location which expects a
1332+
TypeForm, this error code will be generated:
1333+
1334+
.. code-block:: python
1335+
1336+
dict_of_typx = {'str_or_none': TypeForm(str | None)}
1337+
# Error: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. [maybe-unrecognized-str-typeform]
1338+
list_of_typx: list[TypeForm] = [dict_of_typx['str_or_none']]
1339+
1340+
Fix the error by adding ``# type: ignore[maybe-unrecognized-str-typeform]``
1341+
to the line with the string literal:
1342+
1343+
.. code-block:: python
1344+
1345+
dict_of_typx = {'str_or_none': TypeForm(str | None)}
1346+
list_of_typx: list[TypeForm] = [dict_of_typx['str_or_none']] # type: ignore[maybe-unrecognized-str-typeform]
1347+
12891348
.. _code-misc:
12901349

12911350
Miscellaneous checks [misc]

docs/source/generics.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1165,7 +1165,7 @@ This example correctly uses a covariant type variable:
11651165
11661166
See :ref:`variance-of-generics` for more about variance.
11671167

1168-
Generic protocols can also be recursive. Example (Python 3.12 synta):
1168+
Generic protocols can also be recursive. Example (Python 3.12 syntax):
11691169

11701170
.. code-block:: python
11711171

docs/source/kinds_of_types.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ isn't supported by the runtime with some limitations, if you use
294294
def f(x: int | str) -> None: # OK on Python 3.7 and later
295295
...
296296
297+
.. _no-strict-optional:
297298
.. _strict_optional:
298299

299300
Optional types and the None type

docs/source/typed_dict.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ to use inline TypedDict syntax. For example:
303303

304304
.. code-block:: python
305305
306-
def test_values() -> {"int": int, "str": str}:
307-
return {"int": 42, "str": "test"}
306+
def test_values() -> {"width": int, "description": str}:
307+
return {"width": 42, "description": "test"}
308308
309309
class Response(TypedDict):
310310
status: int

misc/analyze_typeform_stats.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Analyze TypeForm parsing efficiency from mypy build stats.
4+
5+
Usage:
6+
python3 analyze_typeform_stats.py '<mypy_output_with_stats>'
7+
python3 -m mypy --dump-build-stats file.py 2>&1 | python3 analyze_typeform_stats.py
8+
9+
Example output:
10+
TypeForm Expression Parsing Statistics:
11+
==================================================
12+
Total calls to SA.try_parse_as_type_expression: 14,555
13+
Quick rejections (no full parse): 14,255
14+
Full parses attempted: 300
15+
- Successful: 248
16+
- Failed: 52
17+
18+
Efficiency Metrics:
19+
- Quick rejection rate: 97.9%
20+
- Full parse rate: 2.1%
21+
- Full parse success rate: 82.7%
22+
- Overall success rate: 1.7%
23+
24+
Performance Implications:
25+
- Expensive failed full parses: 52 (0.4% of all calls)
26+
27+
See also:
28+
- mypy/semanal.py: SemanticAnalyzer.try_parse_as_type_expression()
29+
- mypy/semanal.py: DEBUG_TYPE_EXPRESSION_FULL_PARSE_FAILURES
30+
"""
31+
32+
import re
33+
import sys
34+
35+
36+
def analyze_stats(output: str) -> None:
37+
"""Parse mypy stats output and calculate TypeForm parsing efficiency."""
38+
39+
# Extract the three counters
40+
total_match = re.search(r"type_expression_parse_count:\s*(\d+)", output)
41+
success_match = re.search(r"type_expression_full_parse_success_count:\s*(\d+)", output)
42+
failure_match = re.search(r"type_expression_full_parse_failure_count:\s*(\d+)", output)
43+
44+
if not (total_match and success_match and failure_match):
45+
print("Error: Could not find all required counters in output")
46+
return
47+
48+
total = int(total_match.group(1))
49+
successes = int(success_match.group(1))
50+
failures = int(failure_match.group(1))
51+
52+
full_parses = successes + failures
53+
54+
print("TypeForm Expression Parsing Statistics:")
55+
print("=" * 50)
56+
print(f"Total calls to SA.try_parse_as_type_expression: {total:,}")
57+
print(f"Quick rejections (no full parse): {total - full_parses:,}")
58+
print(f"Full parses attempted: {full_parses:,}")
59+
print(f" - Successful: {successes:,}")
60+
print(f" - Failed: {failures:,}")
61+
if total > 0:
62+
print()
63+
print("Efficiency Metrics:")
64+
print(f" - Quick rejection rate: {((total - full_parses) / total * 100):.1f}%")
65+
print(f" - Full parse rate: {(full_parses / total * 100):.1f}%")
66+
print(f" - Full parse success rate: {(successes / full_parses * 100):.1f}%")
67+
print(f" - Overall success rate: {(successes / total * 100):.1f}%")
68+
print()
69+
print("Performance Implications:")
70+
print(
71+
f" - Expensive failed full parses: {failures:,} ({(failures / total * 100):.1f}% of all calls)"
72+
)
73+
74+
75+
if __name__ == "__main__":
76+
if len(sys.argv) == 1:
77+
# Read from stdin
78+
output = sys.stdin.read()
79+
elif len(sys.argv) == 2:
80+
# Read from command line argument
81+
output = sys.argv[1]
82+
else:
83+
print("Usage: python3 analyze_typeform_stats.py [mypy_output_with_stats]")
84+
print("Examples:")
85+
print(
86+
" python3 -m mypy --dump-build-stats file.py 2>&1 | python3 analyze_typeform_stats.py"
87+
)
88+
print(" python3 analyze_typeform_stats.py 'output_string'")
89+
sys.exit(1)
90+
91+
analyze_stats(output)

0 commit comments

Comments
 (0)