Skip to content

Commit 12f43aa

Browse files
authored
Merge branch 'main' into gh-153668
2 parents fc71c9f + 89c67a9 commit 12f43aa

124 files changed

Lines changed: 3229 additions & 2623 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.

‎.github/CODEOWNERS‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,10 @@ Tools/jit/ @brandtbucher @savannahostrowski @diegorusso
290290
InternalDocs/jit.md @brandtbucher @savannahostrowski @diegorusso @AA-Turner
291291

292292
# Lazy imports (PEP 810)
293-
Objects/lazyimportobject.c @yhg1s @DinoV @pablogsal
294-
Include/internal/pycore_lazyimportobject.h @yhg1s @DinoV @pablogsal
295-
Lib/test/test_lazy_import @yhg1s @DinoV @pablogsal
293+
.github/workflows/reusable-test-lazy-imports-all.yml @yhg1s @DinoV @pablogsal
294+
Objects/lazyimportobject.c @yhg1s @DinoV @pablogsal
295+
Include/internal/pycore_lazyimportobject.h @yhg1s @DinoV @pablogsal
296+
Lib/test/test_lazy_import @yhg1s @DinoV @pablogsal
296297

297298
# Micro-op / μop / Tier 2 Optimiser
298299
Python/optimizer.c @markshannon @Fidget-Spinner
@@ -655,5 +656,8 @@ Objects/**/clinic/
655656
PC/**/clinic/
656657
Python/**/clinic/
657658

659+
# Exclude Lazy Imports=all CI carve out file
660+
Lib/test/lazy_imports_all_exclude.txt
661+
658662
# Exclude HTML IDs list
659663
Doc/tools/removed-ids.txt

‎.github/workflows/build.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,12 @@ jobs:
476476
name: hypothesis-example-db
477477
path: ${{ env.CPYTHON_BUILDDIR }}/.hypothesis/examples/
478478

479+
test-lazy-imports-all:
480+
name: 'Lazy imports enabled'
481+
needs: build-context
482+
if: fromJSON(needs.build-context.outputs.run-tests)
483+
uses: ./.github/workflows/reusable-test-lazy-imports-all.yml
484+
479485
build-asan:
480486
name: 'Address sanitizer'
481487
runs-on: ${{ matrix.os }}
@@ -648,6 +654,7 @@ jobs:
648654
- build-emscripten
649655
- build-wasi
650656
- test-hypothesis
657+
- test-lazy-imports-all
651658
- build-asan
652659
- build-san
653660
- cross-build-linux
@@ -705,4 +712,5 @@ jobs:
705712
${{ !fromJSON(needs.build-context.outputs.run-ios) && 'build-ios,' || '' }}
706713
${{ !fromJSON(needs.build-context.outputs.run-emscripten) && 'build-emscripten,' || '' }}
707714
${{ !fromJSON(needs.build-context.outputs.run-wasi) && 'build-wasi,' || '' }}
715+
${{ !fromJSON(needs.build-context.outputs.run-tests) && 'test-lazy-imports-all,' || '' }}
708716
jobs: ${{ toJSON(needs) }}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Reusable Lazy Imports Tests
2+
3+
# Run the CPython test suite with global lazy imports forced on
4+
# (``-X lazy_imports=all``).
5+
#
6+
# Modules that are known to fail under lazy imports are listed in
7+
# Lib/test/lazy_imports_all_exclude.txt and skipped here. Remove entries from
8+
# that file as the modules are fixed so this workflow starts guarding them
9+
# against regressions. Excluded modules are also checked separately so the
10+
# workflow fails when one starts passing and its exclusion should be removed.
11+
12+
on:
13+
workflow_call:
14+
15+
permissions:
16+
contents: read
17+
18+
env:
19+
FORCE_COLOR: 1
20+
21+
jobs:
22+
test-lazy-imports-all:
23+
name: 'Run Tests with lazy_imports=all'
24+
runs-on: ubuntu-26.04
25+
timeout-minutes: 60
26+
env:
27+
EXCLUDE_FILE: Lib/test/lazy_imports_all_exclude.txt
28+
steps:
29+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
30+
with:
31+
persist-credentials: false
32+
- name: Register gcc problem matcher
33+
run: echo "::add-matcher::.github/problem-matchers/gcc.json"
34+
- name: Install dependencies
35+
run: sudo ./.github/workflows/posix-deps-apt.sh
36+
- name: Configure CPython
37+
run: ./configure --config-cache --with-pydebug
38+
- name: Build CPython
39+
run: make -j4
40+
- name: Display build info
41+
run: make pythoninfo
42+
- name: Verify lazy imports are fully enabled
43+
run: ./python -X lazy_imports=all -c "import sys; assert sys.flags.lazy_imports == 1, sys.flags.lazy_imports; print('lazy imports all enabled')"
44+
- name: Build test list (all tests minus the known-failing exclusions)
45+
run: |
46+
set -euo pipefail
47+
./python -m test --list-tests > all_tests.txt
48+
# Strip comments/blank lines from the exclusion file, then drop those
49+
# exact test names (whole-line, fixed-string match) from the run list.
50+
grep -vE '^\s*(#.*)?$' "$EXCLUDE_FILE" > exclude_tests.txt || true
51+
grep -vxF -f exclude_tests.txt all_tests.txt > run_tests.txt
52+
# Fail loudly if any exclusion entry matched nothing: a stale or
53+
# mistyped name (or a change in `--list-tests` output) would otherwise
54+
# silently stop excluding a module and let it fail the run.
55+
stale=$(comm -23 <(sort -u exclude_tests.txt) <(sort -u all_tests.txt))
56+
if [ -n "$stale" ]; then
57+
echo "::error::Stale entries in $EXCLUDE_FILE (no longer match 'python -m test --list-tests'); remove or fix them:"
58+
echo "$stale"
59+
exit 1
60+
fi
61+
echo "Excluding $(wc -l < exclude_tests.txt) module(s); running $(wc -l < run_tests.txt) of $(wc -l < all_tests.txt)."
62+
- name: Run tests with lazy imports
63+
run: xvfb-run xargs -a run_tests.txt ./python -X lazy_imports=all -m test --fast-ci --timeout=900 < /dev/null
64+
- name: Verify excluded tests still need exclusion
65+
run: |
66+
set -euo pipefail
67+
unexpected_passes=()
68+
while IFS= read -r test_name; do
69+
[ -n "$test_name" ] || continue
70+
echo "Checking excluded test: $test_name"
71+
if xvfb-run ./python -X lazy_imports=all -m test --fast-ci --timeout=900 "$test_name"; then
72+
unexpected_passes+=("$test_name")
73+
fi
74+
done < exclude_tests.txt
75+
if [ "${#unexpected_passes[@]}" -ne 0 ]; then
76+
echo "::error::These tests still appear in $EXCLUDE_FILE but now pass with -X lazy_imports=all. Remove them from the exclude file:"
77+
printf '%s\n' "${unexpected_passes[@]}"
78+
exit 1
79+
fi

‎Doc/Makefile‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ lock:
191191
# Dependencies have a 14 day cooldown period to mitigate supply chain attacks,
192192
# except for sphinx_linklint and python-docs-theme, which are maintained by
193193
# core team members.
194-
uv pip compile requirements.txt \
195-
--exclude-newer P14D \
194+
$(UV) pip compile requirements.txt \
195+
--upgrade --exclude-newer P14D \
196196
--exclude-newer-package sphinx_linklint=PT0S \
197197
--exclude-newer-package python-docs-theme=PT0S \
198198
--no-cache --output-file $(REQUIREMENTS) \

‎Doc/c-api/unicode.rst‎

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ points must be below 1114112 (which is the full Unicode range).
1616

1717
UTF-8 representation is created on demand and cached in the Unicode object.
1818

19+
.. impl-detail::
20+
21+
The internal buffer always includes an extra trailing null character for
22+
compatibility with null terminated C strings. This extra character is not
23+
counted in :c:func:`PyUnicode_GetLength` nor in the various *size* arguments
24+
of the functions below.
25+
1926
.. note::
2027
The :c:type:`Py_UNICODE` representation has been removed since Python 3.12
2128
with deprecated APIs.
@@ -164,11 +171,15 @@ access to internal read-only data of Unicode objects:
164171
.. versionadded:: 3.3
165172
166173
167-
.. c:function:: Py_UCS4 PyUnicode_READ(int kind, void *data, \
168-
Py_ssize_t index)
174+
.. c:function:: Py_UCS4 PyUnicode_READ(int kind, void *data, Py_ssize_t index)
169175
170176
Read a code point from a canonical representation *data* (as obtained with
171-
:c:func:`PyUnicode_DATA`). No checks or ready calls are performed.
177+
:c:func:`PyUnicode_DATA`). No checks are performed.
178+
179+
.. impl-detail::
180+
181+
Accept reading the trailing null character at index
182+
:c:func:`PyUnicode_GetLength`.
172183
173184
.. versionadded:: 3.3
174185
@@ -179,6 +190,11 @@ access to internal read-only data of Unicode objects:
179190
representation. This is less efficient than :c:func:`PyUnicode_READ` if you
180191
do multiple consecutive reads.
181192
193+
.. impl-detail::
194+
195+
Accept reading the trailing null character at index
196+
:c:func:`PyUnicode_GetLength`.
197+
182198
.. versionadded:: 3.3
183199
184200
@@ -716,6 +732,10 @@ APIs:
716732
717733
On error, set an exception and return ``-1``.
718734
735+
.. impl-detail::
736+
737+
The length does not count the trailing null character.
738+
719739
.. versionadded:: 3.3
720740
721741
@@ -794,6 +814,11 @@ APIs:
794814
795815
Return character on success, ``-1`` on error with an exception set.
796816
817+
.. impl-detail::
818+
819+
Do not accept reading the trailing null character at index
820+
:c:func:`PyUnicode_GetLength`.
821+
797822
.. versionadded:: 3.3
798823
799824

‎Doc/conf.py‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,3 +636,40 @@
636636
"library/threadsafety.rst": "builtins/threadsafety.rst",
637637
"library/time-complexity.rst": "builtins/time-complexity.rst",
638638
}
639+
640+
# Refuse to run the doctest builder under a mismatched Python
641+
# -----------------------------------------------------------
642+
643+
644+
def _check_doctest_interpreter(app):
645+
# The doctests are executed by the interpreter running Sphinx,
646+
# so refuse to run them if its version doesn't match the source tree.
647+
if app.builder.name != "doctest":
648+
return
649+
650+
running_version = f"{sys.version_info.major}.{sys.version_info.minor}"
651+
if running_version != version:
652+
from sphinx.util import logging as sphinx_logging
653+
654+
logger = sphinx_logging.getLogger(__name__)
655+
logger.error(
656+
"The doctests are executed by the Python running Sphinx, "
657+
"which is Python %s, however this source tree is Python %s, "
658+
"so they would test Python %s rather than the code "
659+
"documented here.\n"
660+
"Recreate the venv with a matching interpreter, for example: "
661+
"'make clean-venv && make venv PYTHON=../python'.",
662+
running_version,
663+
version,
664+
running_version,
665+
)
666+
raise SystemExit(1)
667+
668+
669+
def setup(app):
670+
app.connect("builder-inited", _check_doctest_interpreter)
671+
return {
672+
"version": "1.0",
673+
"parallel_read_safe": True,
674+
"parallel_write_safe": True,
675+
}

‎Doc/library/ctypes.rst‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,3 +3383,47 @@ Exceptions
33833383
.. availability:: Windows
33843384

33853385
.. versionadded:: 3.14
3386+
3387+
3388+
Library version
3389+
^^^^^^^^^^^^^^^
3390+
3391+
The following constants are only available if :mod:`!ctypes` was built with
3392+
libffi 3.5 or later, which is the first version providing this information.
3393+
3394+
.. data:: LIBFFI_VERSION
3395+
3396+
The version string of the libffi library that was used for building
3397+
the module, like ``'3.5.2'``.
3398+
This may be different from the libffi library actually used at runtime,
3399+
which is available as :const:`libffi_version`.
3400+
3401+
.. versionadded:: next
3402+
3403+
.. data:: libffi_version
3404+
3405+
The version string of the libffi library actually loaded by the interpreter.
3406+
3407+
.. versionadded:: next
3408+
3409+
.. data:: LIBFFI_VERSION_INFO
3410+
3411+
A named tuple containing the three components of the libffi library
3412+
version that was used for building the module:
3413+
*major*, *minor*, and *patch*.
3414+
All values are integers.
3415+
The components can also be accessed by name,
3416+
so ``ctypes.LIBFFI_VERSION_INFO[0]`` is equivalent to
3417+
``ctypes.LIBFFI_VERSION_INFO.major`` and so on.
3418+
This may be different from the libffi library actually used at runtime,
3419+
which is available as :const:`libffi_version_info`.
3420+
3421+
.. versionadded:: next
3422+
3423+
.. data:: libffi_version_info
3424+
3425+
A named tuple containing the version of the libffi library
3426+
actually loaded by the interpreter,
3427+
with the same fields as :const:`LIBFFI_VERSION_INFO`.
3428+
3429+
.. versionadded:: next

‎Doc/library/ipaddress.rst‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,10 @@ dictionaries.
519519
The interpretation is similar to an integer *address*.
520520

521521
4. A two-tuple of an address description and a netmask, where the address
522-
description is either a string, a 32-bits integer, a 4-bytes packed
523-
integer, or an existing :class:`IPv4Address` object; and the netmask is either
524-
an integer representing the prefix length (e.g. ``24``) or a string
525-
representing the prefix mask (e.g. ``255.255.255.0``).
522+
description is either a string, a 32-bit integer, a 4-byte packed
523+
integer, or an existing :class:`IPv4Address` object; and the netmask is
524+
either an integer representing the prefix length (e.g. ``24``) or a
525+
string representing the prefix mask (e.g. ``255.255.255.0``).
526526

527527
An :exc:`AddressValueError` is raised if *address* is not a valid IPv4
528528
address. A :exc:`NetmaskValueError` is raised if the mask is not valid for
@@ -769,9 +769,9 @@ dictionaries.
769769
The interpretation is similar to an integer *address*.
770770

771771
4. A two-tuple of an address description and a netmask, where the address
772-
description is either a string, a 128-bits integer, a 16-bytes packed
773-
integer, or an existing :class:`IPv6Address` object; and the netmask is an
774-
integer representing the prefix length.
772+
description is either a string, a 128-bit integer, a 16-byte packed
773+
integer, or an existing :class:`IPv6Address` object; and the netmask is
774+
an integer representing the prefix length.
775775

776776
An :exc:`AddressValueError` is raised if *address* is not a valid IPv6
777777
address. A :exc:`NetmaskValueError` is raised if the mask is not valid for

‎Doc/library/turtle.rst‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2571,6 +2571,25 @@ Public classes
25712571
* ``a.rotate(angle)`` rotation
25722572

25732573

2574+
Exceptions
2575+
==========
2576+
2577+
The :mod:`!turtle` module defines the following exception:
2578+
2579+
.. exception:: TurtleGraphicsError
2580+
2581+
Raised for invalid arguments or operations.
2582+
For example, a malformed color string:
2583+
2584+
.. doctest::
2585+
:skipif: _tkinter is None
2586+
2587+
>>> turtle.color("blau")
2588+
Traceback (most recent call last):
2589+
...
2590+
turtle.TurtleGraphicsError: bad color string: blau
2591+
2592+
25742593
.. _turtle-explanation:
25752594

25762595
Explanation

‎Doc/library/uuid.rst‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ which relays any information about the UUID's safety, using this enumeration:
4444

4545
.. class:: UUID(hex=None, bytes=None, bytes_le=None, fields=None, int=None, version=None, *, is_safe=SafeUUID.unknown)
4646

47-
Create a UUID from either a string of 32 hexadecimal digits, a string of 16
48-
bytes in big-endian order as the *bytes* argument, a string of 16 bytes in
49-
little-endian order as the *bytes_le* argument, a tuple of six integers
47+
Create a UUID from either a string of 32 hexadecimal digits, a 16-byte
48+
:class:`bytes` object in big-endian order as the *bytes* argument, a
49+
16-byte :class:`bytes` object in little-endian order as the *bytes_le*
50+
argument, a tuple of six integers
5051
(32-bit *time_low*, 16-bit *time_mid*, 16-bit *time_hi_version*,
5152
8-bit *clock_seq_hi_variant*, 8-bit *clock_seq_low*, 48-bit *node*) as the
5253
*fields* argument, or a single 128-bit integer as the *int* argument.
@@ -80,14 +81,14 @@ which relays any information about the UUID's safety, using this enumeration:
8081

8182
.. attribute:: UUID.bytes
8283

83-
The UUID as a 16-byte string (containing the six integer fields in big-endian
84-
byte order).
84+
The UUID as a 16-byte :class:`bytes` object (containing the six integer
85+
fields in big-endian byte order).
8586

8687

8788
.. attribute:: UUID.bytes_le
8889

89-
The UUID as a 16-byte string (with *time_low*, *time_mid*, and *time_hi_version*
90-
in little-endian byte order).
90+
The UUID as a 16-byte :class:`bytes` object (with *time_low*, *time_mid*,
91+
and *time_hi_version* in little-endian byte order).
9192

9293

9394
.. attribute:: UUID.fields
@@ -435,7 +436,7 @@ Here are some examples of typical usage of the :mod:`!uuid` module::
435436
>>> x.bytes
436437
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
437438

438-
>>> # make a UUID from a 16-byte string
439+
>>> # make a UUID from a 16-byte bytes object
439440
>>> uuid.UUID(bytes=x.bytes)
440441
UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
441442

0 commit comments

Comments
 (0)