Skip to content

Commit b2bf829

Browse files
authored
Merge branch 'main' into remove-JUMP_IF_FALSE_OR_POP
2 parents 12595f6 + d1a89ce commit b2bf829

32 files changed

+334
-364
lines changed

Doc/c-api/object.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,15 @@ Object Protocol
179179
If *o1* and *o2* are the same object, :c:func:`PyObject_RichCompareBool`
180180
will always return ``1`` for :const:`Py_EQ` and ``0`` for :const:`Py_NE`.
181181
182+
.. c:function:: PyObject* PyObject_Format(PyObject *obj, PyObject *format_spec)
183+
184+
Format *obj* using *format_spec*. This is equivalent to the Python
185+
expression ``format(obj, format_spec)``.
186+
187+
*format_spec* may be ``NULL``. In this case the call is equivalent
188+
to ``format(obj)``.
189+
Returns the formatted string on success, ``NULL`` on failure.
190+
182191
.. c:function:: PyObject* PyObject_Repr(PyObject *o)
183192
184193
.. index:: builtin: repr

Doc/library/itertools.rst

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ loops that truncate the stream.
195195
if n < 1:
196196
raise ValueError('n must be at least one')
197197
it = iter(iterable)
198-
while (batch := tuple(islice(it, n))):
198+
while batch := tuple(islice(it, n)):
199199
yield batch
200200

201201
.. versionadded:: 3.12
@@ -866,6 +866,17 @@ which incur interpreter overhead.
866866
window.append(x)
867867
yield math.sumprod(kernel, window)
868868

869+
def polynomial_from_roots(roots):
870+
"""Compute a polynomial's coefficients from its roots.
871+
872+
(x - 5) (x + 4) (x - 3) expands to: x³ -4x² -17x + 60
873+
"""
874+
# polynomial_from_roots([5, -4, 3]) --> [1, -4, -17, 60]
875+
expansion = [1]
876+
for r in roots:
877+
expansion = convolve(expansion, (1, -r))
878+
return list(expansion)
879+
869880
def polynomial_eval(coefficients, x):
870881
"""Evaluate a polynomial at a specific value.
871882

@@ -876,20 +887,8 @@ which incur interpreter overhead.
876887
n = len(coefficients)
877888
if n == 0:
878889
return x * 0 # coerce zero to the type of x
879-
powers = map(pow, repeat(x), range(n))
880-
return math.sumprod(reversed(coefficients), powers)
881-
882-
def polynomial_from_roots(roots):
883-
"""Compute a polynomial's coefficients from its roots.
884-
885-
(x - 5) (x + 4) (x - 3) expands to: x³ -4x² -17x + 60
886-
"""
887-
# polynomial_from_roots([5, -4, 3]) --> [1, -4, -17, 60]
888-
roots = list(map(operator.neg, roots))
889-
return [
890-
sum(map(math.prod, combinations(roots, k)))
891-
for k in range(len(roots) + 1)
892-
]
890+
powers = map(pow, repeat(x), reversed(range(n)))
891+
return math.sumprod(coefficients, powers)
893892

894893
def iter_index(iterable, value, start=0):
895894
"Return indices where a value occurs in a sequence or iterable."

Doc/library/os.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3951,7 +3951,7 @@ to be ignored.
39513951

39523952
.. note::
39533953

3954-
The standard way to exit is ``sys.exit(n)``. :func:`_exit` should
3954+
The standard way to exit is :func:`sys.exit(n) <sys.exit>`. :func:`!_exit` should
39553955
normally only be used in the child process after a :func:`fork`.
39563956

39573957
The following exit codes are defined and can be used with :func:`_exit`,

Include/cpython/initconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PyAPI_FUNC(PyStatus) PyStatus_Exit(int exitcode);
2525
PyAPI_FUNC(int) PyStatus_IsError(PyStatus err);
2626
PyAPI_FUNC(int) PyStatus_IsExit(PyStatus err);
2727
PyAPI_FUNC(int) PyStatus_Exception(PyStatus err);
28+
PyAPI_FUNC(PyObject *) _PyErr_SetFromPyStatus(PyStatus status);
2829

2930
/* --- PyWideStringList ------------------------------------------------ */
3031

Include/cpython/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ PyAPI_FUNC(void) _Py_ForgetReference(PyObject *);
1515
PyAPI_FUNC(Py_ssize_t) _Py_GetGlobalRefTotal(void);
1616
# define _Py_GetRefTotal() _Py_GetGlobalRefTotal()
1717
PyAPI_FUNC(Py_ssize_t) _Py_GetLegacyRefTotal(void);
18+
PyAPI_FUNC(Py_ssize_t) _PyInterpreterState_GetRefTotal(PyInterpreterState *);
1819
#endif
1920

2021

Include/cpython/pyerrors.h

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,24 +116,6 @@ PyAPI_FUNC(int) _PyException_AddNote(
116116
PyObject *exc,
117117
PyObject *note);
118118

119-
/* Helper that attempts to replace the current exception with one of the
120-
* same type but with a prefix added to the exception text. The resulting
121-
* exception description looks like:
122-
*
123-
* prefix (exc_type: original_exc_str)
124-
*
125-
* Only some exceptions can be safely replaced. If the function determines
126-
* it isn't safe to perform the replacement, it will leave the original
127-
* unmodified exception in place.
128-
*
129-
* Returns a borrowed reference to the new exception (if any), NULL if the
130-
* existing exception was left in place.
131-
*/
132-
PyAPI_FUNC(PyObject *) _PyErr_TrySetFromCause(
133-
const char *prefix_format, /* ASCII-encoded string */
134-
...
135-
);
136-
137119
/* In signalmodule.c */
138120

139121
int PySignal_SetWakeupFd(int fd);

Include/cpython/pylifecycle.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,6 @@ PyAPI_FUNC(int) _Py_CoerceLegacyLocale(int warn);
6262
PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn);
6363
PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category);
6464

65-
PyAPI_FUNC(PyThreadState *) _Py_NewInterpreterFromConfig(
66-
const _PyInterpreterConfig *);
65+
PyAPI_FUNC(PyStatus) _Py_NewInterpreterFromConfig(
66+
PyThreadState **tstate_p,
67+
const _PyInterpreterConfig *config);

Include/internal/pycore_initconfig.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ struct pyruntimestate;
4444
#define _PyStatus_UPDATE_FUNC(err) \
4545
do { (err).func = _PyStatus_GET_FUNC(); } while (0)
4646

47-
PyObject* _PyErr_SetFromPyStatus(PyStatus status);
48-
4947
/* --- PyWideStringList ------------------------------------------------ */
5048

5149
#define _PyWideStringList_INIT (PyWideStringList){.length = 0, .items = NULL}

Include/internal/pycore_interp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ extern "C" {
2525
#include "pycore_import.h" // struct _import_state
2626
#include "pycore_list.h" // struct _Py_list_state
2727
#include "pycore_global_objects.h" // struct _Py_interp_static_objects
28+
#include "pycore_object_state.h" // struct _py_object_state
2829
#include "pycore_tuple.h" // struct _Py_tuple_state
2930
#include "pycore_typeobject.h" // struct type_cache
3031
#include "pycore_unicodeobject.h" // struct _Py_unicode_state
@@ -138,6 +139,7 @@ struct _is {
138139
// One bit is set for each non-NULL entry in code_watchers
139140
uint8_t active_code_watchers;
140141

142+
struct _py_object_state object_state;
141143
struct _Py_unicode_state unicode;
142144
struct _Py_float_state float_state;
143145
struct _Py_long_state long_state;

Include/internal/pycore_object.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,19 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
4343
built against the pre-3.12 stable ABI. */
4444
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
4545

46-
extern void _Py_AddRefTotal(Py_ssize_t);
47-
extern void _Py_IncRefTotal(void);
48-
extern void _Py_DecRefTotal(void);
46+
extern void _Py_AddRefTotal(PyInterpreterState *, Py_ssize_t);
47+
extern void _Py_IncRefTotal(PyInterpreterState *);
48+
extern void _Py_DecRefTotal(PyInterpreterState *);
4949

50-
# define _Py_DEC_REFTOTAL() _PyRuntime.object_state.reftotal--
50+
# define _Py_DEC_REFTOTAL(interp) \
51+
interp->object_state.reftotal--
5152
#endif
5253

5354
// Increment reference count by n
5455
static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
5556
{
5657
#ifdef Py_REF_DEBUG
57-
_Py_AddRefTotal(n);
58+
_Py_AddRefTotal(_PyInterpreterState_GET(), n);
5859
#endif
5960
op->ob_refcnt += n;
6061
}
@@ -65,7 +66,7 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
6566
{
6667
_Py_DECREF_STAT_INC();
6768
#ifdef Py_REF_DEBUG
68-
_Py_DEC_REFTOTAL();
69+
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
6970
#endif
7071
if (--op->ob_refcnt != 0) {
7172
assert(op->ob_refcnt > 0);
@@ -83,7 +84,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
8384
{
8485
_Py_DECREF_STAT_INC();
8586
#ifdef Py_REF_DEBUG
86-
_Py_DEC_REFTOTAL();
87+
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
8788
#endif
8889
op->ob_refcnt--;
8990
#ifdef Py_DEBUG
@@ -226,6 +227,7 @@ static inline void _PyObject_GC_UNTRACK(
226227
#endif
227228

228229
#ifdef Py_REF_DEBUG
230+
extern void _PyInterpreterState_FinalizeRefTotal(PyInterpreterState *);
229231
extern void _Py_FinalizeRefTotal(_PyRuntimeState *);
230232
extern void _PyDebug_PrintTotalRefs(void);
231233
#endif

Include/internal/pycore_object_state.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ extern "C" {
99
#endif
1010

1111
struct _py_object_runtime_state {
12+
#ifdef Py_REF_DEBUG
13+
Py_ssize_t interpreter_leaks;
14+
#else
15+
int _not_used;
16+
#endif
17+
};
18+
19+
struct _py_object_state {
1220
#ifdef Py_REF_DEBUG
1321
Py_ssize_t reftotal;
1422
#else

Lib/test/test_codecs.py

Lines changed: 42 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2819,24 +2819,19 @@ def test_binary_to_text_denylists_text_transforms(self):
28192819
self.assertIsNone(failure.exception.__cause__)
28202820

28212821
@unittest.skipUnless(zlib, "Requires zlib support")
2822-
def test_custom_zlib_error_is_wrapped(self):
2822+
def test_custom_zlib_error_is_noted(self):
28232823
# Check zlib codec gives a good error for malformed input
2824-
msg = "^decoding with 'zlib_codec' codec failed"
2825-
with self.assertRaisesRegex(Exception, msg) as failure:
2824+
msg = "decoding with 'zlib_codec' codec failed"
2825+
with self.assertRaises(Exception) as failure:
28262826
codecs.decode(b"hello", "zlib_codec")
2827-
self.assertIsInstance(failure.exception.__cause__,
2828-
type(failure.exception))
2827+
self.assertEqual(msg, failure.exception.__notes__[0])
28292828

2830-
def test_custom_hex_error_is_wrapped(self):
2829+
def test_custom_hex_error_is_noted(self):
28312830
# Check hex codec gives a good error for malformed input
2832-
msg = "^decoding with 'hex_codec' codec failed"
2833-
with self.assertRaisesRegex(Exception, msg) as failure:
2831+
msg = "decoding with 'hex_codec' codec failed"
2832+
with self.assertRaises(Exception) as failure:
28342833
codecs.decode(b"hello", "hex_codec")
2835-
self.assertIsInstance(failure.exception.__cause__,
2836-
type(failure.exception))
2837-
2838-
# Unfortunately, the bz2 module throws OSError, which the codec
2839-
# machinery currently can't wrap :(
2834+
self.assertEqual(msg, failure.exception.__notes__[0])
28402835

28412836
# Ensure codec aliases from http://bugs.python.org/issue7475 work
28422837
def test_aliases(self):
@@ -2860,11 +2855,8 @@ def test_uu_invalid(self):
28602855
self.assertRaises(ValueError, codecs.decode, b"", "uu-codec")
28612856

28622857

2863-
# The codec system tries to wrap exceptions in order to ensure the error
2864-
# mentions the operation being performed and the codec involved. We
2865-
# currently *only* want this to happen for relatively stateless
2866-
# exceptions, where the only significant information they contain is their
2867-
# type and a single str argument.
2858+
# The codec system tries to add notes to exceptions in order to ensure
2859+
# the error mentions the operation being performed and the codec involved.
28682860

28692861
# Use a local codec registry to avoid appearing to leak objects when
28702862
# registering multiple search functions
@@ -2874,10 +2866,10 @@ def _get_test_codec(codec_name):
28742866
return _TEST_CODECS.get(codec_name)
28752867

28762868

2877-
class ExceptionChainingTest(unittest.TestCase):
2869+
class ExceptionNotesTest(unittest.TestCase):
28782870

28792871
def setUp(self):
2880-
self.codec_name = 'exception_chaining_test'
2872+
self.codec_name = 'exception_notes_test'
28812873
codecs.register(_get_test_codec)
28822874
self.addCleanup(codecs.unregister, _get_test_codec)
28832875

@@ -2901,91 +2893,77 @@ def set_codec(self, encode, decode):
29012893
_TEST_CODECS[self.codec_name] = codec_info
29022894

29032895
@contextlib.contextmanager
2904-
def assertWrapped(self, operation, exc_type, msg):
2905-
full_msg = r"{} with {!r} codec failed \({}: {}\)".format(
2906-
operation, self.codec_name, exc_type.__name__, msg)
2907-
with self.assertRaisesRegex(exc_type, full_msg) as caught:
2896+
def assertNoted(self, operation, exc_type, msg):
2897+
full_msg = r"{} with {!r} codec failed".format(
2898+
operation, self.codec_name)
2899+
with self.assertRaises(exc_type) as caught:
29082900
yield caught
2909-
self.assertIsInstance(caught.exception.__cause__, exc_type)
2910-
self.assertIsNotNone(caught.exception.__cause__.__traceback__)
2901+
self.assertIn(full_msg, caught.exception.__notes__[0])
2902+
caught.exception.__notes__.clear()
29112903

29122904
def raise_obj(self, *args, **kwds):
29132905
# Helper to dynamically change the object raised by a test codec
29142906
raise self.obj_to_raise
29152907

2916-
def check_wrapped(self, obj_to_raise, msg, exc_type=RuntimeError):
2908+
def check_note(self, obj_to_raise, msg, exc_type=RuntimeError):
29172909
self.obj_to_raise = obj_to_raise
29182910
self.set_codec(self.raise_obj, self.raise_obj)
2919-
with self.assertWrapped("encoding", exc_type, msg):
2911+
with self.assertNoted("encoding", exc_type, msg):
29202912
"str_input".encode(self.codec_name)
2921-
with self.assertWrapped("encoding", exc_type, msg):
2913+
with self.assertNoted("encoding", exc_type, msg):
29222914
codecs.encode("str_input", self.codec_name)
2923-
with self.assertWrapped("decoding", exc_type, msg):
2915+
with self.assertNoted("decoding", exc_type, msg):
29242916
b"bytes input".decode(self.codec_name)
2925-
with self.assertWrapped("decoding", exc_type, msg):
2917+
with self.assertNoted("decoding", exc_type, msg):
29262918
codecs.decode(b"bytes input", self.codec_name)
29272919

29282920
def test_raise_by_type(self):
2929-
self.check_wrapped(RuntimeError, "")
2921+
self.check_note(RuntimeError, "")
29302922

29312923
def test_raise_by_value(self):
2932-
msg = "This should be wrapped"
2933-
self.check_wrapped(RuntimeError(msg), msg)
2924+
msg = "This should be noted"
2925+
self.check_note(RuntimeError(msg), msg)
29342926

29352927
def test_raise_grandchild_subclass_exact_size(self):
2936-
msg = "This should be wrapped"
2928+
msg = "This should be noted"
29372929
class MyRuntimeError(RuntimeError):
29382930
__slots__ = ()
2939-
self.check_wrapped(MyRuntimeError(msg), msg, MyRuntimeError)
2931+
self.check_note(MyRuntimeError(msg), msg, MyRuntimeError)
29402932

29412933
def test_raise_subclass_with_weakref_support(self):
2942-
msg = "This should be wrapped"
2934+
msg = "This should be noted"
29432935
class MyRuntimeError(RuntimeError):
29442936
pass
2945-
self.check_wrapped(MyRuntimeError(msg), msg, MyRuntimeError)
2946-
2947-
def check_not_wrapped(self, obj_to_raise, msg):
2948-
def raise_obj(*args, **kwds):
2949-
raise obj_to_raise
2950-
self.set_codec(raise_obj, raise_obj)
2951-
with self.assertRaisesRegex(RuntimeError, msg):
2952-
"str input".encode(self.codec_name)
2953-
with self.assertRaisesRegex(RuntimeError, msg):
2954-
codecs.encode("str input", self.codec_name)
2955-
with self.assertRaisesRegex(RuntimeError, msg):
2956-
b"bytes input".decode(self.codec_name)
2957-
with self.assertRaisesRegex(RuntimeError, msg):
2958-
codecs.decode(b"bytes input", self.codec_name)
2937+
self.check_note(MyRuntimeError(msg), msg, MyRuntimeError)
29592938

2960-
def test_init_override_is_not_wrapped(self):
2939+
def test_init_override(self):
29612940
class CustomInit(RuntimeError):
29622941
def __init__(self):
29632942
pass
2964-
self.check_not_wrapped(CustomInit, "")
2943+
self.check_note(CustomInit, "")
29652944

2966-
def test_new_override_is_not_wrapped(self):
2945+
def test_new_override(self):
29672946
class CustomNew(RuntimeError):
29682947
def __new__(cls):
29692948
return super().__new__(cls)
2970-
self.check_not_wrapped(CustomNew, "")
2949+
self.check_note(CustomNew, "")
29712950

2972-
def test_instance_attribute_is_not_wrapped(self):
2973-
msg = "This should NOT be wrapped"
2951+
def test_instance_attribute(self):
2952+
msg = "This should be noted"
29742953
exc = RuntimeError(msg)
29752954
exc.attr = 1
2976-
self.check_not_wrapped(exc, "^{}$".format(msg))
2955+
self.check_note(exc, "^{}$".format(msg))
29772956

2978-
def test_non_str_arg_is_not_wrapped(self):
2979-
self.check_not_wrapped(RuntimeError(1), "1")
2957+
def test_non_str_arg(self):
2958+
self.check_note(RuntimeError(1), "1")
29802959

2981-
def test_multiple_args_is_not_wrapped(self):
2960+
def test_multiple_args(self):
29822961
msg_re = r"^\('a', 'b', 'c'\)$"
2983-
self.check_not_wrapped(RuntimeError('a', 'b', 'c'), msg_re)
2962+
self.check_note(RuntimeError('a', 'b', 'c'), msg_re)
29842963

29852964
# http://bugs.python.org/issue19609
2986-
def test_codec_lookup_failure_not_wrapped(self):
2965+
def test_codec_lookup_failure(self):
29872966
msg = "^unknown encoding: {}$".format(self.codec_name)
2988-
# The initial codec lookup should not be wrapped
29892967
with self.assertRaisesRegex(LookupError, msg):
29902968
"str input".encode(self.codec_name)
29912969
with self.assertRaisesRegex(LookupError, msg):

0 commit comments

Comments
 (0)