Skip to content

Commit e19457e

Browse files
authored
Merge branch 'main' into chain_next
2 parents 07881ac + c765683 commit e19457e

40 files changed

Lines changed: 933 additions & 521 deletions

.github/workflows/build.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,14 @@ jobs:
527527
config_hash: ${{ needs.build-context.outputs.config-hash }}
528528
free-threading: ${{ matrix.free-threading }}
529529

530+
build-ubsan:
531+
name: Undefined behavior sanitizer
532+
needs: build-context
533+
if: needs.build-context.outputs.run-tests == 'true'
534+
uses: ./.github/workflows/reusable-ubsan.yml
535+
with:
536+
config_hash: ${{ needs.build-context.outputs.config-hash }}
537+
530538
cross-build-linux:
531539
name: Cross build Linux
532540
runs-on: ubuntu-latest
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Reusable Undefined Behavior Sanitizer
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
config_hash:
7+
required: true
8+
type: string
9+
10+
env:
11+
FORCE_COLOR: 1
12+
13+
jobs:
14+
build-ubsan-reusable:
15+
name: 'Undefined behavior sanitizer'
16+
runs-on: ubuntu-24.04
17+
timeout-minutes: 60
18+
steps:
19+
- uses: actions/checkout@v4
20+
with:
21+
persist-credentials: false
22+
- name: Runner image version
23+
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
24+
- name: Restore config.cache
25+
uses: actions/cache@v4
26+
with:
27+
path: config.cache
28+
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ inputs.config_hash }}
29+
- name: Install dependencies
30+
run: |
31+
sudo ./.github/workflows/posix-deps-apt.sh
32+
# Install clang-20
33+
wget https://apt.llvm.org/llvm.sh
34+
chmod +x llvm.sh
35+
sudo ./llvm.sh 20
36+
- name: UBSAN option setup
37+
run: |
38+
echo "UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1" >> "$GITHUB_ENV"
39+
echo "CC=clang" >> "$GITHUB_ENV"
40+
echo "CXX=clang++" >> "$GITHUB_ENV"
41+
- name: Add ccache to PATH
42+
run: |
43+
echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
44+
- name: Configure ccache action
45+
uses: hendrikmuhs/ccache-action@v1.2
46+
with:
47+
save: ${{ github.event_name == 'push' }}
48+
max-size: "200M"
49+
- name: Configure CPython
50+
run: >-
51+
./configure
52+
--config-cache
53+
--with-undefined-behavior-sanitizer
54+
--with-pydebug
55+
- name: Set up UBSAN log after configuration
56+
run: |
57+
echo "UBSAN_OPTIONS=${UBSAN_OPTIONS}:log_path=${GITHUB_WORKSPACE}/ubsan_log" >> "$GITHUB_ENV"
58+
- name: Build CPython
59+
run: make -j4
60+
- name: Display build info
61+
run: make pythoninfo
62+
- name: Tests
63+
run: ./python -m test -j4
64+
- name: Display UBSAN logs
65+
if: always()
66+
run: find "${GITHUB_WORKSPACE}" -name 'ubsan_log.*' | xargs head -n 1000
67+
- name: Archive UBSAN logs
68+
if: always()
69+
uses: actions/upload-artifact@v4
70+
with:
71+
name: >-
72+
ubsan-logs
73+
path: ubsan_log.*
74+
if-no-files-found: ignore

Doc/c-api/type.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ Type Objects
282282
and other places where a method's defining class cannot be passed using the
283283
:c:type:`PyCMethod` calling convention.
284284
285+
The returned reference is :term:`borrowed <borrowed reference>` from *type*,
286+
and will be valid as long as you hold a reference to *type*.
287+
Do not release it with :c:func:`Py_DECREF` or similar.
288+
285289
.. versionadded:: 3.11
286290
287291
.. c:function:: int PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)

Doc/data/refcounts.dat

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,10 @@ PyType_GetFlags:PyTypeObject*:type:0:
23852385
PyType_GetName:PyObject*::+1:
23862386
PyType_GetName:PyTypeObject*:type:0:
23872387

2388+
PyType_GetModuleByDef:PyObject*::0:
2389+
PyType_GetModuleByDef:PyTypeObject*:type:0:
2390+
PyType_GetModuleByDef:PyModuleDef*:def::
2391+
23882392
PyType_GetQualName:PyObject*::+1:
23892393
PyType_GetQualName:PyTypeObject*:type:0:
23902394

Doc/howto/regex.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,9 @@ extension. This regular expression matches ``foo.bar`` and
10161016
Now, consider complicating the problem a bit; what if you want to match
10171017
filenames where the extension is not ``bat``? Some incorrect attempts:
10181018

1019-
``.*[.][^b].*$`` The first attempt above tries to exclude ``bat`` by requiring
1019+
``.*[.][^b].*$``
1020+
1021+
The first attempt above tries to exclude ``bat`` by requiring
10201022
that the first character of the extension is not a ``b``. This is wrong,
10211023
because the pattern also doesn't match ``foo.bar``.
10221024

@@ -1043,7 +1045,9 @@ confusing.
10431045

10441046
A negative lookahead cuts through all this confusion:
10451047

1046-
``.*[.](?!bat$)[^.]*$`` The negative lookahead means: if the expression ``bat``
1048+
``.*[.](?!bat$)[^.]*$``
1049+
1050+
The negative lookahead means: if the expression ``bat``
10471051
doesn't match at this point, try the rest of the pattern; if ``bat$`` does
10481052
match, the whole pattern will fail. The trailing ``$`` is required to ensure
10491053
that something like ``sample.batch``, where the extension only starts with

Doc/library/concurrent.futures.rst

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ Each worker's interpreter is isolated from all the other interpreters.
265265
"Isolated" means each interpreter has its own runtime state and
266266
operates completely independently. For example, if you redirect
267267
:data:`sys.stdout` in one interpreter, it will not be automatically
268-
redirected any other interpreter. If you import a module in one
268+
redirected to any other interpreter. If you import a module in one
269269
interpreter, it is not automatically imported in any other. You
270270
would need to import the module separately in interpreter where
271271
you need it. In fact, each module imported in an interpreter is
@@ -287,7 +287,7 @@ efficient alternative is to serialize with :mod:`pickle` and then send
287287
the bytes over a shared :mod:`socket <socket>` or
288288
:func:`pipe <os.pipe>`.
289289

290-
.. class:: InterpreterPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=(), shared=None)
290+
.. class:: InterpreterPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=())
291291

292292
A :class:`ThreadPoolExecutor` subclass that executes calls asynchronously
293293
using a pool of at most *max_workers* threads. Each thread runs
@@ -304,32 +304,17 @@ the bytes over a shared :mod:`socket <socket>` or
304304
and *initargs* using :mod:`pickle` when sending them to the worker's
305305
interpreter.
306306

307-
.. note::
308-
Functions defined in the ``__main__`` module cannot be pickled
309-
and thus cannot be used.
310-
311307
.. note::
312308
The executor may replace uncaught exceptions from *initializer*
313309
with :class:`~concurrent.futures.interpreter.ExecutionFailed`.
314310

315-
The optional *shared* argument is a :class:`dict` of objects that all
316-
interpreters in the pool share. The *shared* items are added to each
317-
interpreter's ``__main__`` module. Not all objects are shareable.
318-
Shareable objects include the builtin singletons, :class:`str`
319-
and :class:`bytes`, and :class:`memoryview`. See :pep:`734`
320-
for more info.
321-
322311
Other caveats from parent :class:`ThreadPoolExecutor` apply here.
323312

324313
:meth:`~Executor.submit` and :meth:`~Executor.map` work like normal,
325314
except the worker serializes the callable and arguments using
326315
:mod:`pickle` when sending them to its interpreter. The worker
327316
likewise serializes the return value when sending it back.
328317

329-
.. note::
330-
Functions defined in the ``__main__`` module cannot be pickled
331-
and thus cannot be used.
332-
333318
When a worker's current task raises an uncaught exception, the worker
334319
always tries to preserve the exception as-is. If that is successful
335320
then it also sets the ``__cause__`` to a corresponding

Doc/library/pkgutil.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ support.
6969

7070
Yield :term:`finder` objects for the given module name.
7171

72-
If fullname contains a ``'.'``, the finders will be for the package
73-
containing fullname, otherwise they will be all registered top level
72+
If *fullname* contains a ``'.'``, the finders will be for the package
73+
containing *fullname*, otherwise they will be all registered top level
7474
finders (i.e. those on both :data:`sys.meta_path` and :data:`sys.path_hooks`).
7575

7676
If the named module is in a package, that package is imported as a side

Doc/library/shutil.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ Directory and files operations
327327
The deprecated *onerror* is similar to *onexc*, except that the third
328328
parameter it receives is the tuple returned from :func:`sys.exc_info`.
329329

330+
.. seealso::
331+
:ref:`shutil-rmtree-example` for an example of handling the removal
332+
of a directory tree that contains read-only files.
333+
330334
.. audit-event:: shutil.rmtree path,dir_fd shutil.rmtree
331335

332336
.. versionchanged:: 3.3

Doc/library/zoneinfo.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ The ``ZoneInfo`` class
195195

196196
The ``ZoneInfo`` class has two alternate constructors:
197197

198-
.. classmethod:: ZoneInfo.from_file(fobj, /, key=None)
198+
.. classmethod:: ZoneInfo.from_file(file_obj, /, key=None)
199199

200200
Constructs a ``ZoneInfo`` object from a file-like object returning bytes
201201
(e.g. a file opened in binary mode or an :class:`io.BytesIO` object).
@@ -325,7 +325,7 @@ The behavior of a ``ZoneInfo`` file depends on how it was constructed:
325325
>>> a is b
326326
False
327327
328-
3. ``ZoneInfo.from_file(fobj, /, key=None)``: When constructed from a file, the
328+
3. ``ZoneInfo.from_file(file_obj, /, key=None)``: When constructed from a file, the
329329
``ZoneInfo`` object raises an exception on pickling. If an end user wants to
330330
pickle a ``ZoneInfo`` constructed from a file, it is recommended that they
331331
use a wrapper type or a custom serialization function: either serializing by

Include/internal/pycore_long.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ PyAPI_DATA(PyObject*) _PyLong_Rshift(PyObject *, int64_t);
112112
// Export for 'math' shared extension
113113
PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, int64_t);
114114

115-
PyAPI_FUNC(PyObject*) _PyCompactLong_Add(PyLongObject *left, PyLongObject *right);
116-
PyAPI_FUNC(PyObject*) _PyCompactLong_Multiply(PyLongObject *left, PyLongObject *right);
117-
PyAPI_FUNC(PyObject*) _PyCompactLong_Subtract(PyLongObject *left, PyLongObject *right);
115+
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Add(PyLongObject *left, PyLongObject *right);
116+
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Multiply(PyLongObject *left, PyLongObject *right);
117+
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Subtract(PyLongObject *left, PyLongObject *right);
118118

119119
// Export for 'binascii' shared extension.
120120
PyAPI_DATA(unsigned char) _PyLong_DigitValue[256];
@@ -213,7 +213,6 @@ _PyLong_BothAreCompact(const PyLongObject* a, const PyLongObject* b) {
213213
assert(PyLong_Check(b));
214214
return (a->long_value.lv_tag | b->long_value.lv_tag) < (2 << NON_SIZE_BITS);
215215
}
216-
217216
static inline bool
218217
_PyLong_IsZero(const PyLongObject *op)
219218
{
@@ -313,6 +312,12 @@ _PyLong_FlipSign(PyLongObject *op) {
313312
#define _PyLong_FALSE_TAG TAG_FROM_SIGN_AND_SIZE(0, 0)
314313
#define _PyLong_TRUE_TAG TAG_FROM_SIGN_AND_SIZE(1, 1)
315314

315+
static inline int
316+
_PyLong_CheckExactAndCompact(PyObject *op)
317+
{
318+
return PyLong_CheckExact(op) && _PyLong_IsCompact((const PyLongObject *)op);
319+
}
320+
316321
#ifdef __cplusplus
317322
}
318323
#endif

0 commit comments

Comments
 (0)