Skip to content

Commit 105da17

Browse files
authored
Merge branch 'main' into correct-__ipow__-signature/86069
2 parents 45f3987 + 05e89c3 commit 105da17

Some content is hidden

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

44 files changed

+1592
-843
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ jobs:
621621
- build_wasi
622622
- build_windows
623623
- build_windows_msi
624+
- cross-build-linux
624625
- test_hypothesis
625626
- build_asan
626627
- build_tsan

.github/workflows/tail-call.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ jobs:
4141
- aarch64-apple-darwin/clang
4242
- x86_64-unknown-linux-gnu/gcc
4343
- aarch64-unknown-linux-gnu/gcc
44+
- free-threading
4445
llvm:
4546
- 19
4647
include:
@@ -65,6 +66,9 @@ jobs:
6566
- target: aarch64-unknown-linux-gnu/gcc
6667
architecture: aarch64
6768
runner: ubuntu-22.04-arm
69+
- target: free-threading
70+
architecture: x86_64
71+
runner: ubuntu-24.04
6872
steps:
6973
- uses: actions/checkout@v4
7074
with:
@@ -105,11 +109,20 @@ jobs:
105109
./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
106110
107111
- name: Native Linux (release)
108-
if: runner.os == 'Linux'
112+
if: runner.os == 'Linux' && matrix.target != 'free-threading'
109113
run: |
110114
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
111115
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
112116
CC=clang-19 ./configure --with-tail-call-interp
113117
make all --jobs 4
114118
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
115119
120+
- name: Native Linux with free-threading (release)
121+
if: matrix.target == 'free-threading'
122+
run: |
123+
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
124+
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
125+
CC=clang-19 ./configure --with-tail-call-interp --disable-gil
126+
make all --jobs 4
127+
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
128+

Doc/library/traceback.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ Module-Level Functions
257257

258258
.. versionadded:: 3.5
259259

260+
.. versionchanged:: 3.14
261+
This function previously returned a generator that would walk the stack
262+
when first iterated over. The generator returned now is the state of the
263+
stack when ``walk_stack`` is called.
264+
260265
.. function:: walk_tb(tb)
261266

262267
Walk a traceback following :attr:`~traceback.tb_next` yielding the frame and

Doc/whatsnew/3.14.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ Improved error messages
165165
error message prints the received number of values in more cases than before.
166166
(Contributed by Tushar Sadhwani in :gh:`122239`.)
167167

168-
.. code-block:: pycon
168+
.. code-block:: python
169169
170170
>>> x, y, z = 1, 2, 3, 4
171171
Traceback (most recent call last):
@@ -175,6 +175,17 @@ Improved error messages
175175
ValueError: too many values to unpack (expected 3, got 4)
176176
177177
178+
* When incorrectly closed strings are detected, the error message suggests
179+
that the string may be intended to be part of the string. (Contributed by
180+
Pablo Galindo in :gh:`88535`.)
181+
182+
.. code-block:: python
183+
184+
>>> "The interesting object "The important object" is very important"
185+
Traceback (most recent call last):
186+
SyntaxError: invalid syntax. Is this intended to be part of the string?
187+
188+
178189
.. _whatsnew314-pep741:
179190

180191
PEP 741: Python Configuration C API

Grammar/python.gram

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,9 @@ invalid_type_param:
11781178
}
11791179

11801180
invalid_expression:
1181+
| STRING a=(!STRING expression_without_invalid)+ STRING {
1182+
RAISE_SYNTAX_ERROR_KNOWN_RANGE( PyPegen_first_item(a, expr_ty), PyPegen_last_item(a, expr_ty),
1183+
"invalid syntax. Is this intended to be part of the string?") }
11811184
# !(NAME STRING) is not matched so we don't show this error with some invalid string prefixes like: kf"dsfsdf"
11821185
# Soft keywords need to also be ignored because they can be parsed as NAME NAME
11831186
| !(NAME STRING | SOFT_KEYWORD) a=disjunction b=expression_without_invalid {

Include/internal/pycore_object.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *);
7474
{ \
7575
.ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL, \
7676
.ob_flags = _Py_STATICALLY_ALLOCATED_FLAG, \
77+
.ob_gc_bits = _PyGC_BITS_DEFERRED, \
7778
.ob_type = (type) \
7879
}
7980
#else
@@ -612,7 +613,7 @@ _Py_TryIncrefCompare(PyObject **src, PyObject *op)
612613
static inline int
613614
_Py_TryIncrefCompareStackRef(PyObject **src, PyObject *op, _PyStackRef *out)
614615
{
615-
if (_Py_IsImmortal(op) || _PyObject_HasDeferredRefcount(op)) {
616+
if (_PyObject_HasDeferredRefcount(op)) {
616617
*out = (_PyStackRef){ .bits = (intptr_t)op | Py_TAG_DEFERRED };
617618
return 1;
618619
}

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ extern PyStatus _Py_PreInitializeFromConfig(
7575

7676
extern wchar_t * _Py_GetStdlibDir(void);
7777

78-
extern int _Py_HandleSystemExit(int *exitcode_p);
78+
extern int _Py_HandleSystemExitAndKeyboardInterrupt(int *exitcode_p);
7979

8080
extern PyObject* _PyErr_WriteUnraisableDefaultHook(PyObject *unraisable);
8181

Include/internal/pycore_stackref.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ PyStackRef_FromPyObjectNew(PyObject *obj)
219219
// Make sure we don't take an already tagged value.
220220
assert(((uintptr_t)obj & Py_TAG_BITS) == 0);
221221
assert(obj != NULL);
222-
if (_Py_IsImmortal(obj) || _PyObject_HasDeferredRefcount(obj)) {
222+
if (_PyObject_HasDeferredRefcount(obj)) {
223223
return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_DEFERRED };
224224
}
225225
else {

Lib/sysconfig/__init__.py

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -666,34 +666,34 @@ def get_platform():
666666

667667
# Set for cross builds explicitly
668668
if "_PYTHON_HOST_PLATFORM" in os.environ:
669-
return os.environ["_PYTHON_HOST_PLATFORM"]
670-
671-
# Try to distinguish various flavours of Unix
672-
osname, host, release, version, machine = os.uname()
673-
674-
# Convert the OS name to lowercase, remove '/' characters, and translate
675-
# spaces (for "Power Macintosh")
676-
osname = osname.lower().replace('/', '')
677-
machine = machine.replace(' ', '_')
678-
machine = machine.replace('/', '-')
679-
680-
if osname[:5] == "linux":
681-
if sys.platform == "android":
682-
osname = "android"
683-
release = get_config_var("ANDROID_API_LEVEL")
684-
685-
# Wheel tags use the ABI names from Android's own tools.
686-
machine = {
687-
"x86_64": "x86_64",
688-
"i686": "x86",
689-
"aarch64": "arm64_v8a",
690-
"armv7l": "armeabi_v7a",
691-
}[machine]
692-
else:
693-
# At least on Linux/Intel, 'machine' is the processor --
694-
# i386, etc.
695-
# XXX what about Alpha, SPARC, etc?
696-
return f"{osname}-{machine}"
669+
osname, _, machine = os.environ["_PYTHON_HOST_PLATFORM"].partition('-')
670+
release = None
671+
else:
672+
# Try to distinguish various flavours of Unix
673+
osname, host, release, version, machine = os.uname()
674+
675+
# Convert the OS name to lowercase, remove '/' characters, and translate
676+
# spaces (for "Power Macintosh")
677+
osname = osname.lower().replace('/', '')
678+
machine = machine.replace(' ', '_')
679+
machine = machine.replace('/', '-')
680+
681+
if osname == "android" or sys.platform == "android":
682+
osname = "android"
683+
release = get_config_var("ANDROID_API_LEVEL")
684+
685+
# Wheel tags use the ABI names from Android's own tools.
686+
machine = {
687+
"x86_64": "x86_64",
688+
"i686": "x86",
689+
"aarch64": "arm64_v8a",
690+
"armv7l": "armeabi_v7a",
691+
}[machine]
692+
elif osname == "linux":
693+
# At least on Linux/Intel, 'machine' is the processor --
694+
# i386, etc.
695+
# XXX what about Alpha, SPARC, etc?
696+
return f"{osname}-{machine}"
697697
elif osname[:5] == "sunos":
698698
if release[0] >= "5": # SunOS 5 == Solaris 2
699699
osname = "solaris"
@@ -725,7 +725,7 @@ def get_platform():
725725
get_config_vars(),
726726
osname, release, machine)
727727

728-
return f"{osname}-{release}-{machine}"
728+
return '-'.join(map(str, filter(None, (osname, release, machine))))
729729

730730

731731
def get_python_version():

Lib/test/test_ast/test_ast.py

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,46 +3239,6 @@ def test_folding_tuple(self):
32393239

32403240
self.assert_ast(code, non_optimized_target, optimized_target)
32413241

3242-
def test_folding_comparator(self):
3243-
code = "1 %s %s1%s"
3244-
operators = [("in", ast.In()), ("not in", ast.NotIn())]
3245-
braces = [
3246-
("[", "]", ast.List, (1,)),
3247-
("{", "}", ast.Set, frozenset({1})),
3248-
]
3249-
for left, right, non_optimized_comparator, optimized_comparator in braces:
3250-
for op, node in operators:
3251-
non_optimized_target = self.wrap_expr(ast.Compare(
3252-
left=ast.Constant(1), ops=[node],
3253-
comparators=[non_optimized_comparator(elts=[ast.Constant(1)])]
3254-
))
3255-
optimized_target = self.wrap_expr(ast.Compare(
3256-
left=ast.Constant(1), ops=[node],
3257-
comparators=[ast.Constant(value=optimized_comparator)]
3258-
))
3259-
self.assert_ast(code % (op, left, right), non_optimized_target, optimized_target)
3260-
3261-
def test_folding_iter(self):
3262-
code = "for _ in %s1%s: pass"
3263-
braces = [
3264-
("[", "]", ast.List, (1,)),
3265-
("{", "}", ast.Set, frozenset({1})),
3266-
]
3267-
3268-
for left, right, ast_cls, optimized_iter in braces:
3269-
non_optimized_target = self.wrap_statement(ast.For(
3270-
target=ast.Name(id="_", ctx=ast.Store()),
3271-
iter=ast_cls(elts=[ast.Constant(1)]),
3272-
body=[ast.Pass()]
3273-
))
3274-
optimized_target = self.wrap_statement(ast.For(
3275-
target=ast.Name(id="_", ctx=ast.Store()),
3276-
iter=ast.Constant(value=optimized_iter),
3277-
body=[ast.Pass()]
3278-
))
3279-
3280-
self.assert_ast(code % (left, right), non_optimized_target, optimized_target)
3281-
32823242
def test_folding_type_param_in_function_def(self):
32833243
code = "def foo[%s = 1 + 1](): pass"
32843244

0 commit comments

Comments
 (0)