Skip to content

Commit 3b25dc3

Browse files
committed
Merge branch 'main' into use-stackrefs
2 parents 375bd16 + 2498c22 commit 3b25dc3

File tree

81 files changed

+3929
-3218
lines changed

Some content is hidden

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

81 files changed

+3929
-3218
lines changed

Doc/c-api/arg.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,10 @@ Building values
645645
``n`` (:class:`int`) [:c:type:`Py_ssize_t`]
646646
Convert a C :c:type:`Py_ssize_t` to a Python integer.
647647
648+
``p`` (:class:`bool`) [int]
649+
Convert a C :c:expr:`int` to a Python :class:`bool` object.
650+
.. versionadded:: 3.14
651+
648652
``c`` (:class:`bytes` of length 1) [char]
649653
Convert a C :c:expr:`int` representing a byte to a Python :class:`bytes` object of
650654
length 1.

Doc/c-api/exceptions.rst

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -921,11 +921,7 @@ because the :ref:`call protocol <call>` takes care of recursion handling.
921921
922922
Marks a point where a recursive C-level call is about to be performed.
923923
924-
If :c:macro:`!USE_STACKCHECK` is defined, this function checks if the OS
925-
stack overflowed using :c:func:`PyOS_CheckStack`. If this is the case, it
926-
sets a :exc:`MemoryError` and returns a nonzero value.
927-
928-
The function then checks if the recursion limit is reached. If this is the
924+
The function then checks if the stack limit is reached. If this is the
929925
case, a :exc:`RecursionError` is set and a nonzero value is returned.
930926
Otherwise, zero is returned.
931927

Doc/library/dis.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,23 +97,23 @@ The following options are accepted:
9797

9898
.. program:: dis
9999

100-
.. cmdoption:: -h, --help
100+
.. option:: -h, --help
101101

102102
Display usage and exit.
103103

104-
.. cmdoption:: -C, --show-caches
104+
.. option:: -C, --show-caches
105105

106106
Show inline caches.
107107

108-
.. cmdoption:: -O, --show-offsets
108+
.. option:: -O, --show-offsets
109109

110110
Show offsets of instructions.
111111

112-
.. cmdoption:: -P, --show-positions
112+
.. option:: -P, --show-positions
113113

114114
Show positions of instructions in the source code.
115115

116-
.. cmdoption:: -S, --specialized
116+
.. option:: -S, --specialized
117117

118118
Show specialized bytecode.
119119

Doc/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# won't suddenly cause build failures. Updating the version is fine as long
88
# as no warnings are raised by doing so.
99
# Keep this version in sync with ``Doc/conf.py``.
10-
sphinx~=8.1.0
10+
sphinx~=8.2.0
1111

1212
blurb
1313

Doc/whatsnew/3.14.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,31 @@ Improved error messages
175175
ValueError: too many values to unpack (expected 3, got 4)
176176
177177
178+
* If a statement (:keyword:`pass`, :keyword:`del`, :keyword:`return`,
179+
:keyword:`yield`, :keyword:`raise`, :keyword:`break`, :keyword:`continue`,
180+
:keyword:`assert`, :keyword:`import`, :keyword:`from`) is passed to the
181+
:ref:`if_expr` after :keyword:`else`, or one of :keyword:`pass`,
182+
:keyword:`break`, or :keyword:`continue` is passed before :keyword:`if`, then the
183+
error message highlights where the :token:`~python-grammar:expression` is
184+
required. (Contributed by Sergey Miryanov in :gh:`129515`.)
185+
186+
.. code-block:: pycon
187+
188+
>>> x = 1 if True else pass
189+
Traceback (most recent call last):
190+
File "<string>", line 1
191+
x = 1 if True else pass
192+
^^^^
193+
SyntaxError: expected expression after 'else', but statement is given
194+
195+
>>> x = continue if True else break
196+
Traceback (most recent call last):
197+
File "<string>", line 1
198+
x = continue if True else break
199+
^^^^^^^^
200+
SyntaxError: expected expression before 'if', but statement is given
201+
202+
178203
* When incorrectly closed strings are detected, the error message suggests
179204
that the string may be intended to be part of the string. (Contributed by
180205
Pablo Galindo in :gh:`88535`.)
@@ -1433,6 +1458,10 @@ New features
14331458
and get an attribute of the module.
14341459
(Contributed by Victor Stinner in :gh:`128911`.)
14351460

1461+
* Add support for a new ``p`` format unit in :c:func:`Py_BuildValue` that allows to
1462+
take a C integer and produce a Python :class:`bool` object. (Contributed by
1463+
Pablo Galindo in :issue:`45325`.)
1464+
14361465

14371466
Limited C API changes
14381467
---------------------

Grammar/python.gram

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,12 @@ simple_stmt[stmt_ty] (memo):
117117
| &'return' return_stmt
118118
| &('import' | 'from') import_stmt
119119
| &'raise' raise_stmt
120-
| 'pass' { _PyAST_Pass(EXTRA) }
120+
| &'pass' pass_stmt
121121
| &'del' del_stmt
122122
| &'yield' yield_stmt
123123
| &'assert' assert_stmt
124-
| 'break' { _PyAST_Break(EXTRA) }
125-
| 'continue' { _PyAST_Continue(EXTRA) }
124+
| &'break' break_stmt
125+
| &'continue' continue_stmt
126126
| &'global' global_stmt
127127
| &'nonlocal' nonlocal_stmt
128128

@@ -181,6 +181,15 @@ raise_stmt[stmt_ty]:
181181
| 'raise' a=expression b=['from' z=expression { z }] { _PyAST_Raise(a, b, EXTRA) }
182182
| 'raise' { _PyAST_Raise(NULL, NULL, EXTRA) }
183183

184+
pass_stmt[stmt_ty]:
185+
| 'pass' { _PyAST_Pass(EXTRA) }
186+
187+
break_stmt[stmt_ty]:
188+
| 'break' { _PyAST_Break(EXTRA) }
189+
190+
continue_stmt[stmt_ty]:
191+
| 'continue' { _PyAST_Continue(EXTRA) }
192+
184193
global_stmt[stmt_ty]: 'global' a[asdl_expr_seq*]=','.NAME+ {
185194
_PyAST_Global(CHECK(asdl_identifier_seq*, _PyPegen_map_names_to_ids(p, a)), EXTRA) }
186195

@@ -1187,6 +1196,10 @@ invalid_expression:
11871196
_PyPegen_check_legacy_stmt(p, a) ? NULL : p->tokens[p->mark-1]->level == 0 ? NULL :
11881197
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Perhaps you forgot a comma?") }
11891198
| a=disjunction 'if' b=disjunction !('else'|':') { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "expected 'else' after 'if' expression") }
1199+
| a=disjunction 'if' b=disjunction 'else' !expression {
1200+
RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("expected expression after 'else', but statement is given") }
1201+
| a[stmt_ty]=(pass_stmt|break_stmt|continue_stmt) 'if' b=disjunction 'else' c=simple_stmt {
1202+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION (a, "expected expression before 'if', but statement is given") }
11901203
| a='lambda' [lambda_params] b=':' &FSTRING_MIDDLE {
11911204
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "f-string: lambda expressions are not allowed without parentheses") }
11921205

Include/cpython/object.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -487,18 +487,19 @@ PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(PyThreadState *tstate);
487487
* we have headroom above the trigger limit */
488488
#define Py_TRASHCAN_HEADROOM 50
489489

490+
/* Helper function for Py_TRASHCAN_BEGIN */
491+
PyAPI_FUNC(int) _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count);
492+
490493
#define Py_TRASHCAN_BEGIN(op, dealloc) \
491494
do { \
492495
PyThreadState *tstate = PyThreadState_Get(); \
493-
if (tstate->c_recursion_remaining <= Py_TRASHCAN_HEADROOM && Py_TYPE(op)->tp_dealloc == (destructor)dealloc) { \
496+
if (_Py_ReachedRecursionLimitWithMargin(tstate, 1) && Py_TYPE(op)->tp_dealloc == (destructor)dealloc) { \
494497
_PyTrash_thread_deposit_object(tstate, (PyObject *)op); \
495498
break; \
496-
} \
497-
tstate->c_recursion_remaining--;
499+
}
498500
/* The body of the deallocator is here. */
499501
#define Py_TRASHCAN_END \
500-
tstate->c_recursion_remaining++; \
501-
if (tstate->delete_later && tstate->c_recursion_remaining > (Py_TRASHCAN_HEADROOM*2)) { \
502+
if (tstate->delete_later && !_Py_ReachedRecursionLimitWithMargin(tstate, 2)) { \
502503
_PyTrash_thread_destroy_chain(tstate); \
503504
} \
504505
} while (0);

Include/cpython/pystate.h

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ struct _ts {
112112
int py_recursion_remaining;
113113
int py_recursion_limit;
114114

115-
int c_recursion_remaining;
115+
int c_recursion_remaining; /* Retained for backwards compatibility. Do not use */
116116
int recursion_headroom; /* Allow 50 more calls to handle any errors. */
117117

118118
/* 'tracing' keeps track of the execution depth when tracing/profiling.
@@ -202,36 +202,7 @@ struct _ts {
202202
PyObject *threading_local_sentinel;
203203
};
204204

205-
#ifdef Py_DEBUG
206-
// A debug build is likely built with low optimization level which implies
207-
// higher stack memory usage than a release build: use a lower limit.
208-
# define Py_C_RECURSION_LIMIT 500
209-
#elif defined(__s390x__)
210-
# define Py_C_RECURSION_LIMIT 800
211-
#elif defined(_WIN32) && defined(_M_ARM64)
212-
# define Py_C_RECURSION_LIMIT 1000
213-
#elif defined(_WIN32)
214-
# define Py_C_RECURSION_LIMIT 3000
215-
#elif defined(__ANDROID__)
216-
// On an ARM64 emulator, API level 34 was OK with 10000, but API level 21
217-
// crashed in test_compiler_recursion_limit.
218-
# define Py_C_RECURSION_LIMIT 3000
219-
#elif defined(_Py_ADDRESS_SANITIZER)
220-
# define Py_C_RECURSION_LIMIT 4000
221-
#elif defined(__sparc__)
222-
// test_descr crashed on sparc64 with >7000 but let's keep a margin of error.
223-
# define Py_C_RECURSION_LIMIT 4000
224-
#elif defined(__wasi__)
225-
// Based on wasmtime 16.
226-
# define Py_C_RECURSION_LIMIT 5000
227-
#elif defined(__hppa__) || defined(__powerpc64__)
228-
// test_descr crashed with >8000 but let's keep a margin of error.
229-
# define Py_C_RECURSION_LIMIT 5000
230-
#else
231-
// This value is duplicated in Lib/test/support/__init__.py
232-
# define Py_C_RECURSION_LIMIT 10000
233-
#endif
234-
205+
# define Py_C_RECURSION_LIMIT 5000
235206

236207
/* other API */
237208

@@ -246,7 +217,6 @@ _PyThreadState_UncheckedGet(void)
246217
return PyThreadState_GetUnchecked();
247218
}
248219

249-
250220
// Disable tracing and profiling.
251221
PyAPI_FUNC(void) PyThreadState_EnterTracing(PyThreadState *tstate);
252222

Include/internal/pycore_ceval.h

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -193,18 +193,12 @@ extern void _PyEval_DeactivateOpCache(void);
193193

194194
/* --- _Py_EnterRecursiveCall() ----------------------------------------- */
195195

196-
#ifdef USE_STACKCHECK
197-
/* With USE_STACKCHECK macro defined, trigger stack checks in
198-
_Py_CheckRecursiveCall() on every 64th call to _Py_EnterRecursiveCall. */
199196
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
200-
return (tstate->c_recursion_remaining-- < 0
201-
|| (tstate->c_recursion_remaining & 63) == 0);
197+
char here;
198+
uintptr_t here_addr = (uintptr_t)&here;
199+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
200+
return here_addr < _tstate->c_stack_soft_limit;
202201
}
203-
#else
204-
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
205-
return tstate->c_recursion_remaining-- < 0;
206-
}
207-
#endif
208202

209203
// Export for '_json' shared extension, used via _Py_EnterRecursiveCall()
210204
// static inline function.
@@ -220,23 +214,31 @@ static inline int _Py_EnterRecursiveCallTstate(PyThreadState *tstate,
220214
return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where));
221215
}
222216

223-
static inline void _Py_EnterRecursiveCallTstateUnchecked(PyThreadState *tstate) {
224-
assert(tstate->c_recursion_remaining > 0);
225-
tstate->c_recursion_remaining--;
226-
}
227-
228217
static inline int _Py_EnterRecursiveCall(const char *where) {
229218
PyThreadState *tstate = _PyThreadState_GET();
230219
return _Py_EnterRecursiveCallTstate(tstate, where);
231220
}
232221

233-
static inline void _Py_LeaveRecursiveCallTstate(PyThreadState *tstate) {
234-
tstate->c_recursion_remaining++;
222+
static inline void _Py_LeaveRecursiveCallTstate(PyThreadState *tstate) {
223+
(void)tstate;
224+
}
225+
226+
PyAPI_FUNC(void) _Py_InitializeRecursionLimits(PyThreadState *tstate);
227+
228+
static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
229+
char here;
230+
uintptr_t here_addr = (uintptr_t)&here;
231+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
232+
if (here_addr > _tstate->c_stack_soft_limit) {
233+
return 0;
234+
}
235+
if (_tstate->c_stack_hard_limit == 0) {
236+
_Py_InitializeRecursionLimits(tstate);
237+
}
238+
return here_addr <= _tstate->c_stack_soft_limit;
235239
}
236240

237241
static inline void _Py_LeaveRecursiveCall(void) {
238-
PyThreadState *tstate = _PyThreadState_GET();
239-
_Py_LeaveRecursiveCallTstate(tstate);
240242
}
241243

242244
extern struct _PyInterpreterFrame* _PyEval_GetFrame(void);
@@ -327,7 +329,6 @@ void _Py_unset_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit);
327329

328330
PyAPI_FUNC(_PyStackRef) _PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyStackRef right, double value);
329331

330-
331332
#ifdef __cplusplus
332333
}
333334
#endif

Include/internal/pycore_symtable.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ struct symtable {
8282
PyObject *st_private; /* name of current class or NULL */
8383
_PyFutureFeatures *st_future; /* module's future features that affect
8484
the symbol table */
85-
int recursion_depth; /* current recursion depth */
86-
int recursion_limit; /* recursion limit */
8785
};
8886

8987
typedef struct _symtable_entry {

0 commit comments

Comments
 (0)