From 1446024124fb98c3051199760380685f8a2fd127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Sun, 21 Apr 2024 20:03:46 +0200 Subject: [PATCH 001/217] Docs: replace Harry Potter reference with Monty Python (#118130) --- Doc/library/doctest.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index a643a0e7e313bf..5f7d10a6dce037 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -800,18 +800,18 @@ guarantee about output. For example, when printing a set, Python doesn't guarantee that the element is printed in any particular order, so a test like :: >>> foo() - {"Hermione", "Harry"} + {"spam", "eggs"} is vulnerable! One workaround is to do :: - >>> foo() == {"Hermione", "Harry"} + >>> foo() == {"spam", "eggs"} True instead. Another is to do :: >>> d = sorted(foo()) >>> d - ['Harry', 'Hermione'] + ['eggs', 'spam'] There are others, but you get the idea. From 8b541c017ea92040add608b3e0ef8dc85e9e6060 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Sun, 21 Apr 2024 22:57:05 -0700 Subject: [PATCH 002/217] gh-112075: Make instance attributes stored in inline "dict" thread safe (#114742) Make instance attributes stored in inline "dict" thread safe on free-threaded builds --- Include/cpython/object.h | 1 + Include/internal/pycore_dict.h | 19 +- Include/internal/pycore_object.h | 16 +- .../internal/pycore_pyatomic_ft_wrappers.h | 14 + Lib/test/test_class.py | 9 + Objects/dictobject.c | 385 ++++++++++++++---- Objects/object.c | 45 +- Objects/typeobject.c | 30 +- Python/bytecodes.c | 15 +- Python/executor_cases.c.h | 10 +- Python/generated_cases.c.h | 13 +- Python/specialize.c | 3 +- Tools/cases_generator/analyzer.py | 1 + 13 files changed, 419 insertions(+), 142 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 2797051281f3b4..a8f57827a964cd 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -493,6 +493,7 @@ do { \ PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj); PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg); +PyAPI_FUNC(void) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict); PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj); #define TYPE_MAX_WATCHERS 8 diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index fba0dfc40714ec..f33026dbd6be58 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -1,4 +1,3 @@ - #ifndef Py_INTERNAL_DICT_H #define Py_INTERNAL_DICT_H #ifdef __cplusplus @@ -9,9 +8,10 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif -#include "pycore_freelist.h" // _PyFreeListState -#include "pycore_identifier.h" // _Py_Identifier -#include "pycore_object.h" // PyManagedDictPointer +#include "pycore_freelist.h" // _PyFreeListState +#include "pycore_identifier.h" // _Py_Identifier +#include "pycore_object.h" // PyManagedDictPointer +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_ACQUIRE // Unsafe flavor of PyDict_GetItemWithError(): no error checking extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key); @@ -249,7 +249,7 @@ _PyDict_NotifyEvent(PyInterpreterState *interp, return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK); } -extern PyDictObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj); +extern PyDictObject *_PyObject_MaterializeManagedDict(PyObject *obj); PyAPI_FUNC(PyObject *)_PyDict_FromItems( PyObject *const *keys, Py_ssize_t keys_offset, @@ -277,7 +277,6 @@ _PyDictValues_AddToInsertionOrder(PyDictValues *values, Py_ssize_t ix) static inline size_t shared_keys_usable_size(PyDictKeysObject *keys) { -#ifdef Py_GIL_DISABLED // dk_usable will decrease for each instance that is created and each // value that is added. dk_nentries will increase for each value that // is added. We want to always return the right value or larger. @@ -285,11 +284,9 @@ shared_keys_usable_size(PyDictKeysObject *keys) // second, and conversely here we read dk_usable first and dk_entries // second (to avoid the case where we read entries before the increment // and read usable after the decrement) - return (size_t)(_Py_atomic_load_ssize_acquire(&keys->dk_usable) + - _Py_atomic_load_ssize_acquire(&keys->dk_nentries)); -#else - return (size_t)keys->dk_nentries + (size_t)keys->dk_usable; -#endif + Py_ssize_t dk_usable = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(keys->dk_usable); + Py_ssize_t dk_nentries = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(keys->dk_nentries); + return dk_nentries + dk_usable; } static inline size_t diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 512f7a35f50e38..88b052f4544b15 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -12,6 +12,7 @@ extern "C" { #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() #include "pycore_emscripten_trampoline.h" // _PyCFunction_TrampolineCall() #include "pycore_interp.h" // PyInterpreterState.gc +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_PTR_RELAXED #include "pycore_pystate.h" // _PyInterpreterState_GET() /* Check if an object is consistent. For example, ensure that the reference @@ -659,10 +660,10 @@ extern PyObject* _PyType_GetDocFromInternalDoc(const char *, const char *); extern PyObject* _PyType_GetTextSignatureFromInternalDoc(const char *, const char *, int); void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp); -extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, - PyObject *name, PyObject *value); -PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values, - PyObject *name); +extern int _PyObject_StoreInstanceAttribute(PyObject *obj, + PyObject *name, PyObject *value); +extern bool _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, + PyObject **attr); #ifdef Py_GIL_DISABLED # define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-1) @@ -683,6 +684,13 @@ _PyObject_ManagedDictPointer(PyObject *obj) return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET); } +static inline PyDictObject * +_PyObject_GetManagedDict(PyObject *obj) +{ + PyManagedDictPointer *dorv = _PyObject_ManagedDictPointer(obj); + return (PyDictObject *)FT_ATOMIC_LOAD_PTR_RELAXED(dorv->dict); +} + static inline PyDictValues * _PyObject_InlineValues(PyObject *obj) { diff --git a/Include/internal/pycore_pyatomic_ft_wrappers.h b/Include/internal/pycore_pyatomic_ft_wrappers.h index fed5d6e0ec2c54..bbfc462a733d0e 100644 --- a/Include/internal/pycore_pyatomic_ft_wrappers.h +++ b/Include/internal/pycore_pyatomic_ft_wrappers.h @@ -21,7 +21,10 @@ extern "C" { #ifdef Py_GIL_DISABLED #define FT_ATOMIC_LOAD_PTR(value) _Py_atomic_load_ptr(&value) +#define FT_ATOMIC_STORE_PTR(value, new_value) _Py_atomic_store_ptr(&value, new_value) #define FT_ATOMIC_LOAD_SSIZE(value) _Py_atomic_load_ssize(&value) +#define FT_ATOMIC_LOAD_SSIZE_ACQUIRE(value) \ + _Py_atomic_load_ssize_acquire(&value) #define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) \ _Py_atomic_load_ssize_relaxed(&value) #define FT_ATOMIC_STORE_PTR(value, new_value) \ @@ -30,6 +33,12 @@ extern "C" { _Py_atomic_load_ptr_acquire(&value) #define FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(value) \ _Py_atomic_load_uintptr_acquire(&value) +#define FT_ATOMIC_LOAD_PTR_RELAXED(value) \ + _Py_atomic_load_ptr_relaxed(&value) +#define FT_ATOMIC_LOAD_UINT8(value) \ + _Py_atomic_load_uint8(&value) +#define FT_ATOMIC_STORE_UINT8(value, new_value) \ + _Py_atomic_store_uint8(&value, new_value) #define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) \ _Py_atomic_store_ptr_relaxed(&value, new_value) #define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) \ @@ -43,11 +52,16 @@ extern "C" { #else #define FT_ATOMIC_LOAD_PTR(value) value +#define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value #define FT_ATOMIC_LOAD_SSIZE(value) value +#define FT_ATOMIC_LOAD_SSIZE_ACQUIRE(value) value #define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) value #define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value #define FT_ATOMIC_LOAD_PTR_ACQUIRE(value) value #define FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(value) value +#define FT_ATOMIC_LOAD_PTR_RELAXED(value) value +#define FT_ATOMIC_LOAD_UINT8(value) value +#define FT_ATOMIC_STORE_UINT8(value, new_value) value = new_value #define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) value = new_value #define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) value = new_value #define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) value = new_value diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index a9cfd8df691845..5885db84b66b01 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -873,6 +873,15 @@ def __init__(self): obj.foo = None # Aborted here self.assertEqual(obj.__dict__, {"foo":None}) + def test_store_attr_deleted_dict(self): + class Foo: + pass + + f = Foo() + del f.__dict__ + f.a = 3 + self.assertEqual(f.a, 3) + if __name__ == '__main__': unittest.main() diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 4e696419eb5eb0..2644516bc30770 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1752,7 +1752,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, uint64_t new_version = _PyDict_NotifyEvent( interp, PyDict_EVENT_MODIFIED, mp, key, value); if (_PyDict_HasSplitTable(mp)) { - mp->ma_values->values[ix] = value; + STORE_SPLIT_VALUE(mp, ix, value); if (old_value == NULL) { _PyDictValues_AddToInsertionOrder(mp->ma_values, ix); mp->ma_used++; @@ -2514,7 +2514,7 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, mp->ma_version_tag = new_version; if (_PyDict_HasSplitTable(mp)) { assert(old_value == mp->ma_values->values[ix]); - mp->ma_values->values[ix] = NULL; + STORE_SPLIT_VALUE(mp, ix, NULL); assert(ix < SHARED_KEYS_MAX_SIZE); /* Update order */ delete_index_from_values(mp->ma_values, ix); @@ -4226,7 +4226,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu assert(_PyDict_HasSplitTable(mp)); assert(mp->ma_values->values[ix] == NULL); MAINTAIN_TRACKING(mp, key, value); - mp->ma_values->values[ix] = Py_NewRef(value); + STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value)); _PyDictValues_AddToInsertionOrder(mp->ma_values, ix); mp->ma_used++; mp->ma_version_tag = new_version; @@ -6616,28 +6616,79 @@ make_dict_from_instance_attributes(PyInterpreterState *interp, return res; } -PyDictObject * -_PyObject_MakeDictFromInstanceAttributes(PyObject *obj) +static PyDictObject * +materialize_managed_dict_lock_held(PyObject *obj) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj); + + OBJECT_STAT_INC(dict_materialized_on_request); + PyDictValues *values = _PyObject_InlineValues(obj); PyInterpreterState *interp = _PyInterpreterState_GET(); PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); OBJECT_STAT_INC(dict_materialized_on_request); - return make_dict_from_instance_attributes(interp, keys, values); + PyDictObject *dict = make_dict_from_instance_attributes(interp, keys, values); + FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict, + (PyDictObject *)dict); + return dict; } +PyDictObject * +_PyObject_MaterializeManagedDict(PyObject *obj) +{ + PyDictObject *dict = _PyObject_GetManagedDict(obj); + if (dict != NULL) { + return dict; + } + Py_BEGIN_CRITICAL_SECTION(obj); -int -_PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, +#ifdef Py_GIL_DISABLED + dict = _PyObject_GetManagedDict(obj); + if (dict != NULL) { + // We raced with another thread creating the dict + goto exit; + } +#endif + dict = materialize_managed_dict_lock_held(obj); + +#ifdef Py_GIL_DISABLED +exit: +#endif + Py_END_CRITICAL_SECTION(); + return dict; +} + +static int +set_or_del_lock_held(PyDictObject *dict, PyObject *name, PyObject *value) +{ + if (value == NULL) { + Py_hash_t hash; + if (!PyUnicode_CheckExact(name) || (hash = unicode_get_hash(name)) == -1) { + hash = PyObject_Hash(name); + if (hash == -1) + return -1; + } + return delitem_knownhash_lock_held((PyObject *)dict, name, hash); + } else { + return setitem_lock_held(dict, name, value); + } +} + +// Called with either the object's lock or the dict's lock held +// depending on whether or not a dict has been materialized for +// the object. +static int +store_instance_attr_lock_held(PyObject *obj, PyDictValues *values, PyObject *name, PyObject *value) { - PyInterpreterState *interp = _PyInterpreterState_GET(); PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); assert(keys != NULL); assert(values != NULL); assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES); Py_ssize_t ix = DKIX_EMPTY; + PyDictObject *dict = _PyObject_GetManagedDict(obj); + assert(dict == NULL || ((PyDictObject *)dict)->ma_values == values); if (PyUnicode_CheckExact(name)) { Py_hash_t hash = unicode_get_hash(name); if (hash == -1) { @@ -6674,25 +6725,33 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, } #endif } - PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict; + if (ix == DKIX_EMPTY) { + int res; if (dict == NULL) { - dict = make_dict_from_instance_attributes( - interp, keys, values); - if (dict == NULL) { + // Make the dict but don't publish it in the object + // so that no one else will see it. + dict = make_dict_from_instance_attributes(PyInterpreterState_Get(), keys, values); + if (dict == NULL || + set_or_del_lock_held(dict, name, value) < 0) { + Py_XDECREF(dict); return -1; } - _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)dict; - } - if (value == NULL) { - return PyDict_DelItem((PyObject *)dict, name); - } - else { - return PyDict_SetItem((PyObject *)dict, name, value); + + FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict, + (PyDictObject *)dict); + return 0; } + + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(dict); + + res = set_or_del_lock_held (dict, name, value); + return res; } + PyObject *old_value = values->values[ix]; - values->values[ix] = Py_XNewRef(value); + FT_ATOMIC_STORE_PTR_RELEASE(values->values[ix], Py_XNewRef(value)); + if (old_value == NULL) { if (value == NULL) { PyErr_Format(PyExc_AttributeError, @@ -6719,6 +6778,72 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, return 0; } +static inline int +store_instance_attr_dict(PyObject *obj, PyDictObject *dict, PyObject *name, PyObject *value) +{ + PyDictValues *values = _PyObject_InlineValues(obj); + int res; + Py_BEGIN_CRITICAL_SECTION(dict); + if (dict->ma_values == values) { + res = store_instance_attr_lock_held(obj, values, name, value); + } + else { + res = set_or_del_lock_held(dict, name, value); + } + Py_END_CRITICAL_SECTION(); + return res; +} + +int +_PyObject_StoreInstanceAttribute(PyObject *obj, PyObject *name, PyObject *value) +{ + PyDictValues *values = _PyObject_InlineValues(obj); + if (!FT_ATOMIC_LOAD_UINT8(values->valid)) { + PyDictObject *dict = _PyObject_GetManagedDict(obj); + if (dict == NULL) { + dict = (PyDictObject *)PyObject_GenericGetDict(obj, NULL); + if (dict == NULL) { + return -1; + } + int res = store_instance_attr_dict(obj, dict, name, value); + Py_DECREF(dict); + return res; + } + return store_instance_attr_dict(obj, dict, name, value); + } + +#ifdef Py_GIL_DISABLED + // We have a valid inline values, at least for now... There are two potential + // races with having the values become invalid. One is the dictionary + // being detached from the object. The other is if someone is inserting + // into the dictionary directly and therefore causing it to resize. + // + // If we haven't materialized the dictionary yet we lock on the object, which + // will also be used to prevent the dictionary from being materialized while + // we're doing the insertion. If we race and the dictionary gets created + // then we'll need to release the object lock and lock the dictionary to + // prevent resizing. + PyDictObject *dict = _PyObject_GetManagedDict(obj); + if (dict == NULL) { + int res; + Py_BEGIN_CRITICAL_SECTION(obj); + dict = _PyObject_GetManagedDict(obj); + + if (dict == NULL) { + res = store_instance_attr_lock_held(obj, values, name, value); + } + Py_END_CRITICAL_SECTION(); + + if (dict == NULL) { + return res; + } + } + return store_instance_attr_dict(obj, dict, name, value); +#else + return store_instance_attr_lock_held(obj, values, name, value); +#endif +} + /* Sanity check for managed dicts */ #if 0 #define CHECK(val) assert(val); if (!(val)) { return 0; } @@ -6750,19 +6875,79 @@ _PyObject_ManagedDictValidityCheck(PyObject *obj) } #endif -PyObject * -_PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values, - PyObject *name) +// Attempts to get an instance attribute from the inline values. Returns true +// if successful, or false if the caller needs to lookup in the dictionary. +bool +_PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, PyObject **attr) { assert(PyUnicode_CheckExact(name)); + PyDictValues *values = _PyObject_InlineValues(obj); + if (!FT_ATOMIC_LOAD_UINT8(values->valid)) { + return false; + } + PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); assert(keys != NULL); Py_ssize_t ix = _PyDictKeys_StringLookup(keys, name); if (ix == DKIX_EMPTY) { - return NULL; + *attr = NULL; + return true; + } + +#ifdef Py_GIL_DISABLED + PyObject *value = _Py_atomic_load_ptr_relaxed(&values->values[ix]); + if (value == NULL || _Py_TryIncrefCompare(&values->values[ix], value)) { + *attr = value; + return true; + } + + PyDictObject *dict = _PyObject_GetManagedDict(obj); + if (dict == NULL) { + // No dict, lock the object to prevent one from being + // materialized... + bool success = false; + Py_BEGIN_CRITICAL_SECTION(obj); + + dict = _PyObject_GetManagedDict(obj); + if (dict == NULL) { + // Still no dict, we can read from the values + assert(values->valid); + value = values->values[ix]; + *attr = Py_XNewRef(value); + success = true; + } + + Py_END_CRITICAL_SECTION(); + + if (success) { + return true; + } } + + // We have a dictionary, we'll need to lock it to prevent + // the values from being resized. + assert(dict != NULL); + + bool success; + Py_BEGIN_CRITICAL_SECTION(dict); + + if (dict->ma_values == values && FT_ATOMIC_LOAD_UINT8(values->valid)) { + value = _Py_atomic_load_ptr_relaxed(&values->values[ix]); + *attr = Py_XNewRef(value); + success = true; + } else { + // Caller needs to lookup from the dictionary + success = false; + } + + Py_END_CRITICAL_SECTION(); + + return success; +#else PyObject *value = values->values[ix]; - return Py_XNewRef(value); + *attr = Py_XNewRef(value); + return true; +#endif } int @@ -6775,20 +6960,19 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj) PyDictObject *dict; if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { PyDictValues *values = _PyObject_InlineValues(obj); - if (values->valid) { + if (FT_ATOMIC_LOAD_UINT8(values->valid)) { PyDictKeysObject *keys = CACHED_KEYS(tp); for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - if (values->values[i] != NULL) { + if (FT_ATOMIC_LOAD_PTR_RELAXED(values->values[i]) != NULL) { return 0; } } return 1; } - dict = _PyObject_ManagedDictPointer(obj)->dict; + dict = _PyObject_GetManagedDict(obj); } else if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); - dict = managed_dict->dict; + dict = _PyObject_GetManagedDict(obj); } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); @@ -6820,53 +7004,115 @@ PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) return 0; } +static void +set_dict_inline_values(PyObject *obj, PyDictObject *new_dict) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj); + + PyDictValues *values = _PyObject_InlineValues(obj); + + Py_XINCREF(new_dict); + FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, new_dict); + + if (values->valid) { + FT_ATOMIC_STORE_UINT8(values->valid, 0); + for (Py_ssize_t i = 0; i < values->capacity; i++) { + Py_CLEAR(values->values[i]); + } + } +} + void -PyObject_ClearManagedDict(PyObject *obj) +_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict) { assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); assert(_PyObject_InlineValuesConsistencyCheck(obj)); PyTypeObject *tp = Py_TYPE(obj); if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { - PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict; - if (dict) { - _PyDict_DetachFromObject(dict, obj); - _PyObject_ManagedDictPointer(obj)->dict = NULL; - Py_DECREF(dict); - } - else { - PyDictValues *values = _PyObject_InlineValues(obj); - if (values->valid) { - for (Py_ssize_t i = 0; i < values->capacity; i++) { - Py_CLEAR(values->values[i]); - } - values->valid = 0; + PyDictObject *dict = _PyObject_GetManagedDict(obj); + if (dict == NULL) { +#ifdef Py_GIL_DISABLED + Py_BEGIN_CRITICAL_SECTION(obj); + + dict = _PyObject_ManagedDictPointer(obj)->dict; + if (dict == NULL) { + set_dict_inline_values(obj, (PyDictObject *)new_dict); + } + + Py_END_CRITICAL_SECTION(); + + if (dict == NULL) { + return; } +#else + set_dict_inline_values(obj, (PyDictObject *)new_dict); + return; +#endif } + + Py_BEGIN_CRITICAL_SECTION2(dict, obj); + + // We've locked dict, but the actual dict could have changed + // since we locked it. + dict = _PyObject_ManagedDictPointer(obj)->dict; + + FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, + (PyDictObject *)Py_XNewRef(new_dict)); + + _PyDict_DetachFromObject(dict, obj); + + Py_END_CRITICAL_SECTION2(); + + Py_XDECREF(dict); } else { - Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict); + PyDictObject *dict; + + Py_BEGIN_CRITICAL_SECTION(obj); + + dict = _PyObject_ManagedDictPointer(obj)->dict; + + FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, + (PyDictObject *)Py_XNewRef(new_dict)); + + Py_END_CRITICAL_SECTION(); + + Py_XDECREF(dict); } assert(_PyObject_InlineValuesConsistencyCheck(obj)); } +void +PyObject_ClearManagedDict(PyObject *obj) +{ + _PyObject_SetManagedDict(obj, NULL); +} + int _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj) { - assert(_PyObject_ManagedDictPointer(obj)->dict == mp); - assert(_PyObject_InlineValuesConsistencyCheck(obj)); - if (mp->ma_values == NULL || mp->ma_values != _PyObject_InlineValues(obj)) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj); + + if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) { return 0; } + + // We could be called with an unlocked dict when the caller knows the + // values are already detached, so we assert after inline values check. + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp); assert(mp->ma_values->embedded == 1); assert(mp->ma_values->valid == 1); assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - Py_BEGIN_CRITICAL_SECTION(mp); - mp->ma_values = copy_values(mp->ma_values); - _PyObject_InlineValues(obj)->valid = 0; - Py_END_CRITICAL_SECTION(); - if (mp->ma_values == NULL) { + + PyDictValues *values = copy_values(mp->ma_values); + + if (values == NULL) { return -1; } + mp->ma_values = values; + + FT_ATOMIC_STORE_UINT8(_PyObject_InlineValues(obj)->valid, 0); + assert(_PyObject_InlineValuesConsistencyCheck(obj)); ASSERT_CONSISTENT(mp); return 0; @@ -6877,29 +7123,28 @@ PyObject_GenericGetDict(PyObject *obj, void *context) { PyInterpreterState *interp = _PyInterpreterState_GET(); PyTypeObject *tp = Py_TYPE(obj); + PyDictObject *dict; if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) { - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); - PyDictObject *dict = managed_dict->dict; + dict = _PyObject_GetManagedDict(obj); if (dict == NULL && (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && - _PyObject_InlineValues(obj)->valid - ) { - PyDictValues *values = _PyObject_InlineValues(obj); - OBJECT_STAT_INC(dict_materialized_on_request); - dict = make_dict_from_instance_attributes( - interp, CACHED_KEYS(tp), values); - if (dict != NULL) { - managed_dict->dict = (PyDictObject *)dict; - } + FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(obj)->valid)) { + dict = _PyObject_MaterializeManagedDict(obj); } - else { - dict = managed_dict->dict; + else if (dict == NULL) { + Py_BEGIN_CRITICAL_SECTION(obj); + + // Check again that we're not racing with someone else creating the dict + dict = _PyObject_GetManagedDict(obj); if (dict == NULL) { - dictkeys_incref(CACHED_KEYS(tp)); OBJECT_STAT_INC(dict_materialized_on_request); + dictkeys_incref(CACHED_KEYS(tp)); dict = (PyDictObject *)new_dict_with_shared_keys(interp, CACHED_KEYS(tp)); - managed_dict->dict = (PyDictObject *)dict; + FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict, + (PyDictObject *)dict); } + + Py_END_CRITICAL_SECTION(); } return Py_XNewRef((PyObject *)dict); } @@ -7109,7 +7354,7 @@ _PyObject_InlineValuesConsistencyCheck(PyObject *obj) return 1; } assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictObject *dict = (PyDictObject *)_PyObject_ManagedDictPointer(obj)->dict; + PyDictObject *dict = _PyObject_GetManagedDict(obj); if (dict == NULL) { return 1; } diff --git a/Objects/object.c b/Objects/object.c index 73a1927263cdcb..91bb0114cbfc32 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -6,6 +6,7 @@ #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate() #include "pycore_context.h" // _PyContextTokenMissing_Type +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION, Py_END_CRITICAL_SECTION #include "pycore_descrobject.h" // _PyMethodWrapper_Type #include "pycore_dict.h" // _PyObject_MakeDictFromInstanceAttributes() #include "pycore_floatobject.h" // _PyFloat_DebugMallocStats() @@ -25,6 +26,7 @@ #include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_initialize_generic #include "pycore_unionobject.h" // _PyUnion_Type + #ifdef Py_LIMITED_API // Prevent recursive call _Py_IncRef() <=> Py_INCREF() # error "Py_LIMITED_API macro must not be defined" @@ -1403,16 +1405,15 @@ _PyObject_GetDictPtr(PyObject *obj) if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { return _PyObject_ComputedDictPointer(obj); } - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); - if (managed_dict->dict == NULL && Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) { - PyDictObject *dict = (PyDictObject *)_PyObject_MakeDictFromInstanceAttributes(obj); + PyDictObject *dict = _PyObject_GetManagedDict(obj); + if (dict == NULL && Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + dict = _PyObject_MaterializeManagedDict(obj); if (dict == NULL) { PyErr_Clear(); return NULL; } - managed_dict->dict = dict; } - return (PyObject **)&managed_dict->dict; + return (PyObject **)&_PyObject_ManagedDictPointer(obj)->dict; } PyObject * @@ -1480,10 +1481,9 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) } } } - PyObject *dict; - if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { - PyDictValues *values = _PyObject_InlineValues(obj); - PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name); + PyObject *dict, *attr; + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && + _PyObject_TryGetInstanceAttribute(obj, name, &attr)) { if (attr != NULL) { *method = attr; Py_XDECREF(descr); @@ -1492,8 +1492,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) dict = NULL; } else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { - PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); - dict = (PyObject *)managed_dict->dict; + dict = (PyObject *)_PyObject_GetManagedDict(obj); } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); @@ -1586,26 +1585,23 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, } } if (dict == NULL) { - if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { - PyDictValues *values = _PyObject_InlineValues(obj); - if (PyUnicode_CheckExact(name)) { - res = _PyObject_GetInstanceAttribute(obj, values, name); + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES)) { + if (PyUnicode_CheckExact(name) && + _PyObject_TryGetInstanceAttribute(obj, name, &res)) { if (res != NULL) { goto done; } } else { - dict = (PyObject *)_PyObject_MakeDictFromInstanceAttributes(obj); + dict = (PyObject *)_PyObject_MaterializeManagedDict(obj); if (dict == NULL) { res = NULL; goto done; } - _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)dict; } } else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { - PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); - dict = (PyObject *)managed_dict->dict; + dict = (PyObject *)_PyObject_GetManagedDict(obj); } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); @@ -1700,12 +1696,13 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, if (dict == NULL) { PyObject **dictptr; - if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { - res = _PyObject_StoreInstanceAttribute( - obj, _PyObject_InlineValues(obj), name, value); + + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES)) { + res = _PyObject_StoreInstanceAttribute(obj, name, value); goto error_check; } - else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + + if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); dictptr = (PyObject **)&managed_dict->dict; } @@ -1779,7 +1776,7 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) PyObject **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES) && - _PyObject_ManagedDictPointer(obj)->dict == NULL + _PyObject_GetManagedDict(obj) == NULL ) { /* Was unable to convert to dict */ PyErr_NoMemory(); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 970c82d2a17ada..808e11fcbaf1ff 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3165,9 +3165,9 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context) "not a '%.200s'", Py_TYPE(value)->tp_name); return -1; } + if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyObject_ClearManagedDict(obj); - _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)Py_XNewRef(value); + _PyObject_SetManagedDict(obj, value); } else { dictptr = _PyObject_ComputedDictPointer(obj); @@ -6194,15 +6194,27 @@ object_set_class(PyObject *self, PyObject *value, void *closure) /* Changing the class will change the implicit dict keys, * so we must materialize the dictionary first. */ if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) { - PyDictObject *dict = _PyObject_ManagedDictPointer(self)->dict; + PyDictObject *dict = _PyObject_MaterializeManagedDict(self); if (dict == NULL) { - dict = (PyDictObject *)_PyObject_MakeDictFromInstanceAttributes(self); - if (dict == NULL) { - return -1; - } - _PyObject_ManagedDictPointer(self)->dict = dict; + return -1; + } + + bool error = false; + + Py_BEGIN_CRITICAL_SECTION2(self, dict); + + // If we raced after materialization and replaced the dict + // then the materialized dict should no longer have the + // inline values in which case detach is a nop. + assert(_PyObject_GetManagedDict(self) == dict || + dict->ma_values != _PyObject_InlineValues(self)); + + if (_PyDict_DetachFromObject(dict, self) < 0) { + error = true; } - if (_PyDict_DetachFromObject(dict, self)) { + + Py_END_CRITICAL_SECTION2(); + if (error) { return -1; } } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c1fbd3c7d26e01..b7511b9107fdf6 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1947,15 +1947,13 @@ dummy_func( op(_CHECK_ATTR_WITH_HINT, (owner -- owner)) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(dict == NULL); assert(PyDict_CheckExact((PyObject *)dict)); } op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, null if (oparg & 1))) { - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -2072,14 +2070,15 @@ dummy_func( op(_GUARD_DORV_NO_DICT, (owner -- owner)) { assert(Py_TYPE(owner)->tp_dictoffset < 0); assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(_PyObject_ManagedDictPointer(owner)->dict); + DEOPT_IF(_PyObject_GetManagedDict(owner)); DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0); } op(_STORE_ATTR_INSTANCE_VALUE, (index/1, value, owner --)) { STAT_INC(STORE_ATTR, hit); - assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + assert(_PyObject_GetManagedDict(owner) == NULL); PyDictValues *values = _PyObject_InlineValues(owner); + PyObject *old_value = values->values[index]; values->values[index] = value; if (old_value == NULL) { @@ -2088,6 +2087,7 @@ dummy_func( else { Py_DECREF(old_value); } + Py_DECREF(owner); } @@ -2102,8 +2102,7 @@ dummy_func( assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(dict == NULL); assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index df87f9178f17cf..841ce8cbedb3fb 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1998,8 +1998,7 @@ PyObject *owner; owner = stack_pointer[-1]; assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); if (dict == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); @@ -2015,8 +2014,7 @@ oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; uint16_t hint = (uint16_t)CURRENT_OPERAND(); - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); if (hint >= (size_t)dict->ma_keys->dk_nentries) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); @@ -2159,7 +2157,7 @@ owner = stack_pointer[-1]; assert(Py_TYPE(owner)->tp_dictoffset < 0); assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (_PyObject_ManagedDictPointer(owner)->dict) { + if (_PyObject_GetManagedDict(owner)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } @@ -2177,7 +2175,7 @@ value = stack_pointer[-2]; uint16_t index = (uint16_t)CURRENT_OPERAND(); STAT_INC(STORE_ATTR, hit); - assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + assert(_PyObject_GetManagedDict(owner) == NULL); PyDictValues *values = _PyObject_InlineValues(owner); PyObject *old_value = values->values[index]; values->values[index] = value; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a426d9e208492e..058cac8bedd917 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4017,16 +4017,14 @@ // _CHECK_ATTR_WITH_HINT { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(dict == NULL, LOAD_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); } // _LOAD_ATTR_WITH_HINT { uint16_t hint = read_u16(&this_instr[4].cache); - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -5309,7 +5307,7 @@ { assert(Py_TYPE(owner)->tp_dictoffset < 0); assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(_PyObject_ManagedDictPointer(owner)->dict, STORE_ATTR); + DEOPT_IF(_PyObject_GetManagedDict(owner), STORE_ATTR); DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0, STORE_ATTR); } // _STORE_ATTR_INSTANCE_VALUE @@ -5317,7 +5315,7 @@ { uint16_t index = read_u16(&this_instr[4].cache); STAT_INC(STORE_ATTR, hit); - assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + assert(_PyObject_GetManagedDict(owner) == NULL); PyDictValues *values = _PyObject_InlineValues(owner); PyObject *old_value = values->values[index]; values->values[index] = value; @@ -5380,8 +5378,7 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(dict == NULL, STORE_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); diff --git a/Python/specialize.c b/Python/specialize.c index 5e14bb56b30036..ee51781372166a 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -852,8 +852,7 @@ specialize_dict_access( instr->op.code = values_op; } else { - PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); - PyDictObject *dict = managed_dict->dict; + PyDictObject *dict = _PyObject_GetManagedDict(owner); if (dict == NULL || !PyDict_CheckExact(dict)) { SPECIALIZATION_FAIL(base_op, SPEC_FAIL_NO_DICT); return 0; diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index d17b2b9b024b99..18cefa08328804 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -354,6 +354,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool: NON_ESCAPING_FUNCTIONS = ( "Py_INCREF", "_PyManagedDictPointer_IsValues", + "_PyObject_GetManagedDict", "_PyObject_ManagedDictPointer", "_PyObject_InlineValues", "_PyDictValues_AddToInsertionOrder", From 550483b7e6c54b2a25d4db0c4ca41bd9c1132f93 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 22 Apr 2024 08:43:20 +0200 Subject: [PATCH 003/217] gh-117995: Don't raise DeprecationWarnings for indexed nameless params (#118001) Filter out '?NNN' placeholders when looking for named params. Co-authored-by: AN Long --- Lib/test/test_sqlite3/test_dbapi.py | 14 ++++++++++++++ .../2024-04-17-19-41-59.gh-issue-117995.Vt76Rv.rst | 2 ++ Modules/_sqlite/cursor.c | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-17-19-41-59.gh-issue-117995.Vt76Rv.rst diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 4182de246a071b..6d8744ca5f7969 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -28,6 +28,7 @@ import threading import unittest import urllib.parse +import warnings from test.support import ( SHORT_TIMEOUT, check_disallow_instantiation, requires_subprocess, @@ -887,6 +888,19 @@ def test_execute_named_param_and_sequence(self): self.cu.execute(query, params) self.assertEqual(cm.filename, __file__) + def test_execute_indexed_nameless_params(self): + # See gh-117995: "'?1' is considered a named placeholder" + for query, params, expected in ( + ("select ?1, ?2", (1, 2), (1, 2)), + ("select ?2, ?1", (1, 2), (2, 1)), + ): + with self.subTest(query=query, params=params): + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + cu = self.cu.execute(query, params) + actual, = cu.fetchall() + self.assertEqual(actual, expected) + def test_execute_too_many_params(self): category = sqlite.SQLITE_LIMIT_VARIABLE_NUMBER msg = "too many SQL variables" diff --git a/Misc/NEWS.d/next/Library/2024-04-17-19-41-59.gh-issue-117995.Vt76Rv.rst b/Misc/NEWS.d/next/Library/2024-04-17-19-41-59.gh-issue-117995.Vt76Rv.rst new file mode 100644 index 00000000000000..a289939d33e830 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-17-19-41-59.gh-issue-117995.Vt76Rv.rst @@ -0,0 +1,2 @@ +Don't raise :exc:`DeprecationWarning` when a :term:`sequence` of parameters +is used to bind indexed, nameless placeholders. See also :gh:`100668`. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index f95df612328e57..950596ea82b568 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -669,7 +669,7 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self, } for (i = 0; i < num_params; i++) { const char *name = sqlite3_bind_parameter_name(self->st, i+1); - if (name != NULL) { + if (name != NULL && name[0] != '?') { int ret = PyErr_WarnFormat(PyExc_DeprecationWarning, 1, "Binding %d ('%s') is a named parameter, but you " "supplied a sequence which requires nameless (qmark) " From ceb6038b053c403bed3ca3a8bd17b7e3fc9aab7d Mon Sep 17 00:00:00 2001 From: Kerim Kabirov Date: Mon, 22 Apr 2024 12:28:21 +0200 Subject: [PATCH 004/217] gh-115986 Improve pprint documentation accuracy (#117403) Co-authored-by: Jelle Zijlstra --- Doc/library/pprint.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index eebd270a096ba5..6dfea25d755f75 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -19,9 +19,8 @@ such as files, sockets or classes are included, as well as many other objects which are not representable as Python literals. The formatted representation keeps objects on a single line if it can, and -breaks them onto multiple lines if they don't fit within the allowed width. -Construct :class:`PrettyPrinter` objects explicitly if you need to adjust the -width constraint. +breaks them onto multiple lines if they don't fit within the allowed width, +adjustable by the *width* parameter defaulting to 80 characters. Dictionaries are sorted by key before the display is computed. From a6647d16abf4dd65997865e857371673238e60bf Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 22 Apr 2024 13:34:06 +0100 Subject: [PATCH 005/217] GH-115480: Reduce guard strength for binary ops when type of one operand is known already (GH-118050) --- Include/internal/pycore_optimizer.h | 1 + Include/internal/pycore_uop_ids.h | 168 +++++++++++++------------ Include/internal/pycore_uop_metadata.h | 16 +++ Lib/test/test_capi/test_opt.py | 44 ++++++- Python/bytecodes.c | 16 +++ Python/executor_cases.c.h | 40 ++++++ Python/optimizer_analysis.c | 1 + Python/optimizer_bytecodes.c | 51 ++++++-- Python/optimizer_cases.c.h | 68 ++++++++-- Python/optimizer_symbols.c | 14 ++- 10 files changed, 316 insertions(+), 103 deletions(-) diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index 44cafe61b75596..c0a76e85350541 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -98,6 +98,7 @@ extern bool _Py_uop_sym_set_type(_Py_UopsSymbol *sym, PyTypeObject *typ); extern bool _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val); extern bool _Py_uop_sym_is_bottom(_Py_UopsSymbol *sym); extern int _Py_uop_sym_truthiness(_Py_UopsSymbol *sym); +extern PyTypeObject *_Py_uop_sym_get_type(_Py_UopsSymbol *sym); extern int _Py_uop_abstractcontext_init(_Py_UOpsContext *ctx); diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 3e4dd8b4009cd4..f0558743b32f5e 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -118,17 +118,21 @@ extern "C" { #define _GUARD_IS_NOT_NONE_POP 356 #define _GUARD_IS_TRUE_POP 357 #define _GUARD_KEYS_VERSION 358 -#define _GUARD_NOT_EXHAUSTED_LIST 359 -#define _GUARD_NOT_EXHAUSTED_RANGE 360 -#define _GUARD_NOT_EXHAUSTED_TUPLE 361 -#define _GUARD_TYPE_VERSION 362 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 363 -#define _INIT_CALL_PY_EXACT_ARGS 364 -#define _INIT_CALL_PY_EXACT_ARGS_0 365 -#define _INIT_CALL_PY_EXACT_ARGS_1 366 -#define _INIT_CALL_PY_EXACT_ARGS_2 367 -#define _INIT_CALL_PY_EXACT_ARGS_3 368 -#define _INIT_CALL_PY_EXACT_ARGS_4 369 +#define _GUARD_NOS_FLOAT 359 +#define _GUARD_NOS_INT 360 +#define _GUARD_NOT_EXHAUSTED_LIST 361 +#define _GUARD_NOT_EXHAUSTED_RANGE 362 +#define _GUARD_NOT_EXHAUSTED_TUPLE 363 +#define _GUARD_TOS_FLOAT 364 +#define _GUARD_TOS_INT 365 +#define _GUARD_TYPE_VERSION 366 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 367 +#define _INIT_CALL_PY_EXACT_ARGS 368 +#define _INIT_CALL_PY_EXACT_ARGS_0 369 +#define _INIT_CALL_PY_EXACT_ARGS_1 370 +#define _INIT_CALL_PY_EXACT_ARGS_2 371 +#define _INIT_CALL_PY_EXACT_ARGS_3 372 +#define _INIT_CALL_PY_EXACT_ARGS_4 373 #define _INSTRUMENTED_CALL INSTRUMENTED_CALL #define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX #define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW @@ -145,65 +149,65 @@ extern "C" { #define _INSTRUMENTED_RETURN_CONST INSTRUMENTED_RETURN_CONST #define _INSTRUMENTED_RETURN_VALUE INSTRUMENTED_RETURN_VALUE #define _INSTRUMENTED_YIELD_VALUE INSTRUMENTED_YIELD_VALUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 370 -#define _IS_NONE 371 +#define _INTERNAL_INCREMENT_OPT_COUNTER 374 +#define _IS_NONE 375 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 372 -#define _ITER_CHECK_RANGE 373 -#define _ITER_CHECK_TUPLE 374 -#define _ITER_JUMP_LIST 375 -#define _ITER_JUMP_RANGE 376 -#define _ITER_JUMP_TUPLE 377 -#define _ITER_NEXT_LIST 378 -#define _ITER_NEXT_RANGE 379 -#define _ITER_NEXT_TUPLE 380 -#define _JUMP_TO_TOP 381 +#define _ITER_CHECK_LIST 376 +#define _ITER_CHECK_RANGE 377 +#define _ITER_CHECK_TUPLE 378 +#define _ITER_JUMP_LIST 379 +#define _ITER_JUMP_RANGE 380 +#define _ITER_JUMP_TUPLE 381 +#define _ITER_NEXT_LIST 382 +#define _ITER_NEXT_RANGE 383 +#define _ITER_NEXT_TUPLE 384 +#define _JUMP_TO_TOP 385 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND #define _LOAD_ASSERTION_ERROR LOAD_ASSERTION_ERROR -#define _LOAD_ATTR 382 -#define _LOAD_ATTR_CLASS 383 -#define _LOAD_ATTR_CLASS_0 384 -#define _LOAD_ATTR_CLASS_1 385 +#define _LOAD_ATTR 386 +#define _LOAD_ATTR_CLASS 387 +#define _LOAD_ATTR_CLASS_0 388 +#define _LOAD_ATTR_CLASS_1 389 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 386 -#define _LOAD_ATTR_INSTANCE_VALUE_0 387 -#define _LOAD_ATTR_INSTANCE_VALUE_1 388 -#define _LOAD_ATTR_METHOD_LAZY_DICT 389 -#define _LOAD_ATTR_METHOD_NO_DICT 390 -#define _LOAD_ATTR_METHOD_WITH_VALUES 391 -#define _LOAD_ATTR_MODULE 392 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 393 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 394 +#define _LOAD_ATTR_INSTANCE_VALUE 390 +#define _LOAD_ATTR_INSTANCE_VALUE_0 391 +#define _LOAD_ATTR_INSTANCE_VALUE_1 392 +#define _LOAD_ATTR_METHOD_LAZY_DICT 393 +#define _LOAD_ATTR_METHOD_NO_DICT 394 +#define _LOAD_ATTR_METHOD_WITH_VALUES 395 +#define _LOAD_ATTR_MODULE 396 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 397 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 398 #define _LOAD_ATTR_PROPERTY LOAD_ATTR_PROPERTY -#define _LOAD_ATTR_SLOT 395 -#define _LOAD_ATTR_SLOT_0 396 -#define _LOAD_ATTR_SLOT_1 397 -#define _LOAD_ATTR_WITH_HINT 398 +#define _LOAD_ATTR_SLOT 399 +#define _LOAD_ATTR_SLOT_0 400 +#define _LOAD_ATTR_SLOT_1 401 +#define _LOAD_ATTR_WITH_HINT 402 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS #define _LOAD_CONST LOAD_CONST -#define _LOAD_CONST_INLINE 399 -#define _LOAD_CONST_INLINE_BORROW 400 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 401 -#define _LOAD_CONST_INLINE_WITH_NULL 402 +#define _LOAD_CONST_INLINE 403 +#define _LOAD_CONST_INLINE_BORROW 404 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 405 +#define _LOAD_CONST_INLINE_WITH_NULL 406 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 403 -#define _LOAD_FAST_0 404 -#define _LOAD_FAST_1 405 -#define _LOAD_FAST_2 406 -#define _LOAD_FAST_3 407 -#define _LOAD_FAST_4 408 -#define _LOAD_FAST_5 409 -#define _LOAD_FAST_6 410 -#define _LOAD_FAST_7 411 +#define _LOAD_FAST 407 +#define _LOAD_FAST_0 408 +#define _LOAD_FAST_1 409 +#define _LOAD_FAST_2 410 +#define _LOAD_FAST_3 411 +#define _LOAD_FAST_4 412 +#define _LOAD_FAST_5 413 +#define _LOAD_FAST_6 414 +#define _LOAD_FAST_7 415 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 412 -#define _LOAD_GLOBAL_BUILTINS 413 -#define _LOAD_GLOBAL_MODULE 414 +#define _LOAD_GLOBAL 416 +#define _LOAD_GLOBAL_BUILTINS 417 +#define _LOAD_GLOBAL_MODULE 418 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR @@ -217,49 +221,49 @@ extern "C" { #define _MATCH_SEQUENCE MATCH_SEQUENCE #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_FRAME 415 -#define _POP_JUMP_IF_FALSE 416 -#define _POP_JUMP_IF_TRUE 417 +#define _POP_FRAME 419 +#define _POP_JUMP_IF_FALSE 420 +#define _POP_JUMP_IF_TRUE 421 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 418 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 422 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 419 +#define _PUSH_FRAME 423 #define _PUSH_NULL PUSH_NULL -#define _REPLACE_WITH_TRUE 420 +#define _REPLACE_WITH_TRUE 424 #define _RESUME_CHECK RESUME_CHECK -#define _SAVE_RETURN_OFFSET 421 -#define _SEND 422 +#define _SAVE_RETURN_OFFSET 425 +#define _SEND 426 #define _SEND_GEN SEND_GEN #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _SIDE_EXIT 423 -#define _START_EXECUTOR 424 -#define _STORE_ATTR 425 -#define _STORE_ATTR_INSTANCE_VALUE 426 -#define _STORE_ATTR_SLOT 427 +#define _SIDE_EXIT 427 +#define _START_EXECUTOR 428 +#define _STORE_ATTR 429 +#define _STORE_ATTR_INSTANCE_VALUE 430 +#define _STORE_ATTR_SLOT 431 #define _STORE_ATTR_WITH_HINT STORE_ATTR_WITH_HINT #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 428 -#define _STORE_FAST_0 429 -#define _STORE_FAST_1 430 -#define _STORE_FAST_2 431 -#define _STORE_FAST_3 432 -#define _STORE_FAST_4 433 -#define _STORE_FAST_5 434 -#define _STORE_FAST_6 435 -#define _STORE_FAST_7 436 +#define _STORE_FAST 432 +#define _STORE_FAST_0 433 +#define _STORE_FAST_1 434 +#define _STORE_FAST_2 435 +#define _STORE_FAST_3 436 +#define _STORE_FAST_4 437 +#define _STORE_FAST_5 438 +#define _STORE_FAST_6 439 +#define _STORE_FAST_7 440 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME #define _STORE_SLICE STORE_SLICE -#define _STORE_SUBSCR 437 +#define _STORE_SUBSCR 441 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TO_BOOL 438 +#define _TO_BOOL 442 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -269,12 +273,12 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 439 +#define _UNPACK_SEQUENCE 443 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START -#define MAX_UOP_ID 439 +#define MAX_UOP_ID 443 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 44ede3e77c68e1..4d15be6317d615 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -59,10 +59,14 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_REPLACE_WITH_TRUE] = 0, [_UNARY_INVERT] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GUARD_BOTH_INT] = HAS_EXIT_FLAG, + [_GUARD_NOS_INT] = HAS_EXIT_FLAG, + [_GUARD_TOS_INT] = HAS_EXIT_FLAG, [_BINARY_OP_MULTIPLY_INT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, [_BINARY_OP_ADD_INT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, [_BINARY_OP_SUBTRACT_INT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, [_GUARD_BOTH_FLOAT] = HAS_EXIT_FLAG, + [_GUARD_NOS_FLOAT] = HAS_EXIT_FLAG, + [_GUARD_TOS_FLOAT] = HAS_EXIT_FLAG, [_BINARY_OP_MULTIPLY_FLOAT] = HAS_PURE_FLAG, [_BINARY_OP_ADD_FLOAT] = HAS_PURE_FLAG, [_BINARY_OP_SUBTRACT_FLOAT] = HAS_PURE_FLAG, @@ -352,9 +356,13 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_GUARD_IS_NOT_NONE_POP] = "_GUARD_IS_NOT_NONE_POP", [_GUARD_IS_TRUE_POP] = "_GUARD_IS_TRUE_POP", [_GUARD_KEYS_VERSION] = "_GUARD_KEYS_VERSION", + [_GUARD_NOS_FLOAT] = "_GUARD_NOS_FLOAT", + [_GUARD_NOS_INT] = "_GUARD_NOS_INT", [_GUARD_NOT_EXHAUSTED_LIST] = "_GUARD_NOT_EXHAUSTED_LIST", [_GUARD_NOT_EXHAUSTED_RANGE] = "_GUARD_NOT_EXHAUSTED_RANGE", [_GUARD_NOT_EXHAUSTED_TUPLE] = "_GUARD_NOT_EXHAUSTED_TUPLE", + [_GUARD_TOS_FLOAT] = "_GUARD_TOS_FLOAT", + [_GUARD_TOS_INT] = "_GUARD_TOS_INT", [_GUARD_TYPE_VERSION] = "_GUARD_TYPE_VERSION", [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = "_INIT_CALL_BOUND_METHOD_EXACT_ARGS", [_INIT_CALL_PY_EXACT_ARGS] = "_INIT_CALL_PY_EXACT_ARGS", @@ -566,6 +574,10 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _GUARD_BOTH_INT: return 2; + case _GUARD_NOS_INT: + return 2; + case _GUARD_TOS_INT: + return 1; case _BINARY_OP_MULTIPLY_INT: return 2; case _BINARY_OP_ADD_INT: @@ -574,6 +586,10 @@ int _PyUop_num_popped(int opcode, int oparg) return 2; case _GUARD_BOTH_FLOAT: return 2; + case _GUARD_NOS_FLOAT: + return 2; + case _GUARD_TOS_FLOAT: + return 1; case _BINARY_OP_MULTIPLY_FLOAT: return 2; case _BINARY_OP_ADD_FLOAT: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 28d18739b6d4a5..ae23eadb8aafa0 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -903,10 +903,50 @@ def testfunc(n): self.assertTrue(res) self.assertIsNotNone(ex) uops = get_opnames(ex) - guard_both_float_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_INT"] - self.assertLessEqual(len(guard_both_float_count), 1) + guard_both_int_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_INT"] + self.assertLessEqual(len(guard_both_int_count), 1) self.assertIn("_COMPARE_OP_INT", uops) + def test_compare_op_type_propagation_int_partial(self): + def testfunc(n): + a = 1 + for _ in range(n): + if a > 2: + x = 0 + if a < 2: + x = 1 + return x + + res, ex = self._run_with_optimizer(testfunc, 32) + self.assertEqual(res, 1) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + guard_left_int_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_NOS_INT"] + guard_both_int_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_INT"] + self.assertLessEqual(len(guard_left_int_count), 1) + self.assertEqual(len(guard_both_int_count), 0) + self.assertIn("_COMPARE_OP_INT", uops) + + def test_compare_op_type_propagation_float_partial(self): + def testfunc(n): + a = 1.0 + for _ in range(n): + if a > 2.0: + x = 0 + if a < 2.0: + x = 1 + return x + + res, ex = self._run_with_optimizer(testfunc, 32) + self.assertEqual(res, 1) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + guard_left_float_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_NOS_FLOAT"] + guard_both_float_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_FLOAT"] + self.assertLessEqual(len(guard_left_float_count), 1) + self.assertEqual(len(guard_both_float_count), 0) + self.assertIn("_COMPARE_OP_FLOAT", uops) + def test_compare_op_type_propagation_unicode(self): def testfunc(n): a = "" diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b7511b9107fdf6..4541eb635da015 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -426,6 +426,14 @@ dummy_func( EXIT_IF(!PyLong_CheckExact(right)); } + op(_GUARD_NOS_INT, (left, unused -- left, unused)) { + EXIT_IF(!PyLong_CheckExact(left)); + } + + op(_GUARD_TOS_INT, (value -- value)) { + EXIT_IF(!PyLong_CheckExact(value)); + } + pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) { STAT_INC(BINARY_OP, hit); res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); @@ -462,6 +470,14 @@ dummy_func( EXIT_IF(!PyFloat_CheckExact(right)); } + op(_GUARD_NOS_FLOAT, (left, unused -- left, unused)) { + EXIT_IF(!PyFloat_CheckExact(left)); + } + + op(_GUARD_TOS_FLOAT, (value -- value)) { + EXIT_IF(!PyFloat_CheckExact(value)); + } + pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) { STAT_INC(BINARY_OP, hit); double dres = diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 841ce8cbedb3fb..43b022107a9ae6 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -447,6 +447,26 @@ break; } + case _GUARD_NOS_INT: { + PyObject *left; + left = stack_pointer[-2]; + if (!PyLong_CheckExact(left)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + + case _GUARD_TOS_INT: { + PyObject *value; + value = stack_pointer[-1]; + if (!PyLong_CheckExact(value)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + case _BINARY_OP_MULTIPLY_INT: { PyObject *right; PyObject *left; @@ -511,6 +531,26 @@ break; } + case _GUARD_NOS_FLOAT: { + PyObject *left; + left = stack_pointer[-2]; + if (!PyFloat_CheckExact(left)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + + case _GUARD_TOS_FLOAT: { + PyObject *value; + value = stack_pointer[-1]; + if (!PyFloat_CheckExact(value)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + case _BINARY_OP_MULTIPLY_FLOAT: { PyObject *right; PyObject *left; diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 155f7026b041b0..76de6e50f1f786 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -320,6 +320,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, #define sym_new_const _Py_uop_sym_new_const #define sym_new_null _Py_uop_sym_new_null #define sym_has_type _Py_uop_sym_has_type +#define sym_get_type _Py_uop_sym_get_type #define sym_matches_type _Py_uop_sym_matches_type #define sym_set_null _Py_uop_sym_set_null #define sym_set_non_null _Py_uop_sym_set_non_null diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index f119b8e20719fa..481fb8387af416 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -21,6 +21,7 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame; #define sym_new_const _Py_uop_sym_new_const #define sym_new_null _Py_uop_sym_new_null #define sym_matches_type _Py_uop_sym_matches_type +#define sym_get_type _Py_uop_sym_get_type #define sym_has_type _Py_uop_sym_has_type #define sym_set_null _Py_uop_sym_set_null #define sym_set_non_null _Py_uop_sym_set_non_null @@ -99,9 +100,18 @@ dummy_func(void) { } op(_GUARD_BOTH_INT, (left, right -- left, right)) { - if (sym_matches_type(left, &PyLong_Type) && - sym_matches_type(right, &PyLong_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + if (sym_matches_type(left, &PyLong_Type)) { + if (sym_matches_type(right, &PyLong_Type)) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + else { + REPLACE_OP(this_instr, _GUARD_TOS_INT, 0, 0); + } + } + else { + if (sym_matches_type(right, &PyLong_Type)) { + REPLACE_OP(this_instr, _GUARD_NOS_INT, 0, 0); + } } if (!sym_set_type(left, &PyLong_Type)) { goto hit_bottom; @@ -112,9 +122,18 @@ dummy_func(void) { } op(_GUARD_BOTH_FLOAT, (left, right -- left, right)) { - if (sym_matches_type(left, &PyFloat_Type) && - sym_matches_type(right, &PyFloat_Type)) { - REPLACE_OP(this_instr, _NOP, 0 ,0); + if (sym_matches_type(left, &PyFloat_Type)) { + if (sym_matches_type(right, &PyFloat_Type)) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + else { + REPLACE_OP(this_instr, _GUARD_TOS_FLOAT, 0, 0); + } + } + else { + if (sym_matches_type(right, &PyFloat_Type)) { + REPLACE_OP(this_instr, _GUARD_NOS_FLOAT, 0, 0); + } } if (!sym_set_type(left, &PyFloat_Type)) { goto hit_bottom; @@ -137,6 +156,25 @@ dummy_func(void) { } } + op(_BINARY_OP, (left, right -- res)) { + PyTypeObject *ltype = sym_get_type(left); + PyTypeObject *rtype = sym_get_type(right); + if (ltype != NULL && (ltype == &PyLong_Type || ltype == &PyFloat_Type) && + rtype != NULL && (rtype == &PyLong_Type || rtype == &PyFloat_Type)) + { + if (oparg != NB_TRUE_DIVIDE && oparg != NB_INPLACE_TRUE_DIVIDE && + ltype == &PyLong_Type && rtype == &PyLong_Type) { + /* If both inputs are ints and the op is not division the result is an int */ + OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyLong_Type)); + } + else { + /* For any other op combining ints/floats the result is a float */ + OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyFloat_Type)); + } + } + OUT_OF_SPACE_IF_NULL(res = sym_new_unknown(ctx)); + } + op(_BINARY_OP_ADD_INT, (left, right -- res)) { if (sym_is_const(left) && sym_is_const(right) && sym_matches_type(left, &PyLong_Type) && sym_matches_type(right, &PyLong_Type)) @@ -424,7 +462,6 @@ dummy_func(void) { OUT_OF_SPACE_IF_NULL(null = sym_new_null(ctx)); } - op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { assert(oparg > 0); top = bottom; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 50f335e0c8a0a2..0a7d96d30ad3e8 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -225,9 +225,18 @@ _Py_UopsSymbol *left; right = stack_pointer[-1]; left = stack_pointer[-2]; - if (sym_matches_type(left, &PyLong_Type) && - sym_matches_type(right, &PyLong_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + if (sym_matches_type(left, &PyLong_Type)) { + if (sym_matches_type(right, &PyLong_Type)) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + else { + REPLACE_OP(this_instr, _GUARD_TOS_INT, 0, 0); + } + } + else { + if (sym_matches_type(right, &PyLong_Type)) { + REPLACE_OP(this_instr, _GUARD_NOS_INT, 0, 0); + } } if (!sym_set_type(left, &PyLong_Type)) { goto hit_bottom; @@ -238,6 +247,14 @@ break; } + case _GUARD_NOS_INT: { + break; + } + + case _GUARD_TOS_INT: { + break; + } + case _BINARY_OP_MULTIPLY_INT: { _Py_UopsSymbol *right; _Py_UopsSymbol *left; @@ -333,9 +350,18 @@ _Py_UopsSymbol *left; right = stack_pointer[-1]; left = stack_pointer[-2]; - if (sym_matches_type(left, &PyFloat_Type) && - sym_matches_type(right, &PyFloat_Type)) { - REPLACE_OP(this_instr, _NOP, 0 ,0); + if (sym_matches_type(left, &PyFloat_Type)) { + if (sym_matches_type(right, &PyFloat_Type)) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + else { + REPLACE_OP(this_instr, _GUARD_TOS_FLOAT, 0, 0); + } + } + else { + if (sym_matches_type(right, &PyFloat_Type)) { + REPLACE_OP(this_instr, _GUARD_NOS_FLOAT, 0, 0); + } } if (!sym_set_type(left, &PyFloat_Type)) { goto hit_bottom; @@ -346,6 +372,14 @@ break; } + case _GUARD_NOS_FLOAT: { + break; + } + + case _GUARD_TOS_FLOAT: { + break; + } + case _BINARY_OP_MULTIPLY_FLOAT: { _Py_UopsSymbol *right; _Py_UopsSymbol *left; @@ -1852,9 +1886,27 @@ } case _BINARY_OP: { + _Py_UopsSymbol *right; + _Py_UopsSymbol *left; _Py_UopsSymbol *res; - res = sym_new_not_null(ctx); - if (res == NULL) goto out_of_space; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyTypeObject *ltype = sym_get_type(left); + PyTypeObject *rtype = sym_get_type(right); + if (ltype != NULL && (ltype == &PyLong_Type || ltype == &PyFloat_Type) && + rtype != NULL && (rtype == &PyLong_Type || rtype == &PyFloat_Type)) + { + if (oparg != NB_TRUE_DIVIDE && oparg != NB_INPLACE_TRUE_DIVIDE && + ltype == &PyLong_Type && rtype == &PyLong_Type) { + /* If both inputs are ints and the op is not division the result is an int */ + OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyLong_Type)); + } + else { + /* For any other op combining ints/floats the result is a float */ + OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyFloat_Type)); + } + } + OUT_OF_SPACE_IF_NULL(res = sym_new_unknown(ctx)); stack_pointer[-2] = res; stack_pointer += -1; break; diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c index 86b0d4d395afa2..204599b08766c3 100644 --- a/Python/optimizer_symbols.c +++ b/Python/optimizer_symbols.c @@ -231,6 +231,15 @@ _Py_uop_sym_new_null(_Py_UOpsContext *ctx) return null_sym; } +PyTypeObject * +_Py_uop_sym_get_type(_Py_UopsSymbol *sym) +{ + if (_Py_uop_sym_is_bottom(sym)) { + return NULL; + } + return sym->typ; +} + bool _Py_uop_sym_has_type(_Py_UopsSymbol *sym) { @@ -244,10 +253,7 @@ bool _Py_uop_sym_matches_type(_Py_UopsSymbol *sym, PyTypeObject *typ) { assert(typ != NULL && PyType_Check(typ)); - if (_Py_uop_sym_is_bottom(sym)) { - return false; - } - return sym->typ == typ; + return _Py_uop_sym_get_type(sym) == typ; } int From 287d939ed4445089e8312ab44110cbb6b6306a5c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 22 Apr 2024 16:27:47 +0300 Subject: [PATCH 006/217] gh-118148: Improve tests for shutil.make_archive() (GH-118149) --- Lib/test/test_shutil.py | 247 ++++++++++++++++++++++++++++------------ 1 file changed, 176 insertions(+), 71 deletions(-) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 60e88d57b2b23d..3aa0ec6952339b 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1615,42 +1615,6 @@ class TestArchives(BaseTest, unittest.TestCase): ### shutil.make_archive - @support.requires_zlib() - def test_make_tarball(self): - # creating something to tar - root_dir, base_dir = self._create_files('') - - tmpdir2 = self.mkdtemp() - # force shutil to create the directory - os.rmdir(tmpdir2) - # working with relative paths - work_dir = os.path.dirname(tmpdir2) - rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive') - - with os_helper.change_cwd(work_dir), no_chdir: - base_name = os.path.abspath(rel_base_name) - tarball = make_archive(rel_base_name, 'gztar', root_dir, '.') - - # check if the compressed tarball was created - self.assertEqual(tarball, base_name + '.tar.gz') - self.assertTrue(os.path.isfile(tarball)) - self.assertTrue(tarfile.is_tarfile(tarball)) - with tarfile.open(tarball, 'r:gz') as tf: - self.assertCountEqual(tf.getnames(), - ['.', './sub', './sub2', - './file1', './file2', './sub/file3']) - - # trying an uncompressed one - with os_helper.change_cwd(work_dir), no_chdir: - tarball = make_archive(rel_base_name, 'tar', root_dir, '.') - self.assertEqual(tarball, base_name + '.tar') - self.assertTrue(os.path.isfile(tarball)) - self.assertTrue(tarfile.is_tarfile(tarball)) - with tarfile.open(tarball, 'r') as tf: - self.assertCountEqual(tf.getnames(), - ['.', './sub', './sub2', - './file1', './file2', './sub/file3']) - def _tarinfo(self, path): with tarfile.open(path) as tar: names = tar.getnames() @@ -1671,6 +1635,92 @@ def _create_files(self, base_dir='dist'): write_file((root_dir, 'outer'), 'xxx') return root_dir, base_dir + @support.requires_zlib() + def test_make_tarfile(self): + root_dir, base_dir = self._create_files() + # Test without base_dir. + with os_helper.temp_cwd(), no_chdir: + base_name = os.path.join('dst', 'archive') + archive = make_archive(base_name, 'tar', root_dir) + # check if the compressed tarball was created + self.assertEqual(archive, os.path.abspath(base_name) + '.tar') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(tarfile.is_tarfile(archive)) + with tarfile.open(archive, 'r') as tf: + self.assertCountEqual(tf.getnames(), + ['.', './dist', './dist/sub', './dist/sub2', + './dist/file1', './dist/file2', './dist/sub/file3', + './outer']) + + # Test with base_dir. + with os_helper.temp_cwd(), no_chdir: + base_name = os.path.join('dst2', 'archive') + archive = make_archive(base_name, 'tar', root_dir, base_dir) + self.assertEqual(archive, os.path.abspath(base_name) + '.tar') + # check if the uncompressed tarball was created + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(tarfile.is_tarfile(archive)) + with tarfile.open(archive, 'r') as tf: + self.assertCountEqual(tf.getnames(), + ['dist', 'dist/sub', 'dist/sub2', + 'dist/file1', 'dist/file2', 'dist/sub/file3']) + + # Test with multi-component base_dir. + with os_helper.temp_cwd(), no_chdir: + base_name = os.path.join('dst3', 'archive') + archive = make_archive(base_name, 'tar', root_dir, + os.path.join(base_dir, 'sub')) + self.assertEqual(archive, os.path.abspath(base_name) + '.tar') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(tarfile.is_tarfile(archive)) + with tarfile.open(archive, 'r') as tf: + self.assertCountEqual(tf.getnames(), + ['dist/sub', 'dist/sub/file3']) + + @support.requires_zlib() + def test_make_tarfile_without_rootdir(self): + root_dir, base_dir = self._create_files() + # Test without base_dir. + base_name = os.path.join(self.mkdtemp(), 'dst', 'archive') + base_name = os.path.relpath(base_name, root_dir) + with os_helper.change_cwd(root_dir), no_chdir: + archive = make_archive(base_name, 'gztar') + self.assertEqual(archive, base_name + '.tar.gz') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(tarfile.is_tarfile(archive)) + with tarfile.open(archive, 'r:gz') as tf: + self.assertCountEqual(tf.getnames(), + ['.', './dist', './dist/sub', './dist/sub2', + './dist/file1', './dist/file2', './dist/sub/file3', + './outer']) + + # Test with base_dir. + with os_helper.change_cwd(root_dir), no_chdir: + base_name = os.path.join('dst', 'archive') + archive = make_archive(base_name, 'tar', base_dir=base_dir) + self.assertEqual(archive, base_name + '.tar') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(tarfile.is_tarfile(archive)) + with tarfile.open(archive, 'r') as tf: + self.assertCountEqual(tf.getnames(), + ['dist', 'dist/sub', 'dist/sub2', + 'dist/file1', 'dist/file2', 'dist/sub/file3']) + + def test_make_tarfile_with_explicit_curdir(self): + # Test with base_dir=os.curdir. + root_dir, base_dir = self._create_files() + with os_helper.temp_cwd(), no_chdir: + base_name = os.path.join('dst', 'archive') + archive = make_archive(base_name, 'tar', root_dir, os.curdir) + self.assertEqual(archive, os.path.abspath(base_name) + '.tar') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(tarfile.is_tarfile(archive)) + with tarfile.open(archive, 'r') as tf: + self.assertCountEqual(tf.getnames(), + ['.', './dist', './dist/sub', './dist/sub2', + './dist/file1', './dist/file2', './dist/sub/file3', + './outer']) + @support.requires_zlib() @unittest.skipUnless(shutil.which('tar'), 'Need the tar command to run') @@ -1720,40 +1770,89 @@ def test_tarfile_vs_tar(self): @support.requires_zlib() def test_make_zipfile(self): - # creating something to zip root_dir, base_dir = self._create_files() + # Test without base_dir. + with os_helper.temp_cwd(), no_chdir: + base_name = os.path.join('dst', 'archive') + archive = make_archive(base_name, 'zip', root_dir) + self.assertEqual(archive, os.path.abspath(base_name) + '.zip') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(zipfile.is_zipfile(archive)) + with zipfile.ZipFile(archive) as zf: + self.assertCountEqual(zf.namelist(), + ['dist/', 'dist/sub/', 'dist/sub2/', + 'dist/file1', 'dist/file2', 'dist/sub/file3', + 'outer']) + + # Test with base_dir. + with os_helper.temp_cwd(), no_chdir: + base_name = os.path.join('dst2', 'archive') + archive = make_archive(base_name, 'zip', root_dir, base_dir) + self.assertEqual(archive, os.path.abspath(base_name) + '.zip') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(zipfile.is_zipfile(archive)) + with zipfile.ZipFile(archive) as zf: + self.assertCountEqual(zf.namelist(), + ['dist/', 'dist/sub/', 'dist/sub2/', + 'dist/file1', 'dist/file2', 'dist/sub/file3']) + + # Test with multi-component base_dir. + with os_helper.temp_cwd(), no_chdir: + base_name = os.path.join('dst3', 'archive') + archive = make_archive(base_name, 'zip', root_dir, + os.path.join(base_dir, 'sub')) + self.assertEqual(archive, os.path.abspath(base_name) + '.zip') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(zipfile.is_zipfile(archive)) + with zipfile.ZipFile(archive) as zf: + self.assertCountEqual(zf.namelist(), + ['dist/sub/', 'dist/sub/file3']) - tmpdir2 = self.mkdtemp() - # force shutil to create the directory - os.rmdir(tmpdir2) - # working with relative paths - work_dir = os.path.dirname(tmpdir2) - rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive') - - with os_helper.change_cwd(work_dir), no_chdir: - base_name = os.path.abspath(rel_base_name) - res = make_archive(rel_base_name, 'zip', root_dir) + @support.requires_zlib() + def test_make_zipfile_without_rootdir(self): + root_dir, base_dir = self._create_files() + # Test without base_dir. + base_name = os.path.join(self.mkdtemp(), 'dst', 'archive') + base_name = os.path.relpath(base_name, root_dir) + with os_helper.change_cwd(root_dir), no_chdir: + archive = make_archive(base_name, 'zip') + self.assertEqual(archive, base_name + '.zip') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(zipfile.is_zipfile(archive)) + with zipfile.ZipFile(archive) as zf: + self.assertCountEqual(zf.namelist(), + ['dist/', 'dist/sub/', 'dist/sub2/', + 'dist/file1', 'dist/file2', 'dist/sub/file3', + 'outer']) + + # Test with base_dir. + root_dir, base_dir = self._create_files() + with os_helper.change_cwd(root_dir), no_chdir: + base_name = os.path.join('dst', 'archive') + archive = make_archive(base_name, 'zip', base_dir=base_dir) + self.assertEqual(archive, base_name + '.zip') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(zipfile.is_zipfile(archive)) + with zipfile.ZipFile(archive) as zf: + self.assertCountEqual(zf.namelist(), + ['dist/', 'dist/sub/', 'dist/sub2/', + 'dist/file1', 'dist/file2', 'dist/sub/file3']) - self.assertEqual(res, base_name + '.zip') - self.assertTrue(os.path.isfile(res)) - self.assertTrue(zipfile.is_zipfile(res)) - with zipfile.ZipFile(res) as zf: - self.assertCountEqual(zf.namelist(), - ['dist/', 'dist/sub/', 'dist/sub2/', - 'dist/file1', 'dist/file2', 'dist/sub/file3', - 'outer']) - - with os_helper.change_cwd(work_dir), no_chdir: - base_name = os.path.abspath(rel_base_name) - res = make_archive(rel_base_name, 'zip', root_dir, base_dir) - - self.assertEqual(res, base_name + '.zip') - self.assertTrue(os.path.isfile(res)) - self.assertTrue(zipfile.is_zipfile(res)) - with zipfile.ZipFile(res) as zf: - self.assertCountEqual(zf.namelist(), - ['dist/', 'dist/sub/', 'dist/sub2/', - 'dist/file1', 'dist/file2', 'dist/sub/file3']) + @support.requires_zlib() + def test_make_zipfile_with_explicit_curdir(self): + # Test with base_dir=os.curdir. + root_dir, base_dir = self._create_files() + with os_helper.temp_cwd(), no_chdir: + base_name = os.path.join('dst', 'archive') + archive = make_archive(base_name, 'zip', root_dir, os.curdir) + self.assertEqual(archive, os.path.abspath(base_name) + '.zip') + self.assertTrue(os.path.isfile(archive)) + self.assertTrue(zipfile.is_zipfile(archive)) + with zipfile.ZipFile(archive) as zf: + self.assertCountEqual(zf.namelist(), + ['dist/', 'dist/sub/', 'dist/sub2/', + 'dist/file1', 'dist/file2', 'dist/sub/file3', + 'outer']) @support.requires_zlib() @unittest.skipUnless(shutil.which('zip'), @@ -1923,17 +2022,19 @@ def archiver(base_name, base_dir, **kw): unregister_archive_format('xxx') def test_make_tarfile_in_curdir(self): - # Issue #21280 + # Issue #21280: Test with the archive in the current directory. root_dir = self.mkdtemp() with os_helper.change_cwd(root_dir), no_chdir: + # root_dir must be None, so the archive path is relative. self.assertEqual(make_archive('test', 'tar'), 'test.tar') self.assertTrue(os.path.isfile('test.tar')) @support.requires_zlib() def test_make_zipfile_in_curdir(self): - # Issue #21280 + # Issue #21280: Test with the archive in the current directory. root_dir = self.mkdtemp() with os_helper.change_cwd(root_dir), no_chdir: + # root_dir must be None, so the archive path is relative. self.assertEqual(make_archive('test', 'zip'), 'test.zip') self.assertTrue(os.path.isfile('test.zip')) @@ -1954,10 +2055,11 @@ def test_register_archive_format(self): self.assertNotIn('xxx', formats) def test_make_tarfile_rootdir_nodir(self): - # GH-99203 + # GH-99203: Test with root_dir is not a real directory. self.addCleanup(os_helper.unlink, f'{TESTFN}.tar') for dry_run in (False, True): with self.subTest(dry_run=dry_run): + # root_dir does not exist. tmp_dir = self.mkdtemp() nonexisting_file = os.path.join(tmp_dir, 'nonexisting') with self.assertRaises(FileNotFoundError) as cm: @@ -1966,6 +2068,7 @@ def test_make_tarfile_rootdir_nodir(self): self.assertEqual(cm.exception.filename, nonexisting_file) self.assertFalse(os.path.exists(f'{TESTFN}.tar')) + # root_dir is a file. tmp_fd, tmp_file = tempfile.mkstemp(dir=tmp_dir) os.close(tmp_fd) with self.assertRaises(NotADirectoryError) as cm: @@ -1976,10 +2079,11 @@ def test_make_tarfile_rootdir_nodir(self): @support.requires_zlib() def test_make_zipfile_rootdir_nodir(self): - # GH-99203 + # GH-99203: Test with root_dir is not a real directory. self.addCleanup(os_helper.unlink, f'{TESTFN}.zip') for dry_run in (False, True): with self.subTest(dry_run=dry_run): + # root_dir does not exist. tmp_dir = self.mkdtemp() nonexisting_file = os.path.join(tmp_dir, 'nonexisting') with self.assertRaises(FileNotFoundError) as cm: @@ -1988,6 +2092,7 @@ def test_make_zipfile_rootdir_nodir(self): self.assertEqual(cm.exception.filename, nonexisting_file) self.assertFalse(os.path.exists(f'{TESTFN}.zip')) + # root_dir is a file. tmp_fd, tmp_file = tempfile.mkstemp(dir=tmp_dir) os.close(tmp_fd) with self.assertRaises(NotADirectoryError) as cm: From 78ba4cb758ba1e40d27af6bc2fa15ed3e33a29d2 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 22 Apr 2024 16:57:46 +0200 Subject: [PATCH 007/217] gh-118030: Group definitions for `ParamSpecArgs` and `ParamSpecKwargs` in `typing.rst` (#118154) --- Doc/library/typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 2e71be7eaf8d03..d816e6368f40d2 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1972,7 +1972,7 @@ without the dedicated syntax, as documented below. * :ref:`annotating-callables` .. data:: ParamSpecArgs -.. data:: ParamSpecKwargs + ParamSpecKwargs Arguments and keyword arguments attributes of a :class:`ParamSpec`. The ``P.args`` attribute of a ``ParamSpec`` is an instance of ``ParamSpecArgs``, From 8974a63f5ee0557e14db97eb1284a5dd8c49f8f7 Mon Sep 17 00:00:00 2001 From: tahia Date: Mon, 22 Apr 2024 14:23:36 -0400 Subject: [PATCH 008/217] bpo-18108: Adding dir_fd and follow_symlinks keyword args to shutil.chown (GH-15811) * Adding dir_fd and follow_symlinks keyword args to shutil.chown * Extending test_shutil.TestShutil.test_chown to include new kwargs * Updating shutil.chown documentation Co-authored-by: Serhiy Storchaka Co-authored-by: Berker Peksag Co-authored-by: Zachary Ware --- Doc/library/shutil.rst | 6 +++- Doc/whatsnew/3.13.rst | 4 +++ Lib/shutil.py | 12 +++++-- Lib/test/test_shutil.py | 34 ++++++++++++++++++- .../2019-09-09-18-18-34.bpo-18108.ajPLAO.rst | 1 + 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-09-09-18-18-34.bpo-18108.ajPLAO.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 4f07b9f6040d24..8e5828c789e4e2 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -421,7 +421,8 @@ Directory and files operations .. availability:: Unix, Windows. -.. function:: chown(path, user=None, group=None) +.. function:: chown(path, user=None, group=None, *, dir_fd=None, \ + follow_symlinks=True) Change owner *user* and/or *group* of the given *path*. @@ -436,6 +437,9 @@ Directory and files operations .. versionadded:: 3.3 + .. versionchanged:: 3.13 + Added *dir_fd* and *follow_symlinks* parameters. + .. function:: which(cmd, mode=os.F_OK | os.X_OK, path=None) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 5be562030b507b..c04dc924d1efa5 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -594,6 +594,10 @@ os.path exactly one (back)slash to be absolute. (Contributed by Barney Gale and Jon Foster in :gh:`44626`.) +* Add support of *dir_fd* and *follow_symlinks* keyword arguments in + :func:`shutil.chown`. + (Contributed by Berker Peksag and Tahia K in :gh:`62308`) + pathlib ------- diff --git a/Lib/shutil.py b/Lib/shutil.py index 94b09509008b0b..910d6b6c63ac08 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -1442,11 +1442,18 @@ def disk_usage(path): return _ntuple_diskusage(total, used, free) -def chown(path, user=None, group=None): +def chown(path, user=None, group=None, *, dir_fd=None, follow_symlinks=True): """Change owner user and group of the given path. user and group can be the uid/gid or the user/group names, and in that case, they are converted to their respective uid/gid. + + If dir_fd is set, it should be an open file descriptor to the directory to + be used as the root of *path* if it is relative. + + If follow_symlinks is set to False and the last element of the path is a + symbolic link, chown will modify the link itself and not the file being + referenced by the link. """ sys.audit('shutil.chown', path, user, group) @@ -1472,7 +1479,8 @@ def chown(path, user=None, group=None): if _group is None: raise LookupError("no such group: {!r}".format(group)) - os.chown(path, _user, _group) + os.chown(path, _user, _group, dir_fd=dir_fd, + follow_symlinks=follow_symlinks) def get_terminal_size(fallback=(80, 24)): """Get the size of the terminal window. diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 3aa0ec6952339b..5b0aac67a0adeb 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2212,7 +2212,9 @@ def test_disk_usage(self): def test_chown(self): dirname = self.mkdtemp() filename = tempfile.mktemp(dir=dirname) + linkname = os.path.join(dirname, "chown_link") write_file(filename, 'testing chown function') + os.symlink(filename, linkname) with self.assertRaises(ValueError): shutil.chown(filename) @@ -2233,7 +2235,7 @@ def test_chown(self): gid = os.getgid() def check_chown(path, uid=None, gid=None): - s = os.stat(filename) + s = os.stat(path) if uid is not None: self.assertEqual(uid, s.st_uid) if gid is not None: @@ -2269,6 +2271,36 @@ def check_chown(path, uid=None, gid=None): shutil.chown(dirname, user, group) check_chown(dirname, uid, gid) + dirfd = os.open(dirname, os.O_RDONLY) + self.addCleanup(os.close, dirfd) + basename = os.path.basename(filename) + baselinkname = os.path.basename(linkname) + shutil.chown(basename, uid, gid, dir_fd=dirfd) + check_chown(filename, uid, gid) + shutil.chown(basename, uid, dir_fd=dirfd) + check_chown(filename, uid) + shutil.chown(basename, group=gid, dir_fd=dirfd) + check_chown(filename, gid=gid) + shutil.chown(basename, uid, gid, dir_fd=dirfd, follow_symlinks=True) + check_chown(filename, uid, gid) + shutil.chown(basename, uid, gid, dir_fd=dirfd, follow_symlinks=False) + check_chown(filename, uid, gid) + shutil.chown(linkname, uid, follow_symlinks=True) + check_chown(filename, uid) + shutil.chown(baselinkname, group=gid, dir_fd=dirfd, follow_symlinks=False) + check_chown(filename, gid=gid) + shutil.chown(baselinkname, uid, gid, dir_fd=dirfd, follow_symlinks=True) + check_chown(filename, uid, gid) + + with self.assertRaises(TypeError): + shutil.chown(filename, uid, dir_fd=dirname) + + with self.assertRaises(FileNotFoundError): + shutil.chown('missingfile', uid, gid, dir_fd=dirfd) + + with self.assertRaises(ValueError): + shutil.chown(filename, dir_fd=dirfd) + @support.requires_subprocess() class TestWhich(BaseTest, unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2019-09-09-18-18-34.bpo-18108.ajPLAO.rst b/Misc/NEWS.d/next/Library/2019-09-09-18-18-34.bpo-18108.ajPLAO.rst new file mode 100644 index 00000000000000..70ff76a0c920be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-09-09-18-18-34.bpo-18108.ajPLAO.rst @@ -0,0 +1 @@ +:func:`shutil.chown` now supports *dir_fd* and *follow_symlinks* keyword arguments. From 4c7bfdff90544deb5d2055fcbe4db4c2a231e95d Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 22 Apr 2024 12:17:57 -0700 Subject: [PATCH 009/217] Remove more remnants of deepfreeze (#118159) --- Include/incl.tar | Bin 0 -> 1864192 bytes Include/internal/pycore_code.h | 5 ----- Include/internal/pycore_pylifecycle.h | 2 -- Programs/_bootstrap_python.c | 11 ----------- Programs/_freeze_module.c | 11 ----------- 5 files changed, 29 deletions(-) create mode 100644 Include/incl.tar diff --git a/Include/incl.tar b/Include/incl.tar new file mode 100644 index 0000000000000000000000000000000000000000..fecc1fb5dec534148d40b4a4c3f1bcfce411a832 GIT binary patch literal 1864192 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%0DN$)T@_T(Wn2(qM_vfI#uE# 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
UW98&SdxSMC!PoVY5J#99u=5s5?@ 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%0AK?J9w$I*mRT%S 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 literal 0 HcmV?d00001 diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 1ec0348d6e5e8b..8d832c59e36874 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -288,11 +288,6 @@ extern void _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr); extern void _Py_Specialize_ToBool(PyObject *value, _Py_CODEUNIT *instr); extern void _Py_Specialize_ContainsOp(PyObject *value, _Py_CODEUNIT *instr); -/* Finalizer function for static codeobjects used in deepfreeze.py */ -extern void _PyStaticCode_Fini(PyCodeObject *co); -/* Function to intern strings of codeobjects and quicken the bytecode */ -extern int _PyStaticCode_Init(PyCodeObject *co); - #ifdef Py_STATS #include "pycore_bitutils.h" // _Py_bit_length diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 47ff0806574ac0..f426ae0e103b9c 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -42,7 +42,6 @@ extern PyStatus _Py_HashRandomization_Init(const PyConfig *); extern PyStatus _PyGC_Init(PyInterpreterState *interp); extern PyStatus _PyAtExit_Init(PyInterpreterState *interp); -extern int _Py_Deepfreeze_Init(void); /* Various internal finalizers */ @@ -58,7 +57,6 @@ extern void _PyWarnings_Fini(PyInterpreterState *interp); extern void _PyAST_Fini(PyInterpreterState *interp); extern void _PyAtExit_Fini(PyInterpreterState *interp); extern void _PyThread_FiniType(PyInterpreterState *interp); -extern void _Py_Deepfreeze_Fini(void); extern void _PyArg_Fini(void); extern void _Py_FinalizeAllocatedBlocks(_PyRuntimeState *); diff --git a/Programs/_bootstrap_python.c b/Programs/_bootstrap_python.c index 34f79191b4e8d7..6443d814a22dab 100644 --- a/Programs/_bootstrap_python.c +++ b/Programs/_bootstrap_python.c @@ -15,17 +15,6 @@ #include "Python/frozen_modules/zipimport.h" /* End includes */ -/* Empty initializer for deepfrozen modules */ -int _Py_Deepfreeze_Init(void) -{ - return 0; -} -/* Empty finalizer for deepfrozen modules */ -void -_Py_Deepfreeze_Fini(void) -{ -} - /* Note that a negative size indicates a package. */ static const struct _frozen bootstrap_modules[] = { diff --git a/Programs/_freeze_module.c b/Programs/_freeze_module.c index 3de6c6816c1e61..2a462a42cdad7c 100644 --- a/Programs/_freeze_module.c +++ b/Programs/_freeze_module.c @@ -22,17 +22,6 @@ # include #endif -/* Empty initializer for deepfrozen modules */ -int _Py_Deepfreeze_Init(void) -{ - return 0; -} -/* Empty finalizer for deepfrozen modules */ -void -_Py_Deepfreeze_Fini(void) -{ -} - /* To avoid a circular dependency on frozen.o, we create our own structure of frozen modules instead, left deliberately blank so as to avoid unintentional import of a stale version of _frozen_importlib. */ From 85f727c5fb2afa60affa9ae3396ce4149cf5215d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 22 Apr 2024 12:50:26 -0700 Subject: [PATCH 010/217] gh-109118: Allow lambdas in annotation scopes in classes (#118019) --- Doc/whatsnew/3.13.rst | 3 ++ Lib/test/test_type_params.py | 50 ++++++++++++++++++- ...-04-17-17-52-32.gh-issue-109118.q9iPEI.rst | 2 + Python/symtable.c | 11 ---- 4 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-17-17-52-32.gh-issue-109118.q9iPEI.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index c04dc924d1efa5..67d1956a19697e 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -241,6 +241,9 @@ Other Language Changes ones if configured to do so. (Contributed by Pedro Sousa Lacerda in :gh:`66449`.) +* :ref:`annotation scope ` within class scopes can now + contain lambdas. (Contributed by Jelle Zijlstra in :gh:`109118`.) + New Modules =========== diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index 25ee188731f31f..fbb80d9aac9942 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -486,8 +486,6 @@ class C[T]: {} """ error_cases = [ - "type Alias1[T] = lambda: T", - "type Alias2 = lambda: T", "type Alias3[T] = (T for _ in (1,))", "type Alias4 = (T for _ in (1,))", "type Alias5[T] = [T for _ in (1,)]", @@ -499,6 +497,54 @@ class C[T]: r"Cannot use [a-z]+ in annotation scope within class scope"): run_code(code.format(case)) + def test_lambda_in_alias_in_class(self): + code = """ + T = "global" + class C: + T = "class" + type Alias = lambda: T + """ + C = run_code(code)["C"] + self.assertEqual(C.Alias.__value__(), "global") + + def test_lambda_in_alias_in_generic_class(self): + code = """ + class C[T]: + T = "class" + type Alias = lambda: T + """ + C = run_code(code)["C"] + self.assertIs(C.Alias.__value__(), C.__type_params__[0]) + + def test_lambda_in_generic_alias_in_class(self): + # A lambda nested in the alias cannot see the class scope, but can see + # a surrounding annotation scope. + code = """ + T = U = "global" + class C: + T = "class" + U = "class" + type Alias[T] = lambda: (T, U) + """ + C = run_code(code)["C"] + T, U = C.Alias.__value__() + self.assertIs(T, C.Alias.__type_params__[0]) + self.assertEqual(U, "global") + + def test_lambda_in_generic_alias_in_generic_class(self): + # A lambda nested in the alias cannot see the class scope, but can see + # a surrounding annotation scope. + code = """ + class C[T, U]: + T = "class" + U = "class" + type Alias[T] = lambda: (T, U) + """ + C = run_code(code)["C"] + T, U = C.Alias.__value__() + self.assertIs(T, C.Alias.__type_params__[0]) + self.assertIs(U, C.__type_params__[1]) + def make_base(arg): class Base: diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-17-17-52-32.gh-issue-109118.q9iPEI.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-17-17-52-32.gh-issue-109118.q9iPEI.rst new file mode 100644 index 00000000000000..124540045547b1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-17-17-52-32.gh-issue-109118.q9iPEI.rst @@ -0,0 +1,2 @@ +:ref:`annotation scope ` within class scopes can now +contain lambdas. diff --git a/Python/symtable.c b/Python/symtable.c index 36ccc0e73723d5..483ef1c3c46542 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -2140,17 +2140,6 @@ symtable_visit_expr(struct symtable *st, expr_ty e) VISIT(st, expr, e->v.UnaryOp.operand); break; case Lambda_kind: { - if (st->st_cur->ste_can_see_class_scope) { - // gh-109118 - PyErr_Format(PyExc_SyntaxError, - "Cannot use lambda in annotation scope within class scope"); - PyErr_RangedSyntaxLocationObject(st->st_filename, - e->lineno, - e->col_offset + 1, - e->end_lineno, - e->end_col_offset + 1); - VISIT_QUIT(st, 0); - } if (e->v.Lambda.args->defaults) VISIT_SEQ(st, expr, e->v.Lambda.args->defaults); if (e->v.Lambda.args->kw_defaults) From fc21c7f7a731d64f7e4f0e82469f78fa9c104bbd Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 23 Apr 2024 00:31:01 +0300 Subject: [PATCH 011/217] Set proper permissions for `jit.yml` workflow (#118084) --- .github/workflows/jit.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index f18fb0030bbf8b..490005b7170e95 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -12,6 +12,9 @@ on: - 'Python/optimizer*.c' workflow_dispatch: +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true From 1b85b3424c081835406592868123fe898ee029ad Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 22 Apr 2024 16:20:39 -0700 Subject: [PATCH 012/217] GH-118074: Executors in the COLD_EXITS array are not GC'able (#118117) --- .../2024-04-22-08-34-28.gh-issue-118074.5_JnIa.rst | 2 ++ Python/optimizer.c | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-22-08-34-28.gh-issue-118074.5_JnIa.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-22-08-34-28.gh-issue-118074.5_JnIa.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-22-08-34-28.gh-issue-118074.5_JnIa.rst new file mode 100644 index 00000000000000..69d29bce12ee57 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-22-08-34-28.gh-issue-118074.5_JnIa.rst @@ -0,0 +1,2 @@ +Make sure that the Executor objects in the COLD_EXITS array aren't assumed +to be GC-able (which would access bytes outside the object). diff --git a/Python/optimizer.c b/Python/optimizer.c index bb537c9111a51f..5863336c0d9ecf 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -394,6 +394,15 @@ executor_traverse(PyObject *o, visitproc visit, void *arg) return 0; } +static int +executor_is_gc(PyObject *o) +{ + if ((PyObject *)&COLD_EXITS[0] <= o && o < (PyObject *)&COLD_EXITS[COLD_EXIT_COUNT]) { + return 0; + } + return 1; +} + PyTypeObject _PyUOpExecutor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "uop_executor", @@ -405,6 +414,7 @@ PyTypeObject _PyUOpExecutor_Type = { .tp_methods = executor_methods, .tp_traverse = executor_traverse, .tp_clear = executor_clear, + .tp_is_gc = executor_is_gc, }; /* TO DO -- Generate these tables */ From c9829eec0883a8991ea4d319d965e123a3cf6c20 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Mon, 22 Apr 2024 18:15:08 -0700 Subject: [PATCH 013/217] gh-116741: Upgrade libexpat to 2.6.2 (#117296) Upgrade libexpat to 2.6.2 --- ...-03-27-13-50-02.gh-issue-116741.ZoGryG.rst | 1 + Misc/sbom.spdx.json | 20 ++++++------- Modules/expat/expat.h | 5 ++-- Modules/expat/internal.h | 17 +++++++---- Modules/expat/xmlparse.c | 30 +++++++++++++------ 5 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2024-03-27-13-50-02.gh-issue-116741.ZoGryG.rst diff --git a/Misc/NEWS.d/next/Security/2024-03-27-13-50-02.gh-issue-116741.ZoGryG.rst b/Misc/NEWS.d/next/Security/2024-03-27-13-50-02.gh-issue-116741.ZoGryG.rst new file mode 100644 index 00000000000000..12a41948066bed --- /dev/null +++ b/Misc/NEWS.d/next/Security/2024-03-27-13-50-02.gh-issue-116741.ZoGryG.rst @@ -0,0 +1 @@ +Update bundled libexpat to 2.6.2 diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json index 07db46b09ae5f5..b60adcfd362f68 100644 --- a/Misc/sbom.spdx.json +++ b/Misc/sbom.spdx.json @@ -48,11 +48,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "90c06411f131e777e2b5c3d22b7ccf50bc46f617" + "checksumValue": "4076a884f0ca96873589b5c8159e2e5bfb8b829a" }, { "algorithm": "SHA256", - "checksumValue": "3045f9176950aa13a54e53fa096385670c676c492705d636e977f888e4c72d48" + "checksumValue": "1a434bf3d2f9fb8a0b5adb79201a942788d11824c3e5b46a0b9962c0c482016c" } ], "fileName": "Modules/expat/expat.h" @@ -90,11 +90,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "9f6d9211a7b627785d5c48d10cc8eda66255113f" + "checksumValue": "e23d160cc33cc2c25a4b48f7b242f906444418e0" }, { "algorithm": "SHA256", - "checksumValue": "9f0bdd346dd94ac4359c636a4e60bc768f4ae53ce0e836eb05fb9246ee36c7f2" + "checksumValue": "f7523357d8009749e7dba94b0bd7d0fa60e011cc254e55c4ebccd6313f031122" } ], "fileName": "Modules/expat/internal.h" @@ -188,11 +188,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "3b5de0ed1de33cad85b46230707403247f2851df" + "checksumValue": "fed1311be8577491b7f63085a27014eabf2caec8" }, { "algorithm": "SHA256", - "checksumValue": "a03abd531601eef61a87e06113d218ff139b6969e15a3d4668cd85d65fc6f79b" + "checksumValue": "3dc233eca5fa1bb7387c503f8a12d840707e4374b229e05d5657db9645725040" } ], "fileName": "Modules/expat/xmlparse.c" @@ -1562,14 +1562,14 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "a13447b9aa67d7c860783fdf6820f33ebdea996900d6d8bbc50a628f55f099f7" + "checksumValue": "d4cf38d26e21a56654ffe4acd9cd5481164619626802328506a2869afab29ab3" } ], - "downloadLocation": "https://github.com/libexpat/libexpat/releases/download/R_2_6_0/expat-2.6.0.tar.gz", + "downloadLocation": "https://github.com/libexpat/libexpat/releases/download/R_2_6_2/expat-2.6.2.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:libexpat_project:libexpat:2.6.0:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:libexpat_project:libexpat:2.6.2:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], @@ -1577,7 +1577,7 @@ "name": "expat", "originator": "Organization: Expat development team", "primaryPackagePurpose": "SOURCE", - "versionInfo": "2.6.0" + "versionInfo": "2.6.2" }, { "SPDXID": "SPDXRef-PACKAGE-hacl-star", diff --git a/Modules/expat/expat.h b/Modules/expat/expat.h index 95464b0dd17735..c2770be3897e58 100644 --- a/Modules/expat/expat.h +++ b/Modules/expat/expat.h @@ -18,6 +18,7 @@ Copyright (c) 2022 Thijs Schreijer Copyright (c) 2023 Hanno Böck Copyright (c) 2023 Sony Corporation / Snild Dolkow + Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -1042,7 +1043,7 @@ typedef struct { XMLPARSEAPI(const XML_Feature *) XML_GetFeatureList(void); -#if XML_GE == 1 +#if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1) /* Added in Expat 2.4.0 for XML_DTD defined and * added in Expat 2.6.0 for XML_GE == 1. */ XMLPARSEAPI(XML_Bool) @@ -1065,7 +1066,7 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled); */ #define XML_MAJOR_VERSION 2 #define XML_MINOR_VERSION 6 -#define XML_MICRO_VERSION 0 +#define XML_MICRO_VERSION 2 #ifdef __cplusplus } diff --git a/Modules/expat/internal.h b/Modules/expat/internal.h index cce71e4c5164b5..167ec36804a43b 100644 --- a/Modules/expat/internal.h +++ b/Modules/expat/internal.h @@ -28,10 +28,11 @@ Copyright (c) 2002-2003 Fred L. Drake, Jr. Copyright (c) 2002-2006 Karl Waclawek Copyright (c) 2003 Greg Stein - Copyright (c) 2016-2023 Sebastian Pipping + Copyright (c) 2016-2024 Sebastian Pipping Copyright (c) 2018 Yury Gribov Copyright (c) 2019 David Loffredo - Copyright (c) 2023 Sony Corporation / Snild Dolkow + Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow + Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -155,14 +156,20 @@ extern "C" { void _INTERNAL_trim_to_complete_utf8_characters(const char *from, const char **fromLimRef); -#if XML_GE == 1 +#if defined(XML_GE) && XML_GE == 1 unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser); unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); const char *unsignedCharToPrintable(unsigned char c); #endif -extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c -extern unsigned int g_parseAttempts; // used for testing only +extern +#if ! defined(XML_TESTING) + const +#endif + XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c +#if defined(XML_TESTING) +extern unsigned int g_bytesScanned; // used for testing only +#endif #ifdef __cplusplus } diff --git a/Modules/expat/xmlparse.c b/Modules/expat/xmlparse.c index aaf0fa9c8f96d1..2951fec70c56cb 100644 --- a/Modules/expat/xmlparse.c +++ b/Modules/expat/xmlparse.c @@ -1,4 +1,4 @@ -/* 628e24d4966bedbd4800f6ed128d06d29703765b4bce12d3b7f099f90f842fc9 (2.6.0+) +/* 2a14271ad4d35e82bde8ba210b4edb7998794bcbae54deab114046a300f9639a (2.6.2+) __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| @@ -38,7 +38,7 @@ Copyright (c) 2022 Jann Horn Copyright (c) 2022 Sean McBride Copyright (c) 2023 Owain Davies - Copyright (c) 2023 Sony Corporation / Snild Dolkow + Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -210,7 +210,7 @@ typedef char ICHAR; #endif /* Round up n to be a multiple of sz, where sz is a power of 2. */ -#define ROUND_UP(n, sz) (((n) + ((sz)-1)) & ~((sz)-1)) +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) /* Do safe (NULL-aware) pointer arithmetic */ #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0) @@ -248,7 +248,7 @@ static void copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key); it odd, since odd numbers are always relative prime to a power of 2. */ #define SECOND_HASH(hash, mask, power) \ - ((((hash) & ~(mask)) >> ((power)-1)) & ((mask) >> 2)) + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) #define PROBE_STEP(hash, mask, power) \ ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) @@ -629,8 +629,14 @@ static unsigned long getDebugLevel(const char *variableName, ? 0 \ : ((*((pool)->ptr)++ = c), 1)) -XML_Bool g_reparseDeferralEnabledDefault = XML_TRUE; // write ONLY in runtests.c -unsigned int g_parseAttempts = 0; // used for testing only +#if ! defined(XML_TESTING) +const +#endif + XML_Bool g_reparseDeferralEnabledDefault + = XML_TRUE; // write ONLY in runtests.c +#if defined(XML_TESTING) +unsigned int g_bytesScanned = 0; // used for testing only +#endif struct XML_ParserStruct { /* The first member must be m_userData so that the XML_GetUserData @@ -1017,7 +1023,9 @@ callProcessor(XML_Parser parser, const char *start, const char *end, return XML_ERROR_NONE; } } - g_parseAttempts += 1; +#if defined(XML_TESTING) + g_bytesScanned += (unsigned)have_now; +#endif const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr); if (ret == XML_ERROR_NONE) { // if we consumed nothing, remember what we had on this parse attempt. @@ -6232,7 +6240,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, dtd->keepProcessing = dtd->standalone; goto endEntityValue; } - if (entity->open) { + if (entity->open || (entity == parser->m_declEntity)) { if (enc == parser->m_encoding) parser->m_eventPtr = entityTextPtr; result = XML_ERROR_RECURSIVE_ENTITY_REF; @@ -7779,6 +7787,8 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { static float accountingGetCurrentAmplification(XML_Parser rootParser) { + // 1.........1.........12 => 22 + const size_t lenOfShortestInclude = sizeof("") - 1; const XmlBigCount countBytesOutput = rootParser->m_accounting.countBytesDirect + rootParser->m_accounting.countBytesIndirect; @@ -7786,7 +7796,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) { = rootParser->m_accounting.countBytesDirect ? (countBytesOutput / (float)(rootParser->m_accounting.countBytesDirect)) - : 1.0f; + : ((lenOfShortestInclude + + rootParser->m_accounting.countBytesIndirect) + / (float)lenOfShortestInclude); assert(! rootParser->m_parentParser); return amplificationFactor; } From 8e86579caef59fad0c54ac698d589f23a7951c55 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 22 Apr 2024 18:24:21 -0700 Subject: [PATCH 014/217] gh-95754: Better error when script shadows a standard library or third party module (#113769) --- Doc/whatsnew/3.13.rst | 34 +++ .../pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + .../internal/pycore_runtime_init_generated.h | 1 + .../internal/pycore_unicodeobject_generated.h | 3 + Lib/test/test_import/__init__.py | 221 ++++++++++++++++ ...4-01-07-03-38-34.gh-issue-95754.aPjEBG.rst | 4 + Objects/moduleobject.c | 244 ++++++++++++++---- 8 files changed, 456 insertions(+), 53 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-01-07-03-38-34.gh-issue-95754.aPjEBG.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 67d1956a19697e..89694afdfa3fec 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -104,6 +104,40 @@ Improved Error Messages variables. See also :ref:`using-on-controlling-color`. (Contributed by Pablo Galindo Salgado in :gh:`112730`.) +* A common mistake is to write a script with the same name as a + standard library module. When this results in errors, we now + display a more helpful error message: + + .. code-block:: shell-session + + $ python random.py + Traceback (most recent call last): + File "/home/random.py", line 1, in + import random; print(random.randint(5)) + ^^^^^^^^^^^^^ + File "/home/random.py", line 1, in + import random; print(random.randint(5)) + ^^^^^^^^^^^^^^ + AttributeError: module 'random' has no attribute 'randint' (consider renaming '/home/random.py' since it has the same name as the standard library module named 'random' and the import system gives it precedence) + + Similarly, if a script has the same name as a third-party + module it attempts to import, and this results in errors, + we also display a more helpful error message: + + .. code-block:: shell-session + + $ python numpy.py + Traceback (most recent call last): + File "/home/numpy.py", line 1, in + import numpy as np; np.array([1,2,3]) + ^^^^^^^^^^^^^^^^^^ + File "/home/numpy.py", line 1, in + import numpy as np; np.array([1,2,3]) + ^^^^^^^^ + AttributeError: module 'numpy' has no attribute 'array' (consider renaming '/home/numpy.py' if it has the same name as a third-party module you intended to import) + + (Contributed by Shantanu Jain in :gh:`95754`.) + * When an incorrect keyword argument is passed to a function, the error message now potentially suggests the correct keyword argument. (Contributed by Pablo Galindo Salgado and Shantanu Jain in :gh:`107944`.) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 90a338ade17c61..4a6f40c84088e8 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -981,6 +981,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(h)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(handle)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(handle_seq)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(has_location)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hash_name)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(header)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(headers)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 0899e7ee776617..8332cdf874c0c9 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -470,6 +470,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(h) STRUCT_FOR_ID(handle) STRUCT_FOR_ID(handle_seq) + STRUCT_FOR_ID(has_location) STRUCT_FOR_ID(hash_name) STRUCT_FOR_ID(header) STRUCT_FOR_ID(headers) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index d4323e5bd12a67..103279a4cf228b 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -979,6 +979,7 @@ extern "C" { INIT_ID(h), \ INIT_ID(handle), \ INIT_ID(handle_seq), \ + INIT_ID(has_location), \ INIT_ID(hash_name), \ INIT_ID(header), \ INIT_ID(headers), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 9daef267069d0d..a180054d407b39 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1251,6 +1251,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(handle_seq); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(has_location); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(hash_name); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 469d1fbe59aaa2..947a7b19056bdb 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -804,6 +804,227 @@ def test_issue105979(self): self.assertIn("Frozen object named 'x' is invalid", str(cm.exception)) + def test_script_shadowing_stdlib(self): + with os_helper.temp_dir() as tmp: + with open(os.path.join(tmp, "fractions.py"), "w", encoding='utf-8') as f: + f.write("import fractions\nfractions.Fraction") + + expected_error = ( + rb"AttributeError: module 'fractions' has no attribute 'Fraction' " + rb"\(consider renaming '.*fractions.py' since it has the " + rb"same name as the standard library module named 'fractions' " + rb"and the import system gives it precedence\)" + ) + + popen = script_helper.spawn_python(os.path.join(tmp, "fractions.py"), cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + popen = script_helper.spawn_python('-m', 'fractions', cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + popen = script_helper.spawn_python('-c', 'import fractions', cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + # and there's no error at all when using -P + popen = script_helper.spawn_python('-P', 'fractions.py', cwd=tmp) + stdout, stderr = popen.communicate() + self.assertEqual(stdout, b'') + + tmp_child = os.path.join(tmp, "child") + os.mkdir(tmp_child) + + # test the logic with different cwd + popen = script_helper.spawn_python(os.path.join(tmp, "fractions.py"), cwd=tmp_child) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + popen = script_helper.spawn_python('-m', 'fractions', cwd=tmp_child) + stdout, stderr = popen.communicate() + self.assertEqual(stdout, b'') # no error + + popen = script_helper.spawn_python('-c', 'import fractions', cwd=tmp_child) + stdout, stderr = popen.communicate() + self.assertEqual(stdout, b'') # no error + + def test_package_shadowing_stdlib_module(self): + with os_helper.temp_dir() as tmp: + os.mkdir(os.path.join(tmp, "fractions")) + with open(os.path.join(tmp, "fractions", "__init__.py"), "w", encoding='utf-8') as f: + f.write("shadowing_module = True") + with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: + f.write(""" +import fractions +fractions.shadowing_module +fractions.Fraction +""") + + expected_error = ( + rb"AttributeError: module 'fractions' has no attribute 'Fraction' " + rb"\(consider renaming '.*fractions.__init__.py' since it has the " + rb"same name as the standard library module named 'fractions' " + rb"and the import system gives it precedence\)" + ) + + popen = script_helper.spawn_python(os.path.join(tmp, "main.py"), cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + popen = script_helper.spawn_python('-m', 'main', cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + # and there's no shadowing at all when using -P + popen = script_helper.spawn_python('-P', 'main.py', cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, b"module 'fractions' has no attribute 'shadowing_module'") + + def test_script_shadowing_third_party(self): + with os_helper.temp_dir() as tmp: + with open(os.path.join(tmp, "numpy.py"), "w", encoding='utf-8') as f: + f.write("import numpy\nnumpy.array") + + expected_error = ( + rb"AttributeError: module 'numpy' has no attribute 'array' " + rb"\(consider renaming '.*numpy.py' if it has the " + rb"same name as a third-party module you intended to import\)\s+\Z" + ) + + popen = script_helper.spawn_python(os.path.join(tmp, "numpy.py")) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + popen = script_helper.spawn_python('-m', 'numpy', cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + popen = script_helper.spawn_python('-c', 'import numpy', cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + def test_script_maybe_not_shadowing_third_party(self): + with os_helper.temp_dir() as tmp: + with open(os.path.join(tmp, "numpy.py"), "w", encoding='utf-8') as f: + f.write("this_script_does_not_attempt_to_import_numpy = True") + + expected_error = ( + rb"AttributeError: module 'numpy' has no attribute 'attr'\s+\Z" + ) + + popen = script_helper.spawn_python('-c', 'import numpy; numpy.attr', cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + + def test_script_shadowing_stdlib_edge_cases(self): + with os_helper.temp_dir() as tmp: + with open(os.path.join(tmp, "fractions.py"), "w", encoding='utf-8') as f: + f.write("shadowing_module = True") + with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: + f.write(""" +import fractions +fractions.shadowing_module +class substr(str): + __hash__ = None +fractions.__name__ = substr('fractions') +try: + fractions.Fraction +except TypeError as e: + print(str(e)) +""") + + popen = script_helper.spawn_python("main.py", cwd=tmp) + stdout, stderr = popen.communicate() + self.assertEqual(stdout.rstrip(), b"unhashable type: 'substr'") + + with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: + f.write(""" +import fractions +fractions.shadowing_module + +import sys +sys.stdlib_module_names = None +try: + fractions.Fraction +except AttributeError as e: + print(str(e)) + +del sys.stdlib_module_names +try: + fractions.Fraction +except AttributeError as e: + print(str(e)) + +sys.path = [0] +try: + fractions.Fraction +except AttributeError as e: + print(str(e)) +""") + + popen = script_helper.spawn_python("main.py", cwd=tmp) + stdout, stderr = popen.communicate() + self.assertEqual( + stdout.splitlines(), + [ + b"module 'fractions' has no attribute 'Fraction'", + b"module 'fractions' has no attribute 'Fraction'", + b"module 'fractions' has no attribute 'Fraction'", + ], + ) + + with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: + f.write(""" +import fractions +fractions.shadowing_module +del fractions.__spec__.origin +try: + fractions.Fraction +except AttributeError as e: + print(str(e)) + +fractions.__spec__.origin = 0 +try: + fractions.Fraction +except AttributeError as e: + print(str(e)) +""") + + popen = script_helper.spawn_python("main.py", cwd=tmp) + stdout, stderr = popen.communicate() + self.assertEqual( + stdout.splitlines(), + [ + b"module 'fractions' has no attribute 'Fraction'", + b"module 'fractions' has no attribute 'Fraction'" + ], + ) + + def test_script_shadowing_stdlib_sys_path_modification(self): + with os_helper.temp_dir() as tmp: + with open(os.path.join(tmp, "fractions.py"), "w", encoding='utf-8') as f: + f.write("shadowing_module = True") + + expected_error = ( + rb"AttributeError: module 'fractions' has no attribute 'Fraction' " + rb"\(consider renaming '.*fractions.py' since it has the " + rb"same name as the standard library module named 'fractions' " + rb"and the import system gives it precedence\)" + ) + + with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: + f.write(""" +import sys +sys.path.insert(0, "this_folder_does_not_exist") +import fractions +fractions.Fraction +""") + + popen = script_helper.spawn_python("main.py", cwd=tmp) + stdout, stderr = popen.communicate() + self.assertRegex(stdout, expected_error) + @skip_if_dont_write_bytecode class FilePermissionTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-01-07-03-38-34.gh-issue-95754.aPjEBG.rst b/Misc/NEWS.d/next/Core and Builtins/2024-01-07-03-38-34.gh-issue-95754.aPjEBG.rst new file mode 100644 index 00000000000000..588be2d28cd76e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-01-07-03-38-34.gh-issue-95754.aPjEBG.rst @@ -0,0 +1,4 @@ +Improve the error message when a script shadowing a module from the standard +library causes :exc:`AttributeError` to be raised. Similarly, improve the error +message when a script shadowing a third party module attempts to access an +attribute from that third party module while still initialising. diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index da6a276c41be1f..2f6adb9a2e12be 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_fileutils.h" // _Py_wgetcwd #include "pycore_interp.h" // PyInterpreterState.importlib #include "pycore_modsupport.h" // _PyModule_CreateInitialized() #include "pycore_moduleobject.h" // _PyModule_GetDef() @@ -10,6 +11,7 @@ #include "pycore_pyerrors.h" // _PyErr_FormatFromCause() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "osdefs.h" // MAXPATHLEN static PyMemberDef module_members[] = { @@ -785,11 +787,104 @@ _PyModuleSpec_IsUninitializedSubmodule(PyObject *spec, PyObject *name) return rc; } +static int +_get_file_origin_from_spec(PyObject *spec, PyObject **p_origin) +{ + PyObject *has_location = NULL; + int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(has_location), &has_location); + if (rc <= 0) { + return rc; + } + // If origin is not a location, or doesn't exist, or is not a str), we could consider falling + // back to module.__file__. But the cases in which module.__file__ is not __spec__.origin + // are cases in which we probably shouldn't be guessing. + rc = PyObject_IsTrue(has_location); + Py_DECREF(has_location); + if (rc <= 0) { + return rc; + } + // has_location is true, so origin is a location + PyObject *origin = NULL; + rc = PyObject_GetOptionalAttr(spec, &_Py_ID(origin), &origin); + if (rc <= 0) { + return rc; + } + assert(origin != NULL); + if (!PyUnicode_Check(origin)) { + Py_DECREF(origin); + return 0; + } + *p_origin = origin; + return 1; +} + +static int +_is_module_possibly_shadowing(PyObject *origin) +{ + // origin must be a unicode subtype + // Returns 1 if the module at origin could be shadowing a module of the + // same name later in the module search path. The condition we check is basically: + // root = os.path.dirname(origin.removesuffix(os.sep + "__init__.py")) + // return not sys.flags.safe_path and root == (sys.path[0] or os.getcwd()) + // Returns 0 otherwise (or if we aren't sure) + // Returns -1 if an error occurred that should be propagated + if (origin == NULL) { + return 0; + } + + // not sys.flags.safe_path + const PyConfig *config = _Py_GetConfig(); + if (config->safe_path) { + return 0; + } + + // root = os.path.dirname(origin.removesuffix(os.sep + "__init__.py")) + wchar_t root[MAXPATHLEN + 1]; + Py_ssize_t size = PyUnicode_AsWideChar(origin, root, MAXPATHLEN); + if (size < 0) { + return -1; + } + assert(size <= MAXPATHLEN); + root[size] = L'\0'; + + wchar_t *sep = wcsrchr(root, SEP); + if (sep == NULL) { + return 0; + } + // If it's a package then we need to look one directory further up + if (wcscmp(sep + 1, L"__init__.py") == 0) { + *sep = L'\0'; + sep = wcsrchr(root, SEP); + if (sep == NULL) { + return 0; + } + } + *sep = L'\0'; + + // sys.path[0] or os.getcwd() + wchar_t *sys_path_0 = config->sys_path_0; + if (!sys_path_0) { + return 0; + } + + wchar_t sys_path_0_buf[MAXPATHLEN]; + if (sys_path_0[0] == L'\0') { + // if sys.path[0] == "", treat it as if it were the current directory + if (!_Py_wgetcwd(sys_path_0_buf, MAXPATHLEN)) { + return -1; + } + sys_path_0 = sys_path_0_buf; + } + + int result = wcscmp(sys_path_0, root) == 0; + return result; +} + PyObject* _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) { // When suppress=1, this function suppresses AttributeError. - PyObject *attr, *mod_name, *getattr, *origin; + PyObject *attr, *mod_name, *getattr; attr = _PyObject_GenericGetAttrWithDict((PyObject *)m, name, NULL, suppress); if (attr) { return attr; @@ -820,68 +915,111 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) Py_DECREF(getattr); return result; } + + // The attribute was not found. We make a best effort attempt at a useful error message, + // but only if we're not suppressing AttributeError. + if (suppress == 1) { + return NULL; + } if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__name__), &mod_name) < 0) { return NULL; } - if (mod_name && PyUnicode_Check(mod_name)) { - PyObject *spec; - if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__spec__), &spec) < 0) { - Py_DECREF(mod_name); - return NULL; + if (!mod_name || !PyUnicode_Check(mod_name)) { + Py_XDECREF(mod_name); + PyErr_Format(PyExc_AttributeError, + "module has no attribute '%U'", name); + return NULL; + } + PyObject *spec; + if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__spec__), &spec) < 0) { + Py_DECREF(mod_name); + return NULL; + } + if (spec == NULL) { + PyErr_Format(PyExc_AttributeError, + "module '%U' has no attribute '%U'", + mod_name, name); + Py_DECREF(mod_name); + return NULL; + } + + PyObject *origin = NULL; + if (_get_file_origin_from_spec(spec, &origin) < 0) { + goto done; + } + + int is_possibly_shadowing = _is_module_possibly_shadowing(origin); + if (is_possibly_shadowing < 0) { + goto done; + } + int is_possibly_shadowing_stdlib = 0; + if (is_possibly_shadowing) { + PyObject *stdlib_modules = PySys_GetObject("stdlib_module_names"); + if (stdlib_modules && PyAnySet_Check(stdlib_modules)) { + is_possibly_shadowing_stdlib = PySet_Contains(stdlib_modules, mod_name); + if (is_possibly_shadowing_stdlib < 0) { + goto done; + } + } + } + + if (is_possibly_shadowing_stdlib) { + assert(origin); + PyErr_Format(PyExc_AttributeError, + "module '%U' has no attribute '%U' " + "(consider renaming '%U' since it has the same " + "name as the standard library module named '%U' " + "and the import system gives it precedence)", + mod_name, name, origin, mod_name); + } + else { + int rc = _PyModuleSpec_IsInitializing(spec); + if (rc > 0) { + if (is_possibly_shadowing) { + assert(origin); + // For third-party modules, only mention the possibility of + // shadowing if the module is being initialized. + PyErr_Format(PyExc_AttributeError, + "module '%U' has no attribute '%U' " + "(consider renaming '%U' if it has the same name " + "as a third-party module you intended to import)", + mod_name, name, origin); + } + else if (origin) { + PyErr_Format(PyExc_AttributeError, + "partially initialized " + "module '%U' from '%U' has no attribute '%U' " + "(most likely due to a circular import)", + mod_name, origin, name); + } + else { + PyErr_Format(PyExc_AttributeError, + "partially initialized " + "module '%U' has no attribute '%U' " + "(most likely due to a circular import)", + mod_name, name); + } } - if (suppress != 1) { - int rc = _PyModuleSpec_IsInitializing(spec); + else if (rc == 0) { + rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name); if (rc > 0) { - int valid_spec = PyObject_GetOptionalAttr(spec, &_Py_ID(origin), &origin); - if (valid_spec == -1) { - Py_XDECREF(spec); - Py_DECREF(mod_name); - return NULL; - } - if (valid_spec == 1 && !PyUnicode_Check(origin)) { - valid_spec = 0; - Py_DECREF(origin); - } - if (valid_spec == 1) { - PyErr_Format(PyExc_AttributeError, - "partially initialized " - "module '%U' from '%U' has no attribute '%U' " - "(most likely due to a circular import)", - mod_name, origin, name); - Py_DECREF(origin); - } - else { - PyErr_Format(PyExc_AttributeError, - "partially initialized " - "module '%U' has no attribute '%U' " - "(most likely due to a circular import)", - mod_name, name); - } + PyErr_Format(PyExc_AttributeError, + "cannot access submodule '%U' of module '%U' " + "(most likely due to a circular import)", + name, mod_name); } else if (rc == 0) { - rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name); - if (rc > 0) { - PyErr_Format(PyExc_AttributeError, - "cannot access submodule '%U' of module '%U' " - "(most likely due to a circular import)", - name, mod_name); - } - else if (rc == 0) { - PyErr_Format(PyExc_AttributeError, - "module '%U' has no attribute '%U'", - mod_name, name); - } + PyErr_Format(PyExc_AttributeError, + "module '%U' has no attribute '%U'", + mod_name, name); } } - Py_XDECREF(spec); - Py_DECREF(mod_name); - return NULL; - } - Py_XDECREF(mod_name); - if (suppress != 1) { - PyErr_Format(PyExc_AttributeError, - "module has no attribute '%U'", name); } + +done: + Py_XDECREF(origin); + Py_DECREF(spec); + Py_DECREF(mod_name); return NULL; } From 456c29cf85847c67dfc0fa36d6fe6168569b46fe Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Tue, 23 Apr 2024 09:46:28 +0200 Subject: [PATCH 015/217] gh-118039: Fix config.cache key on WASI (#118137) --- .github/workflows/build.yml | 1 + .github/workflows/reusable-wasi.yml | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e1a2a62c60c6de..299c02e0944d42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -137,6 +137,7 @@ jobs: uses: actions/cache@v4 with: path: config.cache + # Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }}-${{ env.pythonLocation }} - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh diff --git a/.github/workflows/reusable-wasi.yml b/.github/workflows/reusable-wasi.yml index 60eef7bc478bbf..4a509a8acfee96 100644 --- a/.github/workflows/reusable-wasi.yml +++ b/.github/workflows/reusable-wasi.yml @@ -50,7 +50,8 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.CROSS_BUILD_PYTHON }}/config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }} + # Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python + key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}-${{ env.pythonLocation }} - name: "Configure build Python" run: python3 Tools/wasm/wasi.py configure-build-python -- --config-cache --with-pydebug - name: "Make build Python" @@ -59,7 +60,8 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.CROSS_BUILD_WASI }}/config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-wasi-sdk-${{ env.WASI_SDK_VERSION }}-${{ inputs.config_hash }} + # Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python + key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-wasi-sdk-${{ env.WASI_SDK_VERSION }}-${{ inputs.config_hash }}-${{ env.pythonLocation }} - name: "Configure host" # `--with-pydebug` inferred from configure-build-python run: python3 Tools/wasm/wasi.py configure-host -- --config-cache From e17cd1fbfd4f20824c686c7242423e84ba6a6cc5 Mon Sep 17 00:00:00 2001 From: Yichen Yan Date: Tue, 23 Apr 2024 15:56:00 +0800 Subject: [PATCH 016/217] gh-116984: Install mimalloc headers (#116985) - Install mimalloc header only when enabled - Rename WITH_MIMALLOC to INSTALL_MIMALLOC --- Makefile.pre.in | 19 +++++++++++++++++++ configure | 3 ++- configure.ac | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index f7c21a380caa99..c7cf44de2dbe79 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -233,6 +233,9 @@ LIBHACL_SHA2_A= Modules/_hacl/libHacl_Hash_SHA2.a # Default zoneinfo.TZPATH. Added here to expose it in sysconfig.get_config_var TZPATH=@TZPATH@ +# If to install mimalloc headers +INSTALL_MIMALLOC=@INSTALL_MIMALLOC@ + # Modes for directories, executables and data files created by the # install process. Default to user-only-writable for all file types. DIRMODE= 755 @@ -2616,6 +2619,12 @@ inclinstall: $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(INCLUDEPY)/internal; \ else true; \ fi + @if test "$(INSTALL_MIMALLOC)" == "yes"; then \ + if test ! -d $(DESTDIR)$(INCLUDEPY)/internal/mimalloc/mimalloc; then \ + echo "Creating directory $(DESTDIR)$(INCLUDEPY)/internal/mimalloc/mimalloc"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(INCLUDEPY)/internal/mimalloc/mimalloc; \ + fi; \ + fi @for i in $(srcdir)/Include/*.h; \ do \ echo $(INSTALL_DATA) $$i $(INCLUDEPY); \ @@ -2631,6 +2640,16 @@ inclinstall: echo $(INSTALL_DATA) $$i $(INCLUDEPY)/internal; \ $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEPY)/internal; \ done + @if test "$(INSTALL_MIMALLOC)" == "yes"; then \ + echo $(INSTALL_DATA) $(srcdir)/Include/internal/mimalloc/mimalloc.h $(DESTDIR)$(INCLUDEPY)/internal/mimalloc/mimalloc.h; \ + $(INSTALL_DATA) $(srcdir)/Include/internal/mimalloc/mimalloc.h $(DESTDIR)$(INCLUDEPY)/internal/mimalloc/mimalloc.h; \ + for i in $(srcdir)/Include/internal/mimalloc/mimalloc/*.h; \ + do \ + echo $(INSTALL_DATA) $$i $(INCLUDEPY)/internal/mimalloc/mimalloc; \ + $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEPY)/internal/mimalloc/mimalloc; \ + done; \ + fi + echo $(INSTALL_DATA) pyconfig.h $(DESTDIR)$(CONFINCLUDEPY)/pyconfig.h $(INSTALL_DATA) pyconfig.h $(DESTDIR)$(CONFINCLUDEPY)/pyconfig.h # Install the library and miscellaneous stuff needed for extending/embedding diff --git a/configure b/configure index 29a7d5b2b86c29..94ee1ca9cd0b8d 100755 --- a/configure +++ b/configure @@ -869,7 +869,7 @@ DTRACE_OBJS DTRACE_HEADERS DFLAGS DTRACE -WITH_MIMALLOC +INSTALL_MIMALLOC MIMALLOC_HEADERS GDBM_LIBS GDBM_CFLAGS @@ -17335,6 +17335,7 @@ fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_mimalloc" >&5 printf "%s\n" "$with_mimalloc" >&6; } +INSTALL_MIMALLOC=$with_mimalloc diff --git a/configure.ac b/configure.ac index 7723c805b93dae..7877ef45c2e500 100644 --- a/configure.ac +++ b/configure.ac @@ -4779,7 +4779,7 @@ elif test "$disable_gil" = "yes"; then fi AC_MSG_RESULT([$with_mimalloc]) -AC_SUBST([WITH_MIMALLOC]) +AC_SUBST([INSTALL_MIMALLOC], [$with_mimalloc]) AC_SUBST([MIMALLOC_HEADERS]) # Check for Python-specific malloc support From eb927e9fc823de9539fcb82c9ea9d055462eb04a Mon Sep 17 00:00:00 2001 From: Joe Jevnik Date: Tue, 23 Apr 2024 07:15:15 -0400 Subject: [PATCH 017/217] gh-68114: Fix handling for removed PyArg_ParseTuple 'w' formatters (GH-8204) Co-authored-by: Joe Jevnik Co-authored-by: Petr Viktorin --- Lib/test/test_capi/test_getargs.py | 38 +++--- ...4-03-18-17-29-52.gh-issue-68114.W7R_lI.rst | 2 + Modules/_testcapi/getargs.c | 118 ++++++++++++++++++ Python/getargs.c | 5 + 4 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-03-18-17-29-52.gh-issue-68114.W7R_lI.rst diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index 12039803ba543e..e710400f75c235 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -856,20 +856,24 @@ def test_y_hash(self): def test_w_star(self): # getargs_w_star() modifies first and last byte - from _testcapi import getargs_w_star - self.assertRaises(TypeError, getargs_w_star, 'abc\xe9') - self.assertRaises(TypeError, getargs_w_star, b'bytes') - self.assertRaises(TypeError, getargs_w_star, b'nul:\0') - self.assertRaises(TypeError, getargs_w_star, memoryview(b'bytes')) - buf = bytearray(b'bytearray') - self.assertEqual(getargs_w_star(buf), b'[ytearra]') - self.assertEqual(buf, bytearray(b'[ytearra]')) - buf = bytearray(b'memoryview') - self.assertEqual(getargs_w_star(memoryview(buf)), b'[emoryvie]') - self.assertEqual(buf, bytearray(b'[emoryvie]')) - self.assertRaises(TypeError, getargs_w_star, None) - self.assertRaises(TypeError, getargs_w_star, NONCONTIG_WRITABLE) - self.assertRaises(TypeError, getargs_w_star, NONCONTIG_READONLY) + # getargs_w_star_opt() takes additional optional args: with one + # argument it should behave the same as getargs_w_star + from _testcapi import getargs_w_star, getargs_w_star_opt + for func in (getargs_w_star, getargs_w_star_opt): + with self.subTest(func=func): + self.assertRaises(TypeError, func, 'abc\xe9') + self.assertRaises(TypeError, func, b'bytes') + self.assertRaises(TypeError, func, b'nul:\0') + self.assertRaises(TypeError, func, memoryview(b'bytes')) + buf = bytearray(b'bytearray') + self.assertEqual(func(buf), b'[ytearra]') + self.assertEqual(buf, bytearray(b'[ytearra]')) + buf = bytearray(b'memoryview') + self.assertEqual(func(memoryview(buf)), b'[emoryvie]') + self.assertEqual(buf, bytearray(b'[emoryvie]')) + self.assertRaises(TypeError, func, None) + self.assertRaises(TypeError, func, NONCONTIG_WRITABLE) + self.assertRaises(TypeError, func, NONCONTIG_READONLY) def test_getargs_empty(self): from _testcapi import getargs_empty @@ -1112,9 +1116,9 @@ def test_skipitem(self): c = chr(i) # skip parentheses, the error reporting is inconsistent about them - # skip 'e', it's always a two-character code + # skip 'e' and 'w', they're always two-character codes # skip '|' and '$', they don't represent arguments anyway - if c in '()e|$': + if c in '()ew|$': continue # test the format unit when not skipped @@ -1152,7 +1156,7 @@ def test_skipitem_with_suffix(self): dict_b = {'b':1} keywords = ["a", "b"] - supported = ('s#', 's*', 'z#', 'z*', 'y#', 'y*', 'w#', 'w*') + supported = ('s#', 's*', 'z#', 'z*', 'y#', 'y*', 'w*') for c in string.ascii_letters: for c2 in '#*': f = c + c2 diff --git a/Misc/NEWS.d/next/C API/2024-03-18-17-29-52.gh-issue-68114.W7R_lI.rst b/Misc/NEWS.d/next/C API/2024-03-18-17-29-52.gh-issue-68114.W7R_lI.rst new file mode 100644 index 00000000000000..fa09d2a0a72df7 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-03-18-17-29-52.gh-issue-68114.W7R_lI.rst @@ -0,0 +1,2 @@ +Fixed skipitem()'s handling of the old 'w' and 'w#' formatters. These are +no longer supported and now raise an exception if used. diff --git a/Modules/_testcapi/getargs.c b/Modules/_testcapi/getargs.c index 0d61d8c8969f82..ee04c760d27213 100644 --- a/Modules/_testcapi/getargs.c +++ b/Modules/_testcapi/getargs.c @@ -141,6 +141,122 @@ getargs_w_star(PyObject *self, PyObject *args) return result; } +static PyObject * +getargs_w_star_opt(PyObject *self, PyObject *args) +{ + Py_buffer buffer; + Py_buffer buf2; + int number = 1; + + if (!PyArg_ParseTuple(args, "w*|w*i:getargs_w_star", + &buffer, &buf2, &number)) { + return NULL; + } + + if (2 <= buffer.len) { + char *str = buffer.buf; + str[0] = '['; + str[buffer.len-1] = ']'; + } + + PyObject *result = PyBytes_FromStringAndSize(buffer.buf, buffer.len); + PyBuffer_Release(&buffer); + return result; +} + +/* Test the old w and w# codes that no longer work */ +static PyObject * +test_w_code_invalid(PyObject *self, PyObject *arg) +{ + static const char * const keywords[] = {"a", "b", "c", "d", NULL}; + char *formats_3[] = {"O|w#$O", + "O|w$O", + "O|w#O", + "O|wO", + NULL}; + char *formats_4[] = {"O|w#O$O", + "O|wO$O", + "O|Ow#O", + "O|OwO", + "O|Ow#$O", + "O|Ow$O", + NULL}; + size_t n; + PyObject *args; + PyObject *kwargs; + PyObject *tmp; + + if (!(args = PyTuple_Pack(1, Py_None))) { + return NULL; + } + + kwargs = PyDict_New(); + if (!kwargs) { + Py_DECREF(args); + return NULL; + } + + if (PyDict_SetItemString(kwargs, "c", Py_None)) { + Py_DECREF(args); + Py_XDECREF(kwargs); + return NULL; + } + + for (n = 0; formats_3[n]; ++n) { + if (PyArg_ParseTupleAndKeywords(args, kwargs, formats_3[n], + (char**) keywords, + &tmp, &tmp, &tmp)) { + Py_DECREF(args); + Py_DECREF(kwargs); + PyErr_Format(PyExc_AssertionError, + "test_w_code_invalid_suffix: %s", + formats_3[n]); + return NULL; + } + else { + if (!PyErr_ExceptionMatches(PyExc_SystemError)) { + Py_DECREF(args); + Py_DECREF(kwargs); + return NULL; + } + PyErr_Clear(); + } + } + + if (PyDict_DelItemString(kwargs, "c") || + PyDict_SetItemString(kwargs, "d", Py_None)) { + + Py_DECREF(kwargs); + Py_DECREF(args); + return NULL; + } + + for (n = 0; formats_4[n]; ++n) { + if (PyArg_ParseTupleAndKeywords(args, kwargs, formats_4[n], + (char**) keywords, + &tmp, &tmp, &tmp, &tmp)) { + Py_DECREF(args); + Py_DECREF(kwargs); + PyErr_Format(PyExc_AssertionError, + "test_w_code_invalid_suffix: %s", + formats_4[n]); + return NULL; + } + else { + if (!PyErr_ExceptionMatches(PyExc_SystemError)) { + Py_DECREF(args); + Py_DECREF(kwargs); + return NULL; + } + PyErr_Clear(); + } + } + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_RETURN_NONE; +} + static PyObject * getargs_empty(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -684,6 +800,7 @@ static PyMethodDef test_methods[] = { {"getargs_s_star", getargs_s_star, METH_VARARGS}, {"getargs_tuple", getargs_tuple, METH_VARARGS}, {"getargs_w_star", getargs_w_star, METH_VARARGS}, + {"getargs_w_star_opt", getargs_w_star_opt, METH_VARARGS}, {"getargs_empty", _PyCFunction_CAST(getargs_empty), METH_VARARGS|METH_KEYWORDS}, {"getargs_y", getargs_y, METH_VARARGS}, {"getargs_y_hash", getargs_y_hash, METH_VARARGS}, @@ -693,6 +810,7 @@ static PyMethodDef test_methods[] = { {"getargs_z_star", getargs_z_star, METH_VARARGS}, {"parse_tuple_and_keywords", parse_tuple_and_keywords, METH_VARARGS}, {"gh_99240_clear_args", gh_99240_clear_args, METH_VARARGS}, + {"test_w_code_invalid", test_w_code_invalid, METH_NOARGS}, {NULL}, }; diff --git a/Python/getargs.c b/Python/getargs.c index bec981698767ca..539925e471f54c 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2641,6 +2641,11 @@ skipitem(const char **p_format, va_list *p_va, int flags) if (p_va != NULL) { (void) va_arg(*p_va, char **); } + if (c == 'w' && *format != '*') + { + /* after 'w', only '*' is allowed */ + goto err; + } if (*format == '#') { if (p_va != NULL) { (void) va_arg(*p_va, Py_ssize_t *); From de1f6868270d31f56c388ef416daacd35feb152d Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 23 Apr 2024 15:00:52 +0300 Subject: [PATCH 018/217] gh-118082: Improve `import` without names syntax error message (#118083) --- Grammar/python.gram | 4 + Lib/test/test_syntax.py | 12 + ...-04-19-11-59-57.gh-issue-118082._FLuOT.rst | 3 + Parser/parser.c | 349 ++++++++++-------- 4 files changed, 219 insertions(+), 149 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-19-11-59-57.gh-issue-118082._FLuOT.rst diff --git a/Grammar/python.gram b/Grammar/python.gram index 9564abf5ec314b..11438e57da527b 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -1299,10 +1299,14 @@ invalid_group: invalid_import: | a='import' ','.dotted_name+ 'from' dotted_name { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "Did you mean to use 'from ... import ...' instead?") } + | 'import' token=NEWLINE { + RAISE_SYNTAX_ERROR_STARTING_FROM(token, "Expected one or more names after 'import'") } invalid_import_from_targets: | import_from_as_names ',' NEWLINE { RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") } + | token=NEWLINE { + RAISE_SYNTAX_ERROR_STARTING_FROM(token, "Expected one or more names after 'import'") } invalid_compound_stmt: | a='elif' named_expression ':' { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "'elif' must match an if-statement here") } diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index dfa2a3b2f5413b..e9bec3317811dd 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1699,6 +1699,18 @@ Traceback (most recent call last): SyntaxError: invalid syntax +>>> from i import +Traceback (most recent call last): +SyntaxError: Expected one or more names after 'import' + +>>> from .. import +Traceback (most recent call last): +SyntaxError: Expected one or more names after 'import' + +>>> import +Traceback (most recent call last): +SyntaxError: Expected one or more names after 'import' + >>> (): int Traceback (most recent call last): SyntaxError: only single target (not tuple) can be annotated diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-19-11-59-57.gh-issue-118082._FLuOT.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-19-11-59-57.gh-issue-118082._FLuOT.rst new file mode 100644 index 00000000000000..7b9a726d7c77c2 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-19-11-59-57.gh-issue-118082._FLuOT.rst @@ -0,0 +1,3 @@ +Improve :exc:`SyntaxError` message for imports without names, like in +``from x import`` and ``import`` cases. It now points +out to users that :keyword:`import` expects at least one name after it. diff --git a/Parser/parser.c b/Parser/parser.c index 35d672b0d397f9..b6683bfd1f1bc0 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -21,28 +21,28 @@ static KeywordToken *reserved_keywords[] = { (KeywordToken[]) {{NULL, -1}}, (KeywordToken[]) {{NULL, -1}}, (KeywordToken[]) { - {"if", 661}, - {"as", 659}, - {"in", 672}, + {"if", 662}, + {"as", 660}, + {"in", 673}, {"or", 581}, {"is", 589}, {NULL, -1}, }, (KeywordToken[]) { {"del", 616}, - {"def", 674}, - {"for", 671}, - {"try", 643}, + {"def", 675}, + {"for", 672}, + {"try", 644}, {"and", 582}, - {"not", 678}, + {"not", 679}, {NULL, -1}, }, (KeywordToken[]) { {"from", 621}, {"pass", 504}, - {"with", 634}, - {"elif", 663}, - {"else", 664}, + {"with", 635}, + {"elif", 664}, + {"else", 665}, {"None", 614}, {"True", 613}, {NULL, -1}, @@ -51,24 +51,24 @@ static KeywordToken *reserved_keywords[] = { {"raise", 525}, {"yield", 580}, {"break", 508}, - {"async", 673}, - {"class", 676}, - {"while", 666}, + {"async", 674}, + {"class", 677}, + {"while", 667}, {"False", 615}, {"await", 590}, {NULL, -1}, }, (KeywordToken[]) { {"return", 522}, - {"import", 620}, + {"import", 622}, {"assert", 529}, {"global", 526}, - {"except", 656}, + {"except", 657}, {"lambda", 612}, {NULL, -1}, }, (KeywordToken[]) { - {"finally", 652}, + {"finally", 653}, {NULL, -1}, }, (KeywordToken[]) { @@ -2135,7 +2135,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'if' if_stmt")); stmt_ty if_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 661) // token='if' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 662) // token='if' && (if_stmt_var = if_stmt_rule(p)) // if_stmt ) @@ -2219,7 +2219,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'try' try_stmt")); stmt_ty try_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 643) // token='try' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 644) // token='try' && (try_stmt_var = try_stmt_rule(p)) // try_stmt ) @@ -2240,7 +2240,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'while' while_stmt")); stmt_ty while_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 666) // token='while' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 667) // token='while' && (while_stmt_var = while_stmt_rule(p)) // while_stmt ) @@ -3514,7 +3514,7 @@ import_name_rule(Parser *p) Token * _keyword; asdl_alias_seq* a; if ( - (_keyword = _PyPegen_expect_token(p, 620)) // token='import' + (_keyword = _PyPegen_expect_token(p, 622)) // token='import' && (a = dotted_as_names_rule(p)) // dotted_as_names ) @@ -3589,7 +3589,7 @@ import_from_rule(Parser *p) && (b = dotted_name_rule(p)) // dotted_name && - (_keyword_1 = _PyPegen_expect_token(p, 620)) // token='import' + (_keyword_1 = _PyPegen_expect_token(p, 622)) // token='import' && (c = import_from_targets_rule(p)) // import_from_targets ) @@ -3631,7 +3631,7 @@ import_from_rule(Parser *p) && (a = _loop1_25_rule(p)) // (('.' | '...'))+ && - (_keyword_1 = _PyPegen_expect_token(p, 620)) // token='import' + (_keyword_1 = _PyPegen_expect_token(p, 622)) // token='import' && (b = import_from_targets_rule(p)) // import_from_targets ) @@ -4380,7 +4380,7 @@ class_def_raw_rule(Parser *p) asdl_stmt_seq* c; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 676)) // token='class' + (_keyword = _PyPegen_expect_token(p, 677)) // token='class' && (a = _PyPegen_name_token(p)) // NAME && @@ -4547,7 +4547,7 @@ function_def_raw_rule(Parser *p) void *t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 674)) // token='def' + (_keyword = _PyPegen_expect_token(p, 675)) // token='def' && (n = _PyPegen_name_token(p)) // NAME && @@ -4608,9 +4608,9 @@ function_def_raw_rule(Parser *p) void *t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 673)) // token='async' + (_keyword = _PyPegen_expect_token(p, 674)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 674)) // token='def' + (_keyword_1 = _PyPegen_expect_token(p, 675)) // token='def' && (n = _PyPegen_name_token(p)) // NAME && @@ -5948,7 +5948,7 @@ if_stmt_rule(Parser *p) asdl_stmt_seq* b; stmt_ty c; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (a = named_expression_rule(p)) // named_expression && @@ -5993,7 +5993,7 @@ if_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (a = named_expression_rule(p)) // named_expression && @@ -6088,7 +6088,7 @@ elif_stmt_rule(Parser *p) asdl_stmt_seq* b; stmt_ty c; if ( - (_keyword = _PyPegen_expect_token(p, 663)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 664)) // token='elif' && (a = named_expression_rule(p)) // named_expression && @@ -6133,7 +6133,7 @@ elif_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 663)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 664)) // token='elif' && (a = named_expression_rule(p)) // named_expression && @@ -6214,7 +6214,7 @@ else_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 664)) // token='else' + (_keyword = _PyPegen_expect_token(p, 665)) // token='else' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -6293,7 +6293,7 @@ while_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 666)) // token='while' + (_keyword = _PyPegen_expect_token(p, 667)) // token='while' && (a = named_expression_rule(p)) // named_expression && @@ -6393,11 +6393,11 @@ for_stmt_rule(Parser *p) expr_ty t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='for' + (_keyword = _PyPegen_expect_token(p, 672)) // token='for' && (t = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 672)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 673)) // token='in' && (_cut_var = 1) && @@ -6455,13 +6455,13 @@ for_stmt_rule(Parser *p) expr_ty t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 673)) // token='async' + (_keyword = _PyPegen_expect_token(p, 674)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 671)) // token='for' + (_keyword_1 = _PyPegen_expect_token(p, 672)) // token='for' && (t = star_targets_rule(p)) // star_targets && - (_keyword_2 = _PyPegen_expect_token(p, 672)) // token='in' + (_keyword_2 = _PyPegen_expect_token(p, 673)) // token='in' && (_cut_var = 1) && @@ -6590,7 +6590,7 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 634)) // token='with' + (_keyword = _PyPegen_expect_token(p, 635)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -6641,7 +6641,7 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 634)) // token='with' + (_keyword = _PyPegen_expect_token(p, 635)) // token='with' && (a = (asdl_withitem_seq*)_gather_53_rule(p)) // ','.with_item+ && @@ -6690,9 +6690,9 @@ with_stmt_rule(Parser *p) asdl_withitem_seq* a; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 673)) // token='async' + (_keyword = _PyPegen_expect_token(p, 674)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 634)) // token='with' + (_keyword_1 = _PyPegen_expect_token(p, 635)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -6742,9 +6742,9 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 673)) // token='async' + (_keyword = _PyPegen_expect_token(p, 674)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 634)) // token='with' + (_keyword_1 = _PyPegen_expect_token(p, 635)) // token='with' && (a = (asdl_withitem_seq*)_gather_57_rule(p)) // ','.with_item+ && @@ -6830,7 +6830,7 @@ with_item_rule(Parser *p) if ( (e = expression_rule(p)) // expression && - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (t = star_target_rule(p)) // star_target && @@ -6955,7 +6955,7 @@ try_stmt_rule(Parser *p) asdl_stmt_seq* b; asdl_stmt_seq* f; if ( - (_keyword = _PyPegen_expect_token(p, 643)) // token='try' + (_keyword = _PyPegen_expect_token(p, 644)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -6999,7 +6999,7 @@ try_stmt_rule(Parser *p) asdl_excepthandler_seq* ex; void *f; if ( - (_keyword = _PyPegen_expect_token(p, 643)) // token='try' + (_keyword = _PyPegen_expect_token(p, 644)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7047,7 +7047,7 @@ try_stmt_rule(Parser *p) asdl_excepthandler_seq* ex; void *f; if ( - (_keyword = _PyPegen_expect_token(p, 643)) // token='try' + (_keyword = _PyPegen_expect_token(p, 644)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7145,7 +7145,7 @@ except_block_rule(Parser *p) expr_ty e; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='except' + (_keyword = _PyPegen_expect_token(p, 657)) // token='except' && (e = expression_rule(p)) // expression && @@ -7188,7 +7188,7 @@ except_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='except' + (_keyword = _PyPegen_expect_token(p, 657)) // token='except' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -7299,7 +7299,7 @@ except_star_block_rule(Parser *p) expr_ty e; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='except' + (_keyword = _PyPegen_expect_token(p, 657)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -7401,7 +7401,7 @@ finally_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* a; if ( - (_keyword = _PyPegen_expect_token(p, 652)) // token='finally' + (_keyword = _PyPegen_expect_token(p, 653)) // token='finally' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7709,7 +7709,7 @@ guard_rule(Parser *p) Token * _keyword; expr_ty guard; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (guard = named_expression_rule(p)) // named_expression ) @@ -7904,7 +7904,7 @@ as_pattern_rule(Parser *p) if ( (pattern = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (target = pattern_capture_target_rule(p)) // pattern_capture_target ) @@ -11127,11 +11127,11 @@ expression_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 664)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 665)) // token='else' && (c = expression_rule(p)) // expression ) @@ -12013,7 +12013,7 @@ inversion_rule(Parser *p) Token * _keyword; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 678)) // token='not' + (_keyword = _PyPegen_expect_token(p, 679)) // token='not' && (a = inversion_rule(p)) // inversion ) @@ -12667,9 +12667,9 @@ notin_bitwise_or_rule(Parser *p) Token * _keyword_1; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 678)) // token='not' + (_keyword = _PyPegen_expect_token(p, 679)) // token='not' && - (_keyword_1 = _PyPegen_expect_token(p, 672)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 673)) // token='in' && (a = bitwise_or_rule(p)) // bitwise_or ) @@ -12715,7 +12715,7 @@ in_bitwise_or_rule(Parser *p) Token * _keyword; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 672)) // token='in' + (_keyword = _PyPegen_expect_token(p, 673)) // token='in' && (a = bitwise_or_rule(p)) // bitwise_or ) @@ -12764,7 +12764,7 @@ isnot_bitwise_or_rule(Parser *p) if ( (_keyword = _PyPegen_expect_token(p, 589)) // token='is' && - (_keyword_1 = _PyPegen_expect_token(p, 678)) // token='not' + (_keyword_1 = _PyPegen_expect_token(p, 679)) // token='not' && (a = bitwise_or_rule(p)) // bitwise_or ) @@ -16935,13 +16935,13 @@ for_if_clause_rule(Parser *p) expr_ty b; asdl_expr_seq* c; if ( - (_keyword = _PyPegen_expect_token(p, 673)) // token='async' + (_keyword = _PyPegen_expect_token(p, 674)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 671)) // token='for' + (_keyword_1 = _PyPegen_expect_token(p, 672)) // token='for' && (a = star_targets_rule(p)) // star_targets && - (_keyword_2 = _PyPegen_expect_token(p, 672)) // token='in' + (_keyword_2 = _PyPegen_expect_token(p, 673)) // token='in' && (_cut_var = 1) && @@ -16980,11 +16980,11 @@ for_if_clause_rule(Parser *p) expr_ty b; asdl_expr_seq* c; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='for' + (_keyword = _PyPegen_expect_token(p, 672)) // token='for' && (a = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 672)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 673)) // token='in' && (_cut_var = 1) && @@ -17021,13 +17021,13 @@ for_if_clause_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings void *_tmp_122_var; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 671)) // token='for' + (_keyword = _PyPegen_expect_token(p, 672)) // token='for' && (_tmp_122_var = _tmp_122_rule(p)) // bitwise_or ((',' bitwise_or))* ','? && - _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 672) // token='in' + _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 673) // token='in' ) { D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'for' (bitwise_or ((',' bitwise_or))* ','?) !'in'")); @@ -20301,11 +20301,11 @@ expression_without_invalid_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 664)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 665)) // token='else' && (c = expression_rule(p)) // expression ) @@ -20486,7 +20486,7 @@ invalid_expression_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (b = disjunction_rule(p)) // disjunction && @@ -22424,7 +22424,7 @@ invalid_with_item_rule(Parser *p) if ( (expression_var = expression_rule(p)) // expression && - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (a = expression_rule(p)) // expression && @@ -22474,9 +22474,9 @@ invalid_for_target_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings expr_ty a; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 671)) // token='for' + (_keyword = _PyPegen_expect_token(p, 672)) // token='for' && (a = star_expressions_rule(p)) // star_expressions ) @@ -22582,7 +22582,7 @@ invalid_group_rule(Parser *p) return _res; } -// invalid_import: 'import' ','.dotted_name+ 'from' dotted_name +// invalid_import: 'import' ','.dotted_name+ 'from' dotted_name | 'import' NEWLINE static void * invalid_import_rule(Parser *p) { @@ -22606,7 +22606,7 @@ invalid_import_rule(Parser *p) Token * a; expr_ty dotted_name_var; if ( - (a = _PyPegen_expect_token(p, 620)) // token='import' + (a = _PyPegen_expect_token(p, 622)) // token='import' && (_gather_206_var = _gather_206_rule(p)) // ','.dotted_name+ && @@ -22628,13 +22628,40 @@ invalid_import_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_import[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'import' ','.dotted_name+ 'from' dotted_name")); } + { // 'import' NEWLINE + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_import[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'import' NEWLINE")); + Token * _keyword; + Token * token; + if ( + (_keyword = _PyPegen_expect_token(p, 622)) // token='import' + && + (token = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' + ) + { + D(fprintf(stderr, "%*c+ invalid_import[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'import' NEWLINE")); + _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( token , "Expected one or more names after 'import'" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_import[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'import' NEWLINE")); + } _res = NULL; done: p->level--; return _res; } -// invalid_import_from_targets: import_from_as_names ',' NEWLINE +// invalid_import_from_targets: import_from_as_names ',' NEWLINE | NEWLINE static void * invalid_import_from_targets_rule(Parser *p) { @@ -22677,6 +22704,30 @@ invalid_import_from_targets_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_import_from_targets[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "import_from_as_names ',' NEWLINE")); } + { // NEWLINE + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_import_from_targets[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + Token * token; + if ( + (token = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' + ) + { + D(fprintf(stderr, "%*c+ invalid_import_from_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( token , "Expected one or more names after 'import'" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_import_from_targets[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE")); + } _res = NULL; done: p->level--; @@ -22706,7 +22757,7 @@ invalid_compound_stmt_rule(Parser *p) Token * a; expr_ty named_expression_var; if ( - (a = _PyPegen_expect_token(p, 663)) // token='elif' + (a = _PyPegen_expect_token(p, 664)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -22735,7 +22786,7 @@ invalid_compound_stmt_rule(Parser *p) Token * _literal; Token * a; if ( - (a = _PyPegen_expect_token(p, 664)) // token='else' + (a = _PyPegen_expect_token(p, 665)) // token='else' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) @@ -22786,9 +22837,9 @@ invalid_with_stmt_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 634)) // token='with' + (_keyword = _PyPegen_expect_token(p, 635)) // token='with' && (_gather_208_var = _gather_208_rule(p)) // ','.(expression ['as' star_target])+ && @@ -22824,9 +22875,9 @@ invalid_with_stmt_rule(Parser *p) UNUSED(_opt_var_1); // Silence compiler warnings Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 634)) // token='with' + (_keyword = _PyPegen_expect_token(p, 635)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -22886,9 +22937,9 @@ invalid_with_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 634)) // token='with' + (a = _PyPegen_expect_token(p, 635)) // token='with' && (_gather_212_var = _gather_212_rule(p)) // ','.(expression ['as' star_target])+ && @@ -22929,9 +22980,9 @@ invalid_with_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 634)) // token='with' + (a = _PyPegen_expect_token(p, 635)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -22994,7 +23045,7 @@ invalid_try_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 643)) // token='try' + (a = _PyPegen_expect_token(p, 644)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23026,7 +23077,7 @@ invalid_try_stmt_rule(Parser *p) Token * _literal; asdl_stmt_seq* block_var; if ( - (_keyword = _PyPegen_expect_token(p, 643)) // token='try' + (_keyword = _PyPegen_expect_token(p, 644)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23065,7 +23116,7 @@ invalid_try_stmt_rule(Parser *p) Token * b; expr_ty expression_var; if ( - (_keyword = _PyPegen_expect_token(p, 643)) // token='try' + (_keyword = _PyPegen_expect_token(p, 644)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23073,7 +23124,7 @@ invalid_try_stmt_rule(Parser *p) && (_loop1_218_var = _loop1_218_rule(p)) // except_block+ && - (a = _PyPegen_expect_token(p, 656)) // token='except' + (a = _PyPegen_expect_token(p, 657)) // token='except' && (b = _PyPegen_expect_token(p, 16)) // token='*' && @@ -23112,7 +23163,7 @@ invalid_try_stmt_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings Token * a; if ( - (_keyword = _PyPegen_expect_token(p, 643)) // token='try' + (_keyword = _PyPegen_expect_token(p, 644)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23120,7 +23171,7 @@ invalid_try_stmt_rule(Parser *p) && (_loop1_221_var = _loop1_221_rule(p)) // except_star_block+ && - (a = _PyPegen_expect_token(p, 656)) // token='except' + (a = _PyPegen_expect_token(p, 657)) // token='except' && (_opt_var = _tmp_222_rule(p), !p->error_indicator) // [expression ['as' NAME]] && @@ -23179,7 +23230,7 @@ invalid_except_stmt_rule(Parser *p) expr_ty a; expr_ty expressions_var; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='except' + (_keyword = _PyPegen_expect_token(p, 657)) // token='except' && (_opt_var = _PyPegen_expect_token(p, 16), !p->error_indicator) // '*'? && @@ -23221,7 +23272,7 @@ invalid_except_stmt_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 656)) // token='except' + (a = _PyPegen_expect_token(p, 657)) // token='except' && (_opt_var = _PyPegen_expect_token(p, 16), !p->error_indicator) // '*'? && @@ -23254,7 +23305,7 @@ invalid_except_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 656)) // token='except' + (a = _PyPegen_expect_token(p, 657)) // token='except' && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -23282,7 +23333,7 @@ invalid_except_stmt_rule(Parser *p) void *_tmp_225_var; Token * a; if ( - (a = _PyPegen_expect_token(p, 656)) // token='except' + (a = _PyPegen_expect_token(p, 657)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -23331,7 +23382,7 @@ invalid_finally_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 652)) // token='finally' + (a = _PyPegen_expect_token(p, 653)) // token='finally' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23387,7 +23438,7 @@ invalid_except_stmt_indent_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 656)) // token='except' + (a = _PyPegen_expect_token(p, 657)) // token='except' && (expression_var = expression_rule(p)) // expression && @@ -23423,7 +23474,7 @@ invalid_except_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 656)) // token='except' + (a = _PyPegen_expect_token(p, 657)) // token='except' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23479,7 +23530,7 @@ invalid_except_star_stmt_indent_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 656)) // token='except' + (a = _PyPegen_expect_token(p, 657)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -23718,7 +23769,7 @@ invalid_as_pattern_rule(Parser *p) if ( (or_pattern_var = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (a = _PyPegen_expect_soft_keyword(p, "_")) // soft_keyword='"_"' ) @@ -23748,7 +23799,7 @@ invalid_as_pattern_rule(Parser *p) if ( (or_pattern_var = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && _PyPegen_lookahead_with_name(0, _PyPegen_name_token, p) && @@ -23902,7 +23953,7 @@ invalid_if_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23933,7 +23984,7 @@ invalid_if_stmt_rule(Parser *p) expr_ty a_1; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 661)) // token='if' + (a = _PyPegen_expect_token(p, 662)) // token='if' && (a_1 = named_expression_rule(p)) // named_expression && @@ -23988,7 +24039,7 @@ invalid_elif_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 663)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 664)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -24019,7 +24070,7 @@ invalid_elif_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 663)) // token='elif' + (a = _PyPegen_expect_token(p, 664)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -24072,7 +24123,7 @@ invalid_else_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 664)) // token='else' + (a = _PyPegen_expect_token(p, 665)) // token='else' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -24125,7 +24176,7 @@ invalid_while_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 666)) // token='while' + (_keyword = _PyPegen_expect_token(p, 667)) // token='while' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -24156,7 +24207,7 @@ invalid_while_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 666)) // token='while' + (a = _PyPegen_expect_token(p, 667)) // token='while' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -24215,13 +24266,13 @@ invalid_for_stmt_rule(Parser *p) expr_ty star_expressions_var; expr_ty star_targets_var; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 671)) // token='for' + (_keyword = _PyPegen_expect_token(p, 672)) // token='for' && (star_targets_var = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 672)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 673)) // token='in' && (star_expressions_var = star_expressions_rule(p)) // star_expressions && @@ -24256,13 +24307,13 @@ invalid_for_stmt_rule(Parser *p) expr_ty star_expressions_var; expr_ty star_targets_var; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 671)) // token='for' + (a = _PyPegen_expect_token(p, 672)) // token='for' && (star_targets_var = star_targets_rule(p)) // star_targets && - (_keyword = _PyPegen_expect_token(p, 672)) // token='in' + (_keyword = _PyPegen_expect_token(p, 673)) // token='in' && (star_expressions_var = star_expressions_rule(p)) // star_expressions && @@ -24327,9 +24378,9 @@ invalid_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 673), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 674)) // token='def' + (a = _PyPegen_expect_token(p, 675)) // token='def' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -24398,7 +24449,7 @@ invalid_class_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 676)) // token='class' + (_keyword = _PyPegen_expect_token(p, 677)) // token='class' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -24437,7 +24488,7 @@ invalid_class_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 676)) // token='class' + (a = _PyPegen_expect_token(p, 677)) // token='class' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -25212,7 +25263,7 @@ invalid_arithmetic_rule(Parser *p) && (_tmp_251_var = _tmp_251_rule(p)) // '+' | '-' | '*' | '/' | '%' | '//' | '@' && - (a = _PyPegen_expect_token(p, 678)) // token='not' + (a = _PyPegen_expect_token(p, 679)) // token='not' && (b = inversion_rule(p)) // inversion ) @@ -25261,7 +25312,7 @@ invalid_factor_rule(Parser *p) if ( (_tmp_252_var = _tmp_252_rule(p)) // '+' | '-' | '~' && - (a = _PyPegen_expect_token(p, 678)) // token='not' + (a = _PyPegen_expect_token(p, 679)) // token='not' && (b = factor_rule(p)) // factor ) @@ -25629,7 +25680,7 @@ _tmp_6_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_6[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'import'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 620)) // token='import' + (_keyword = _PyPegen_expect_token(p, 622)) // token='import' ) { D(fprintf(stderr, "%*c+ _tmp_6[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'import'")); @@ -25686,7 +25737,7 @@ _tmp_7_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_7[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 674)) // token='def' + (_keyword = _PyPegen_expect_token(p, 675)) // token='def' ) { D(fprintf(stderr, "%*c+ _tmp_7[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'def'")); @@ -25724,7 +25775,7 @@ _tmp_7_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_7[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 673)) // token='async' + (_keyword = _PyPegen_expect_token(p, 674)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_7[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -25762,7 +25813,7 @@ _tmp_8_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_8[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'class'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 676)) // token='class' + (_keyword = _PyPegen_expect_token(p, 677)) // token='class' ) { D(fprintf(stderr, "%*c+ _tmp_8[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'class'")); @@ -25819,7 +25870,7 @@ _tmp_9_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'with'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 634)) // token='with' + (_keyword = _PyPegen_expect_token(p, 635)) // token='with' ) { D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'with'")); @@ -25838,7 +25889,7 @@ _tmp_9_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 673)) // token='async' + (_keyword = _PyPegen_expect_token(p, 674)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -25876,7 +25927,7 @@ _tmp_10_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_10[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'for'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='for' + (_keyword = _PyPegen_expect_token(p, 672)) // token='for' ) { D(fprintf(stderr, "%*c+ _tmp_10[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for'")); @@ -25895,7 +25946,7 @@ _tmp_10_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_10[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 673)) // token='async' + (_keyword = _PyPegen_expect_token(p, 674)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_10[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -26919,7 +26970,7 @@ _tmp_28_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -27082,7 +27133,7 @@ _tmp_31_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -29069,7 +29120,7 @@ _tmp_62_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -29115,7 +29166,7 @@ _tmp_63_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -35059,7 +35110,7 @@ _tmp_160_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 664)) // token='else' + (_keyword = _PyPegen_expect_token(p, 665)) // token='else' ) { D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); @@ -38652,7 +38703,7 @@ _tmp_216_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_216[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='except' + (_keyword = _PyPegen_expect_token(p, 657)) // token='except' ) { D(fprintf(stderr, "%*c+ _tmp_216[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); @@ -38671,7 +38722,7 @@ _tmp_216_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_216[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 652)) // token='finally' + (_keyword = _PyPegen_expect_token(p, 653)) // token='finally' ) { D(fprintf(stderr, "%*c+ _tmp_216[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); @@ -38849,7 +38900,7 @@ _tmp_219_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -39071,7 +39122,7 @@ _tmp_223_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -39112,7 +39163,7 @@ _tmp_224_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -39210,7 +39261,7 @@ _tmp_226_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -39251,7 +39302,7 @@ _tmp_227_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -41304,7 +41355,7 @@ _tmp_263_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (z = disjunction_rule(p)) // disjunction ) @@ -41350,7 +41401,7 @@ _tmp_264_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='if' + (_keyword = _PyPegen_expect_token(p, 662)) // token='if' && (z = disjunction_rule(p)) // disjunction ) @@ -42063,7 +42114,7 @@ _tmp_279_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -42322,7 +42373,7 @@ _tmp_284_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -42363,7 +42414,7 @@ _tmp_285_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -42404,7 +42455,7 @@ _tmp_286_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -42445,7 +42496,7 @@ _tmp_287_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='as' + (_keyword = _PyPegen_expect_token(p, 660)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) From d687d3fcfaa13b173005897634fc5ab515c8a660 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Tue, 23 Apr 2024 15:36:06 +0300 Subject: [PATCH 019/217] gh-118140: Make the``test_concurrent_futures.test_init`` quiet. (GH-118141) Add stream argument to unittest.TextTestRunner call --- Lib/test/test_concurrent_futures/test_init.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_concurrent_futures/test_init.py b/Lib/test/test_concurrent_futures/test_init.py index 113a4d1c54be03..a36f592b79b7cf 100644 --- a/Lib/test/test_concurrent_futures/test_init.py +++ b/Lib/test/test_concurrent_futures/test_init.py @@ -4,6 +4,7 @@ import time import unittest import sys +import io from concurrent.futures._base import BrokenExecutor from concurrent.futures.process import _check_system_limits @@ -124,7 +125,7 @@ def _test(self, test_class): except NotImplementedError: self.skipTest("ProcessPoolExecutor unavailable on this system") - runner = unittest.TextTestRunner() + runner = unittest.TextTestRunner(stream=io.StringIO()) runner.run(test_class('test_initializer')) # GH-104090: From d0b664ee065e69fc4f1506b00391e093d2d6638d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 23 Apr 2024 06:40:26 -0700 Subject: [PATCH 020/217] gh-118168: Fix Unpack interaction with builtin aliases (#118169) Co-authored-by: Alex Waygood --- Lib/test/test_typing.py | 32 +++++++++++++++++++ Lib/typing.py | 5 +-- ...-04-22-20-42-29.gh-issue-118168.Igni7h.rst | 4 +++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-22-20-42-29.gh-issue-118168.Igni7h.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 58781e52aca6d8..703fe84f3aad10 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -978,6 +978,38 @@ def foo(**kwargs: Unpack[Movie]): ... self.assertEqual(repr(foo.__annotations__['kwargs']), f"typing.Unpack[{__name__}.Movie]") + def test_builtin_tuple(self): + Ts = TypeVarTuple("Ts") + + class Old(Generic[*Ts]): ... + class New[*Ts]: ... + + PartOld = Old[int, *Ts] + self.assertEqual(PartOld[str].__args__, (int, str)) + self.assertEqual(PartOld[*tuple[str]].__args__, (int, str)) + self.assertEqual(PartOld[*Tuple[str]].__args__, (int, str)) + self.assertEqual(PartOld[Unpack[tuple[str]]].__args__, (int, str)) + self.assertEqual(PartOld[Unpack[Tuple[str]]].__args__, (int, str)) + + PartNew = New[int, *Ts] + self.assertEqual(PartNew[str].__args__, (int, str)) + self.assertEqual(PartNew[*tuple[str]].__args__, (int, str)) + self.assertEqual(PartNew[*Tuple[str]].__args__, (int, str)) + self.assertEqual(PartNew[Unpack[tuple[str]]].__args__, (int, str)) + self.assertEqual(PartNew[Unpack[Tuple[str]]].__args__, (int, str)) + + def test_unpack_wrong_type(self): + Ts = TypeVarTuple("Ts") + class Gen[*Ts]: ... + PartGen = Gen[int, *Ts] + + bad_unpack_param = re.escape("Unpack[...] must be used with a tuple type") + with self.assertRaisesRegex(TypeError, bad_unpack_param): + PartGen[Unpack[list[int]]] + with self.assertRaisesRegex(TypeError, bad_unpack_param): + PartGen[Unpack[List[int]]] + + class TypeVarTupleTests(BaseTestCase): def assertEndsWith(self, string, tail): diff --git a/Lib/typing.py b/Lib/typing.py index a0b68f593ca0d9..b3f4ba99f9ec21 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1786,8 +1786,9 @@ def __typing_unpacked_tuple_args__(self): assert self.__origin__ is Unpack assert len(self.__args__) == 1 arg, = self.__args__ - if isinstance(arg, _GenericAlias): - assert arg.__origin__ is tuple + if isinstance(arg, (_GenericAlias, types.GenericAlias)): + if arg.__origin__ is not tuple: + raise TypeError("Unpack[...] must be used with a tuple type") return arg.__args__ return None diff --git a/Misc/NEWS.d/next/Library/2024-04-22-20-42-29.gh-issue-118168.Igni7h.rst b/Misc/NEWS.d/next/Library/2024-04-22-20-42-29.gh-issue-118168.Igni7h.rst new file mode 100644 index 00000000000000..78c3e0fe17979a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-22-20-42-29.gh-issue-118168.Igni7h.rst @@ -0,0 +1,4 @@ +Fix incorrect argument substitution when :data:`typing.Unpack` is used with +the builtin :class:`tuple`. :data:`!typing.Unpack` now raises +:exc:`TypeError` when used with certain invalid types. Patch by Jelle +Zijlstra. From 23950beff84c39d50f48011e930f4c6ebf32fc73 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Apr 2024 08:25:50 -0600 Subject: [PATCH 021/217] gh-117953: Small Cleanup of Extensions-Related Machinery Code (gh-118167) This is a collection of very basic cleanups I've pulled out of gh-118116. It is mostly renaming variables and moving a couple bits of code in functionally equivalent ways. --- Include/internal/pycore_import.h | 1 + Python/import.c | 134 ++++++++++++++++++++----------- Python/importdl.c | 45 +++++------ 3 files changed, 105 insertions(+), 75 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index eb8a9a0db46c22..08af53258cde97 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -22,6 +22,7 @@ extern int _PyImport_SetModuleString(const char *name, PyObject* module); extern void _PyImport_AcquireLock(PyInterpreterState *interp); extern int _PyImport_ReleaseLock(PyInterpreterState *interp); +// This is used exclusively for the sys and builtins modules: extern int _PyImport_FixupBuiltin( PyObject *mod, const char *name, /* UTF-8 encoded string */ diff --git a/Python/import.c b/Python/import.c index b040c7d5c0f7f5..8cdc04f03dd201 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1125,10 +1125,10 @@ _PyImport_CheckSubinterpIncompatibleExtensionAllowed(const char *name) static PyObject * get_core_module_dict(PyInterpreterState *interp, - PyObject *name, PyObject *filename) + PyObject *name, PyObject *path) { /* Only builtin modules are core. */ - if (filename == name) { + if (path == name) { assert(!PyErr_Occurred()); if (PyUnicode_CompareWithASCIIString(name, "sys") == 0) { return interp->sysdict_copy; @@ -1143,11 +1143,11 @@ get_core_module_dict(PyInterpreterState *interp, } static inline int -is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *filename) +is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) { /* This might be called before the core dict copies are in place, so we can't rely on get_core_module_dict() here. */ - if (filename == name) { + if (path == name) { if (PyUnicode_CompareWithASCIIString(name, "sys") == 0) { return 1; } @@ -1159,7 +1159,7 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *filename) } static int -fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) +fix_up_extension(PyObject *mod, PyObject *name, PyObject *path) { if (mod == NULL || !PyModule_Check(mod)) { PyErr_BadInternalCall(); @@ -1180,7 +1180,7 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) // bpo-44050: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. if (def->m_size == -1) { - if (!is_core_module(tstate->interp, name, filename)) { + if (!is_core_module(tstate->interp, name, path)) { assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0); assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0); if (def->m_base.m_copy) { @@ -1202,7 +1202,7 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) // XXX Why special-case the main interpreter? if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) { - if (_extensions_cache_set(filename, name, def) < 0) { + if (_extensions_cache_set(path, name, def) < 0) { return -1; } } @@ -1227,10 +1227,10 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, static PyObject * import_find_extension(PyThreadState *tstate, PyObject *name, - PyObject *filename) + PyObject *path) { /* Only single-phase init modules will be in the cache. */ - PyModuleDef *def = _extensions_cache_get(filename, name); + PyModuleDef *def = _extensions_cache_get(path, name); if (def == NULL) { return NULL; } @@ -1253,7 +1253,7 @@ import_find_extension(PyThreadState *tstate, PyObject *name, if (m_copy == NULL) { /* It might be a core module (e.g. sys & builtins), for which we don't set m_copy. */ - m_copy = get_core_module_dict(tstate->interp, name, filename); + m_copy = get_core_module_dict(tstate->interp, name, path); if (m_copy == NULL) { return NULL; } @@ -1292,16 +1292,16 @@ import_find_extension(PyThreadState *tstate, PyObject *name, int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; if (verbose) { PySys_FormatStderr("import %U # previously loaded (%R)\n", - name, filename); + name, path); } return mod; } static int clear_singlephase_extension(PyInterpreterState *interp, - PyObject *name, PyObject *filename) + PyObject *name, PyObject *path) { - PyModuleDef *def = _extensions_cache_get(filename, name); + PyModuleDef *def = _extensions_cache_get(path, name); if (def == NULL) { if (PyErr_Occurred()) { return -1; @@ -1322,7 +1322,7 @@ clear_singlephase_extension(PyInterpreterState *interp, } /* Clear the cached module def. */ - _extensions_cache_delete(filename, name); + _extensions_cache_delete(path, name); return 0; } @@ -1336,11 +1336,20 @@ int _PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) { int res = -1; + assert(mod != NULL && PyModule_Check(mod)); + PyObject *nameobj; nameobj = PyUnicode_InternFromString(name); if (nameobj == NULL) { return -1; } + + PyModuleDef *def = PyModule_GetDef(mod); + if (def == NULL) { + PyErr_BadInternalCall(); + goto finally; + } + if (PyObject_SetItem(modules, nameobj, mod) < 0) { goto finally; } @@ -1348,6 +1357,7 @@ _PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) PyMapping_DelItem(modules, nameobj); goto finally; } + res = 0; finally: @@ -1382,39 +1392,45 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) } PyObject *modules = MODULES(tstate->interp); + struct _inittab *found = NULL; for (struct _inittab *p = INITTAB; p->name != NULL; p++) { if (_PyUnicode_EqualToASCIIString(name, p->name)) { - if (p->initfunc == NULL) { - /* Cannot re-init internal module ("sys" or "builtins") */ - return import_add_module(tstate, name); - } - mod = (*p->initfunc)(); - if (mod == NULL) { - return NULL; - } + found = p; + } + } + if (found == NULL) { + // not found + Py_RETURN_NONE; + } - if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { - return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); - } - else { - /* Remember pointer to module init function. */ - PyModuleDef *def = PyModule_GetDef(mod); - if (def == NULL) { - return NULL; - } + PyModInitFunction p0 = (PyModInitFunction)found->initfunc; + if (p0 == NULL) { + /* Cannot re-init internal module ("sys" or "builtins") */ + assert(is_core_module(tstate->interp, name, name)); + return import_add_module(tstate, name); + } - def->m_base.m_init = p->initfunc; - if (_PyImport_FixupExtensionObject(mod, name, name, - modules) < 0) { - return NULL; - } - return mod; - } - } + mod = p0(); + if (mod == NULL) { + return NULL; } - // not found - Py_RETURN_NONE; + if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { + return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); + } + else { + /* Remember pointer to module init function. */ + PyModuleDef *def = PyModule_GetDef(mod); + if (def == NULL) { + return NULL; + } + + def->m_base.m_init = p0; + if (_PyImport_FixupExtensionObject(mod, name, name, modules) < 0) { + return NULL; + } + return mod; + } } @@ -3724,7 +3740,7 @@ static PyObject * _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) /*[clinic end generated code: output=83249b827a4fde77 input=c31b954f4cf4e09d]*/ { - PyObject *mod, *name, *path; + PyObject *mod, *name, *filename; FILE *fp; name = PyObject_GetAttrString(spec, "name"); @@ -3732,36 +3748,56 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) return NULL; } - path = PyObject_GetAttrString(spec, "origin"); - if (path == NULL) { + filename = PyObject_GetAttrString(spec, "origin"); + if (filename == NULL) { Py_DECREF(name); return NULL; } PyThreadState *tstate = _PyThreadState_GET(); - mod = import_find_extension(tstate, name, path); + mod = import_find_extension(tstate, name, filename); if (mod != NULL || _PyErr_Occurred(tstate)) { assert(mod == NULL || !_PyErr_Occurred(tstate)); goto finally; } + if (PySys_Audit("import", "OOOOO", name, filename, + Py_None, Py_None, Py_None) < 0) + { + goto finally; + } + + /* Is multi-phase init or this is the first time being loaded. */ + + /* We would move this (and the fclose() below) into + * _PyImport_GetModInitFunc(), but it isn't clear if the intervening + * code relies on fp still being open. */ if (file != NULL) { - fp = _Py_fopen_obj(path, "r"); + fp = _Py_fopen_obj(filename, "r"); if (fp == NULL) { goto finally; } } - else + else { fp = NULL; + } mod = _PyImport_LoadDynamicModuleWithSpec(spec, fp); + if (mod != NULL) { + /* Remember the filename as the __file__ attribute */ + if (PyModule_AddObjectRef(mod, "__file__", filename) < 0) { + PyErr_Clear(); /* Not important enough to report */ + } + } - if (fp) + // XXX Shouldn't this happen in the error cases too. + if (fp) { fclose(fp); + } finally: Py_DECREF(name); - Py_DECREF(path); + Py_DECREF(filename); return mod; } diff --git a/Python/importdl.c b/Python/importdl.c index 7dfd301d77efb4..7cf30bea3a861a 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -97,9 +97,10 @@ PyObject * _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) { #ifndef MS_WINDOWS - PyObject *pathbytes = NULL; + PyObject *filename_bytes = NULL; + const char *filename_buf; #endif - PyObject *name_unicode = NULL, *name = NULL, *path = NULL, *m = NULL; + PyObject *name_unicode = NULL, *name = NULL, *filename = NULL, *m = NULL; const char *name_buf, *hook_prefix; const char *oldcontext, *newcontext; dl_funcptr exportfunc; @@ -126,26 +127,23 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) } name_buf = PyBytes_AS_STRING(name); - path = PyObject_GetAttrString(spec, "origin"); - if (path == NULL) - goto error; - - if (PySys_Audit("import", "OOOOO", name_unicode, path, - Py_None, Py_None, Py_None) < 0) { + filename = PyObject_GetAttrString(spec, "origin"); + if (filename == NULL) { goto error; } #ifdef MS_WINDOWS - exportfunc = _PyImport_FindSharedFuncptrWindows(hook_prefix, name_buf, - path, fp); + exportfunc = _PyImport_FindSharedFuncptrWindows( + hook_prefix, name_buf, filename, fp); #else - pathbytes = PyUnicode_EncodeFSDefault(path); - if (pathbytes == NULL) + filename_bytes = PyUnicode_EncodeFSDefault(filename); + if (filename_bytes == NULL) { goto error; - exportfunc = _PyImport_FindSharedFuncptr(hook_prefix, name_buf, - PyBytes_AS_STRING(pathbytes), - fp); - Py_DECREF(pathbytes); + } + filename_buf = PyBytes_AS_STRING(filename_bytes); + exportfunc = _PyImport_FindSharedFuncptr( + hook_prefix, name_buf, filename_buf, fp); + Py_DECREF(filename_bytes); #endif if (exportfunc == NULL) { @@ -157,7 +155,7 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) hook_prefix, name_buf); if (msg == NULL) goto error; - PyErr_SetImportError(msg, name_unicode, path); + PyErr_SetImportError(msg, name_unicode, filename); Py_DECREF(msg); } goto error; @@ -199,7 +197,7 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) if (PyObject_TypeCheck(m, &PyModuleDef_Type)) { Py_DECREF(name_unicode); Py_DECREF(name); - Py_DECREF(path); + Py_DECREF(filename); return PyModule_FromDefAndSpec((PyModuleDef*)m, spec); } @@ -228,25 +226,20 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) } def->m_base.m_init = p0; - /* Remember the filename as the __file__ attribute */ - if (PyModule_AddObjectRef(m, "__file__", path) < 0) { - PyErr_Clear(); /* Not important enough to report */ - } - PyObject *modules = PyImport_GetModuleDict(); - if (_PyImport_FixupExtensionObject(m, name_unicode, path, modules) < 0) + if (_PyImport_FixupExtensionObject(m, name_unicode, filename, modules) < 0) goto error; Py_DECREF(name_unicode); Py_DECREF(name); - Py_DECREF(path); + Py_DECREF(filename); return m; error: Py_DECREF(name_unicode); Py_XDECREF(name); - Py_XDECREF(path); + Py_XDECREF(filename); Py_XDECREF(m); return NULL; } From 8fa124868519eeda0a6dfe9191ceabd708d84fa7 Mon Sep 17 00:00:00 2001 From: morotti Date: Tue, 23 Apr 2024 16:51:20 +0100 Subject: [PATCH 022/217] gh-117151: optimize BufferedWriter(), do not buffer writes that are the buffer size (GH-118037) BufferedWriter() was buffering calls that are the exact same size as the buffer. it's a very common case to read/write in blocks of the exact buffer size. it's pointless to copy a full buffer, it's costing extra memory copy and the full buffer will have to be written in the next call anyway. Co-authored-by: rmorotti --- Modules/_io/bufferedio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 4133d3438253dd..aa52711941d374 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -2092,7 +2092,7 @@ _io_BufferedWriter_write_impl(buffered *self, Py_buffer *buffer) self->raw_pos = 0; } avail = Py_SAFE_DOWNCAST(self->buffer_size - self->pos, Py_off_t, Py_ssize_t); - if (buffer->len <= avail) { + if (buffer->len <= avail && buffer->len < self->buffer_size) { memcpy(self->buffer + self->pos, buffer->buf, buffer->len); if (!VALID_WRITE_BUFFER(self) || self->write_pos > self->pos) { self->write_pos = self->pos; @@ -2161,7 +2161,7 @@ _io_BufferedWriter_write_impl(buffered *self, Py_buffer *buffer) /* Then write buf itself. At this point the buffer has been emptied. */ remaining = buffer->len; written = 0; - while (remaining > self->buffer_size) { + while (remaining >= self->buffer_size) { Py_ssize_t n = _bufferedwriter_raw_write( self, (char *) buffer->buf + written, buffer->len - written); if (n == -1) { From 0d221e9a1952949465df4e737e8d3189bdd9632a Mon Sep 17 00:00:00 2001 From: Animesh Kumar Date: Tue, 23 Apr 2024 22:42:49 +0530 Subject: [PATCH 023/217] Fix typo in py_compile.rst (GH-118102) --- Doc/library/py_compile.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/py_compile.rst b/Doc/library/py_compile.rst index 38c416f9ad0305..a35fa0ba3f7bde 100644 --- a/Doc/library/py_compile.rst +++ b/Doc/library/py_compile.rst @@ -96,7 +96,7 @@ byte-code cache files in the directory containing the source code. .. class:: PycInvalidationMode - A enumeration of possible methods the interpreter can use to determine + An enumeration of possible methods the interpreter can use to determine whether a bytecode file is up to date with a source file. The ``.pyc`` file indicates the desired invalidation mode in its header. See :ref:`pyc-invalidation` for more information on how Python invalidates From 2e7771a03d8975ee8a9918ce754c665508c3f682 Mon Sep 17 00:00:00 2001 From: mpage Date: Tue, 23 Apr 2024 10:20:14 -0700 Subject: [PATCH 024/217] gh-117657: Quiet TSAN warnings about remaining non-atomic accesses of `tstate->state` (#118165) Quiet TSAN warnings about remaining non-atomic accesses of `tstate->state` --- Python/parking_lot.c | 3 ++- Python/pystate.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Python/parking_lot.c b/Python/parking_lot.c index d5877fef56e4d0..b368b500ccdfdb 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -194,7 +194,8 @@ _PySemaphore_Wait(_PySemaphore *sema, PyTime_t timeout, int detach) PyThreadState *tstate = NULL; if (detach) { tstate = _PyThreadState_GET(); - if (tstate && tstate->state == _Py_THREAD_ATTACHED) { + if (tstate && _Py_atomic_load_int_relaxed(&tstate->state) == + _Py_THREAD_ATTACHED) { // Only detach if we are attached PyEval_ReleaseThread(tstate); } diff --git a/Python/pystate.c b/Python/pystate.c index 06806bd75fbcb2..bca28cebcc9059 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2096,7 +2096,7 @@ _PyThreadState_Suspend(PyThreadState *tstate) { _PyRuntimeState *runtime = &_PyRuntime; - assert(tstate->state == _Py_THREAD_ATTACHED); + assert(_Py_atomic_load_int_relaxed(&tstate->state) == _Py_THREAD_ATTACHED); struct _stoptheworld_state *stw = NULL; HEAD_LOCK(runtime); From 258408239a4fe8a14919d81b73a16e2cfa374050 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Tue, 23 Apr 2024 20:38:33 +0300 Subject: [PATCH 025/217] gh-118189: Revert accidentally added incl.tar (#118190) --- Include/incl.tar | Bin 1864192 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Include/incl.tar diff --git a/Include/incl.tar b/Include/incl.tar deleted file mode 100644 index fecc1fb5dec534148d40b4a4c3f1bcfce411a832..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1864192 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%0DN$)T@_T(Wn2(qM_vfI#uE# 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
UW98&SdxSMC!PoVY5J#99u=5s5?@ zFNc+jUyxQGY6@-T5w-%0YEHUWftG7pZlRXulNY_J-(~YF<2l#~6{P&G=yH|h@aiJ! z%2-Q#*PNO{9?D8$MTi5tgo(&Dh0T2|%ae)x(3llj|QXJs3yaAKKQymYuZ^zJJ2%a?&`s;iMR zO_F3MUerK~Y&>nAt=o$pY>2Aa$$hdKdM=03McIcTsLbVFgo*(r2s{|{cbOkvmd0ff;IJf-GVyuP&c6OiKcHlFNduIE`t~4yi_WKv%-3Q zJK9U2(ydhjTMnT(2A$M_ z%b`m$@8v-4wY%lOog8dlgwoclKe+5JMsqLd6WtR(Kl=I6qpt?z{_rw+`ZPIe@u}65 z-D6}Q$8z7qOUBJd02p3^Sdyl_>1dJh|3Fh%7I`!I_mey1^}|)bb3`Q>?DSo-`Sj7h zefjghTmp@!xbicYjk}|#)4SeeHb}c!f7G~s$YyVCZM}T{Joz{L{p#fl`Fm?e{l=%Q z7q6Z@OSYfCc=_Vhi>;S0wv(;xms?xU|1H^iNG3wG7TLTz18Y}{VSf?=!gqjOUPj8< zlC3S(IR5=V$#HMA=ns-LJzl4G z-PvH=ZCtPE_*UpcyjsxY$Mmo}8cllZ8;>5%@1_IQ(Bq504tn!sGa2>=E@|)>sFdvNW7_~cu5T{V~U^khtujbd2@#ExTF;9m0UzT)} zt4Vhxl&u)&lSy*3=v{+>*#}bT&H(^mjO439BS|`w$H^snVeqNT${r`}Z$=|@!xWvs znsV`=f=uzjJ_f&v15l!bvc@AU6?V8FRThR14vf-wt>#|xUlMkVKFzY>-v{YD;a|T4 zJF4L0WY|wOHwUA^4eHDwHPcZbzCbtqB=|+5kA7C-$_9U15`jZQRVCz8Q{Ru<|NT2d zO}b;)OIM?Ma%aXGENZ$hldvO}*}uo6kdu1iaOS&~RT#NM`!@HEgFk{6hvCfkV9^Ie?IuQTlTaYC=& z1_qh5Ci&{CgrE60Mdt8OE?SBi(EUjd-~fGA@`r@kFn*^2_3$5?-_Q=d>p|~>ObAZm ztP5qk`BZ~LQLGas`F*mL{No?`E61_={PovxTgX4t`Gx>8t}FRtvfgR`A6S9S?~|uO zdUCn0M*ar>B!+KNI=O4jX6e!7aBwr3-K}pJ)>x*m>AEfWSu-(4%M=rTQh%cgiaXid zfL2u{WM)ITQ8boqFIp!2_PS%L#csb3jgoaQq3nvyAEPz4F;7fmyaIoyhW*DM;?)=l zsqS6(@&YlG*B_Z$kCU;LFnjmkLF7UD<7RM^4d&}0_ZTk`Ikn)+lRt{ARkIxKpZw9N zKz1ev$mZsTz!&w6LD9nj5d6Z;hqewx1o$j@lJT$;#=+it2aCxDSK;(j&JWkuI~#e8 zKd!eo{(HkqA%I!rDTEKlFX|~&%ir+oaTLR^w}k;IoKPkkFbebr34HFg-kiUsWm0Yy zoHfGKiJ^fBZvOdu zhilUzq$+JG$wuQaH^lmPwvwNd+w0+IkgRV(?t@V_sBLPw^GemQ3sn^)$bIOt(!?dR ze31OhO4ZDcA3sl?)y9_PYAcFJbcqdo$#`FS^IqNm4adsz8cxPsDi?!sZ}55hLF|iS z`@iSd?_vM|cSq{qUeM0IPDkB~!ARY6P#@?Tm}BcTzjnXM{%H1b z0QhV^9l2$Cs1P#)OoIsmhj;Q{@+2(^Ma*B07`BtT#BA^ty9Q zL||YWYfCGYp?n|=q}Pgrbf{S1DgndmY;ZH3%(}C?1iRf)KYN@kGPUjGW|WK54Fow$ zfQ-#1{s$FcCx1Ghq`k{4suYq_4S-~>2H520UnZBAutKyr+PM;`3c{yh^ghq*reyD^ zc77*!4zXl2L({+=c8aFzER{_@H`4A$wh(paP-6^;^l~;}$FZX+7=BCmEs<~7^Rk(O zgwn(Al>6;&UO1m9;b3RVZc%Mc*119dM9EAN!{0_jsK6j@!`@X@-hdtqeOe=rxmh14 zgDy7cwrZ5IKQz_hJR6KI4Vhf{5P@KTn%iFUEv0dz8;&-d+#L)vaT_>dPmE&E*uL=~ zqZ8tCF_u*# zs*SW*&rX+{8qy=|UTLUei)!)|`M}Pzflyz^H1d?AF|?C8iboeC(Bi-RplP$;pG>24 zR3CGUh5nGdn(eAmndj<7%n*uv_b36V0Pu@~k{mT+^-2FIdy0R@H; zA%17KI{~0w`U2KJZeYUlWdNGU>-hON`8t3=s`(biUHAfLJT>e4(kA1tVU~QU<5!YE z)&9q_KAFxx&*CpKUt|Aoy?U_&`~Ug&&dVKa1$KD;^X%EHFZO>?ioeV+pZ$OG1Fl%` zrEb5??-c(McD`1udRad1z%s_E1CD5{#?9R=uodK+`%^3+O^&Tg-4LA;h%}Wpc)kn2FZjL}vtWK^)CJ;cK}3z718U~~vmKoI7WV(!uXdj8e3}2h%#1Hm_04|_ApI(`Ujew~ zzk~+BuH}5p&Fa3{k$lSmY&_0+_1ieJmY6fw4$S|(PeRfw7|BZy3N6v^{7ol;S00p;=4tbg!K>98_~o?+Mb{GGEBUPl zMJ0D?W1W)oz;>~=ZC~%R7Q`qvJ6^`Nx1mb4{~zaP42UqVgL~omkvKf)e=}Ta#O~Z@ z#Nu(_Q;el{r{x)hz7ea1se%&a=1zfs--s=ssMFLY+HVBq8?l9`Y~rV?-*sKjL&DrC zFjM#e9JBNqcR;eazG;`Fls<~>4fqPqu4f1v>CEBv*-Yke)XfpVkmF$WT@0`^)>GSA zmfCiOK4YD24Fyx7#IU$ll&zpvavCSerm`S@rv0E+T-7||yED8;DDSL9dB;#zUKa)l zt}A|g9LPah+pw0UqLQsf{tQ~kNyYAjR=#@1eRlO2=pay=xwZ_I|qPm$3SX)`orKZYb33Y16YSwv&lmI^DmMM_{v$_6;Pbvk0RDeT;Nw2p+WFR@rub_@uZQ6KLPGhJ5GHeQ1CeDzoC>M_ zkW;V(th{a{r&xK>W;W?!qKGFDGMeyc)V<>uElsC8$I1T{#nSt~jYp5p7=|b-4+16G z)p8mI;wJba>38QiT*E2L0<|zmF`G}wAmSYvGRd+eFtTio{4|fx)=B}cjyX4b2jVV9YI=4z`hyIaWb3E<;a|_f zcQk&MfNReYIHbvm5j(*!nI$-p;daZK!Y6kk1LdHe6dlwP?Vz@Dgh!g@aPtCI3n!jT zW*KLs={0GL)uzt;eufoCY7_{}Dq`;?V$T%Hn_x0v4z$@7jP(H*y#|}oat1JFe4n}q{8&Mo*>d#lmzWz&O({CPcDZuC@6FY^d{cq zaCM!Wbt>s1$`?n!R>rPu5y#AFH<@KxFR`L7Yla&Np@NqKL|gX;^oD_r;ry|jBtx-6 zO|$cju9AwN<|>#t_#_^*0>YH~AOtKLqOUA`0 ztki&`$*(a$b^G%1ZbpM_zw|hK!`EI+MEdt5h!IY#LzO1U zm4ur0dNta-MwrOOUrkrC^TDgkOX-OO<*Y;jT!=@4t><@>k zOA|Odpj%MzyJT$>5?K>ddw}IZDPT2z*SpEZ1XbOl!=OhI%!1V$>cDn6TTs(hSM;n& zw_WTATE%N0rEX>WqV~00Fv|uN+Ksk-WX$ZXYAV|CkrBQ6XFw_?W?O$Nv5Hfa;;^|b z4h&gC8GH}@FQNjILo|VkouBZJ^~r`A4_2p|`EY>ZBK=8`em9}=uo@wmV5MY5dVrbv zZl#S0#+go}I>O(H@wYN*g;qx#Ox&!_wqW^42hOdy0(`lD8r`-(SYg%wGrha%&aWTp z?2nAb{XaWfFQ0AE|MTj_^R1WL>i*}m=U?J~AFPjKgna&=lOK@u@m=ur-xU7y?tpR$ zr=i>2`|~*-eaG3z(c9Mc`lpRY2)C>h?5ux!oZLxS2kRVG&@pWI32uX24B%RzD+eAC z>|+q;IlQ>B2BX@<496cQAFvR>Q>6AyxI2MbZo0kM1ZLzV)be9kDln@-f$N5~H*t*; z*Z9}7jfAmeMP_%j*W+>li)T}8N`sea8&&OZxzG88FC>+1h@p!W{HFT>{xCX|K42ZD z>x)#neVVCqkCN?^hJK4%BFzoPpu&AMnoK@qXe8Q0PNy)KA%GjzY(znjh+zFei8SfM zlm#VN6NB01Xma~F>5T?Bx)7j7vi`d z`qA9>SeUxJ#5SM1Kf2`RvpW&CYz~3drT++$#O77+3YCWl{k8l0w~8pKTnyk(8%}00 zi8hnn?t;4lSXY}cIk11AVI_kbbof|-(Y7B z3wkjg4Klr78^e9*5aQ)zx(PHPwdrwwLy8<)gHKa*m>7>7xxzF0!XcF1A?WyqBFxI& z{#dy#_4P=4|77ci3(cVOo`nVDJvnXdA~D_Zky>xy z)vx~pV)&mpMDittW2}Fb!eKX`^w&3J+Ujrp`J{_4*c7T;{ZIL!(M#5GV-V*8JhcEh zt*qc)gxXt7ahpUA6>vzye?hg2Z)J)(f)nFOf6>DfvLusd4O~suBuGuoXD*gRgff33 z{xv8btWsJLT@0w?=pgY3``)0t&Gnwi$0P01O+ts6n8eEhz*DMU(7tfyX)CL!J zdUE2y^#pF55l%*|F1M`gpPWlWxp!vGH{!R1n&yWm*WJw!RTwllp4q@KGmj?P=x8S)Qb=A2~2Cotld2k?7EQ!d0OAf6e zZI$76&*YZ9fVG|S@zoKI(3uXF(UIQ`Geksm=8OJtLS-rI5w$67 z6ojW3D@tj*m(Rsgfog4@9yX{~jV@eI4?DX|i%~%1p4cU?L2zpfr1g8T!W!3qEO#`TiF;*A`M-XZ?l6zmlIl_Qg4KRJI;i%ZPxoP?wJ2+8 zG1S4#du8=-wJ-2xS3RzcT(l-*B%m%xBTmL7x{Yr zLATg0Hah%zx|<%gnx{5j3G07gl~NeU)kZ&7thr1+Hd1h#Xug@hzpf6~$8y8lBXpDW zjJUsma;h1w|BIG~LSI(L-2Z~U)C&#GYD~>H491KX{R{oPyGJ-R9E~}=3-1>B>ZQIvk*_J7o-#I0t z9EF0mbEP33>(*eqf9I6c*A#h> z11rj(I#0TrkO#r#@=Al5yb zq73?!o>uX)zT>UFePm)k!}Y3@Q{`(}%EhXku;MFeU*a5v(cm~{xLUwTg)qUY=aGJG zLeMKoyOjA~soSlKRC`^OYw;2Xl8BbVi3aW2IowcPI9`YY1;djnqTUR;5>sa!ukZzS zwIfzy$KfO5xmB{hjmUKPOpxdnT=MMg3W1i0=uu8e1amXU6=kCl&V|4nx|H2Ye;NZ4 zuXv(^N)Ypkn7bt?!R!a`3^mjRE0mJJ%8~Vrzr&-t# zsnB;yL)0i;g}H8}6_3WNeUY@6dTN~>9UMj?sNMK<5r9m`B>F%}_``T|i$K_0V&j?> z4(=!{{_)qOHd}UIwRe2}=Agw490%>QGyDsqr`xlXO3Jha=TTt{_t0p%?%C_0tq4I`WS z$-y=40q#xr)9_|-lZ+QP7YO)(&Xm}2xmzZw+9a8-rU_q|YQDVHWTuVT~e z5V379mRCemEUrkVSX>oMvD_yX7a}B;5=>MmfYpl*j`)}|pO!T>xFkE|KM*hCAF4Eq zRDwVga*X=}H`$Nsn=5*KfrNfsOb!lhWHeLN{)|I2r)HTkrK*$C6k)yCRiBBm344Xn zb!NJV>5xyh%h}`x53*s-nXY!GyK?7(7$~WIh@SSKsWM?Av-kj+HE~Kkx>M2Rth>du zi^8Zn0G3zg^4asZxu@tr<))UTny{fjBiKK%ElQolzb(YPRLd zt2%5jwmd*;T9dG(Mo1T93IlsvDAN}Am*^*sZP{eV@d;RBI3Y|#?%C2e2u|MFM<5wCsyAJV_T`i9t>65P^AeBos1PxRv2kkR zaSS8a%USn|5!TpRPY?(OZS5@K?`MdL8uaBZjfg(Cd72%lDMN(r6^UPgCizHJJ&`p) zJ6c$Z>ymK$K=DdqUhJjiF0H|bvrv*5r-<9*{IvaKf-xq9D>?4NS_7zL z`RH1VG%_8gn^%yQRwenVGg&)OB@0S^R-uX(9)DF~iYozs`C*C*mvH=|f`T2!N*Yam zR-q~?LVu`2=Htl73=>Mv=@@ir)L9&yVPH5`G5y`|egzIYlO8hMTr~k>YCxpByz@ZS zL1mV$I|8Zn_@-$Hq6fK#53!ChuyFWyQ5c9XD^;Ovp+XH0TY(rP#U?y#1r*9JNnci| zU$O$RRer%9Xqs(rp^gV0M|xrXCFP`ML#|>Uv@WwjUBzE&cJyEFir(KHSoQy6w~TD< z59j@@yZ^cMZ2Q@B>;K)_dM5dwcfRC*zJI5#fa&x9o-|K8Eo7>(;T^7XI4}I?`F^1+ zN{@gdSycW2D9ttT9GI0Fx6u%*QpUL&;p*IocVb|IBiS+D4~4gZ&a=JN?m-g|BI6kx zggjOdD=&@;A`Dxce|8Z;uYGMoNM04QfmukB$qcV}Uk90CJ7?|Pj`r0mABJ_KsX3U= zkY#9iGoamyTNZXBZwa%Ra>mO&ia=p4M&f3qb*a{bEqLx_p>2X>H}h27EXIdIdalfH z|MTYNF4cJ|VIP%+BS-&_leM)x=rr~5jS6lud?<7c%2~#*0&ch;H*xnqs$&!Ofw@M9e8YGTGDN=owkdehC z)z7lv0$!zvzZpUR$xw_Yelw-O=gydU-Jxmh2`!GXh{N9 z`_C*obo@Vz6&OGN-+A`xIrD!w@BeMTdhv4WOaA{-SN$SCefHleUg&HeA_xkLcEAcO zgZ%Ss!HRX(8z5(bGMSV~sEoQ6zZ$krTBrNcysB|`*+sqrb#{nNkhTfkk4392M1=?D z6O{x{9;PP~(>vn;q|X_)*OB5fCKmn!uwH)I3VPA=3b4HA8G@~#P@{b&td zSd#$)X7h)3t3SfGvObJr5NkAqf5#-YO721c_*40&dL)Pj3)V6XL?teMlv5{3JxK}@ z2^l0Zm!$Bvc11*ev|Z0ZU6uIXvJ2JQy4hslKRa(urK)!Gpo(FnK}oAe*6FLaBL+75 z#T{}Gr!>uAG~jSAz&2wuyCciAYPPOeVmc2vkbvzn;fT{O= z1<(uxX^y8hsZl$DdDeqFsq#`L9!)Q`4A)k2xXK1b=!oHl(|Z|D5}yzw=R!9KT_I}s z9;D7c4F^X}*-(=t>cOdJp#Y4BspS~}uMvHA&1gxh=z z<0mo3DuGv#EmVY|GH8WhwfkJXWZGL+?9vtNB#CWuh7c63qn!3BF4da^tW+*!2A>8! zoo&31&?O#UHo$&jJkFn`(|KWBdzrG3enOZ_h)tM|5V}>zRSxPW1Ib%QiI{pO7ti46zNPlylvA+JzrjVMtuKzgINj!UP!x+X>s*Z?G>j<$#eaN>Za*x+&mP}{7pDRRDW6IRt4fS7v zuGB5l7gPHRL9B%?iBMADep216HykRq<-v0hWpoaVYPc~wyY04Ot%6(_NQEDbe5VOw zWewIqv(z;_B)dUUqR7tF?oBFN1=(zXc$I+KvT$!&K|&+(5CpvVUPN0e+oB3G;!hN5 z;NxZ%4K`hQxuk+$6pnEskbwDjT5%}2^*7voKhWHtmC72*YM>Saf7*mF-WrO<2-N@< zI<}#=%f$BWoC|C@h6JlCceLzz5q?QC0&#u}fVR6D#9lKR9SNtHAS^LKmc^YTGLqrc zZp4E`$L8qB*0m+Qz-)VX=>p>o4g+j;%Ue%ci>X**PYNed#UA-I^l;yp4X$toh+y7o z%VY|wE<(k!A=8+1xO~abba*`GS%x$ot-X%) zZ?Rd}@^^bZBBPyvLuWKQnXG`FRPUVmqq%8UR91OfQ8Q=3-Gex z@d&BGkWi92x$}DrT`C1Barb02WqD9F5Yvmu!+a7Jq+cXM3ka!c5p-Sc!Um#M5mPM6 z55=+J;kqo@9`|=?FIQGO4abE52rS7!?i&TAslzD6 zi%JFqj#am-Wbmsit*M0k()3yevB((B0dL31oYg*~bl#<#C(_4k*|4YW3NJmC&~MHM zZK`6F#$tBp1GvuDIJ%iq zB1j<0?>Gt*WsJ9-%G8G-Qeh-np{ihC(<5cYVZmUr(#BG?eb=2S^CmwFSpVAGtD)r? z>OwyY*L(y`y+I`pAf*q$4df_K!>G5a_?VYGG`1RJIQW;#26JR!AE!4+uBsfU%Sov! zA~3b9NS=}6p&}@Zc}ILjT!jyXAC2?|7b`d&jSEzDRmB7-7&8?l%LZaWoJ>7_Dso}> z#Oned_ES9DVz!vYb)u2pUh}oC0E6$}wT^aM>Cy2S(x$r8xS^e&z*A3Rt7X0b5?of1 zu3;e#bxh|QoCC;uzSr7so*yv%#_qXtuphJ!izaYU$l~WM_kJU=tcqGd+FS9t|CEU3 zh+7x3F!6@$Leetjen_?p!D2&FeQ!Jj-p) zht8UBU5OWE`4Ee~joB-=gyQF)hA8vZ6u4JWiuUGOQjoQ(ME$p+-cAH3`fVR-`OS^5 zDVh$Fa~!nJj*r&;i;I_NL0qP$#)w{iB-g?*O^N}6yI3irxNs$^ItL66IPkWqbj0ap zf;1ZNywQq4yA+*DtDIHa=wDMg|E{#8@S*UdA;;d>NomJ9ss;OJx!=8wM=c8Tr?@ou zAob46oeGWIdT=n7qbpE8nuUeLB^zEtV5;ADDZsIGqRstc_Dp2CD+8$DUoFEsIf-epc?P5dtDEwlnC@i6m3`cm+;oxy1 z1qRoxi*3pWPIXS5+!@*xP0_Hrb-HNKl zed~H4o;pKul2I=tg+L0#y8n;RUa4fuanov3DNE2?U6+4p&gBS4y)Wu`P^2f#R;$98H;uH-Aw@D^P zNxUHG1;7p8#6kuDtT&*JRSPk6<{4B=lHPIqE{U$R!H3+CBf@fIn8A4hZ}N~so+~&l zvh;4RCrm!ch5?!x=%woUkPoa_f4m~t$5S@~D*5^B!)cOWLbsT2!Xa8s-k=q+!UAAw zibTbBB+WN%oSj^a`MgU0ye|^$TrJcKb+F*!>c-P!fpA2m;AKvICVsr_&c@yzY1aK(~xi5xuWVRtS-MxIu%#jPl z)c`}}=?%x=R0f?S$TZLWkiqwZ3%o{*BreD@l)T3?DBtkls6T|QJ5QxYVCC>`6*kDE zWf_v=Dm<5rVa6i@WPoXE388y%kc18Z3+gQz3>!yEgnGF(|TxVlW|F z4o`LYni33Vct!aH9h!-U?d~wTakMo-pR4SUxcDUW7?R+I@B{07ogDFR z^2EQv72Y=Zwzd*y^xd29v+)&RfX<2xLAx}>f zHsY2`cbe64>~^)6>u6`>_2U7p9(|;9$F3FKj824}h2!8cUL3;;#8*xy8FaFd)j9&! z=NtaI1G-~_L@5)HAUcP!+zW@2`<(K@=4$2Zw8b>W>MUmsFAb<{q}Vvyw^^Y_ za$v2T)*O$tv+O)rN za3pa=I1z3HpsmIa%FU5sC44?BEKZtwVg$7FfXO*7O~*y6$9 zs@uDJk8SPbHrwwQ^5(Hk)6=^9jda@C0b|7nyOY7Jhn?sdEaky`-EDu58`>1{>|lv^ z4srY<7dYUEdBXfB*h!mMLj?D5SE=i;8$>quJ9S~tu%kg-;p{tCd)4-lOsePsSDfmc z1N&eI^o_cQrNFBZok$TV5g#sk-WwPa>8@nrRKvmB4{AIZJ1p7U#Jw4a50+I6IEzm3emad&E0+B*=ZHAVM{&x3%-QS$IKH+jO2j?oS zP4@uxX|!lbtTuC7+P)}xmv7<@RMW}h3iAbNT>RiyE8n@ zgwlxYEHj^sU6$~PwKHQLKiaFs|3h0WQl zV&<8~iTQS}KDvg&$sK@<@akCl zV08Xtn_!FV>*Z*9KW?(Ay-Ydh^NYL>U50i2cFl`3;%T+hQY0lvGK;WANJ3Z@GT z8+s9=%N(`0!%Mb7G~M0Kc5(KT4|)44dl_)Q-_C@t%B{6-Q-?WXrr70fXFf(SVzmXW zh9F)w%cH%`7}S00fz`4429Oa%txwDaAP+ZugGdb|A|6h@;W6C|heM= z+UfrJ(XO}!We=cRfJ;M&|JC}DyMVaFVCyQYghs7qRg3Sqlb)+G;V{{}SRfTV-rmHX z$mF4BNCNx3%sSqngQc?^=XpIxlko-h_!9MPCTcH$@PP3gPf<%aT9m%$0~mQqh`ZkU~#VuI?oHU22Xxq@9Gs<0|axtjzV zi;>;PQ^M0b-ujZHdHk0gQf89{uH^8@tT*66Z}vzx=X5bnI|!ME$`eFMvPZ|`5w7bN z_dhsULznUXb3UXw+S%LYjML50M^cNA%)fj zXi)u-X`xilvsp=&C9ift_p3mBBHVtj65!*wUf9)j-ktjLs{hY=QRE30##|43-JyKr!P!tT|<=9$D!di#9o`PX^NOR zZpOI+5vR0{8@hapgVdD?OS3u>2yf!X(WT;u1p{3Jl;{|@K9cs$xqB`aSKe%)Gvq`P zASOt#EZ26`z|l{Jzzlif=+Au?&s}jBdN~7M28zZQBQeFeO!>nYoLu-gp5! zXS|FLs}|;bb%v_o!iziP^TRO>;>QsEm3<@j6ehN*JrQfyzK$ftFj6@6S;6t%@wdra z=szV$PZ5@*FaJI=L0qxvc6^)`^b3vu2 zDsf=aUG!1SHT(rmPjQsd05huuhD?)ibSt#ROrr3U)EX||>8NO@W$FGkS-s zd-$o9g)DG7385-94J=H4-FS-KlDt%&KMWIvSSVDT>f|O=Fh83FkV>7SZN$P%bvjLV zib^n3-B9>Ems?aS$)JbftDfhhTH0(vKo11YXLz&GQ(}_wd5Vjhg=*8|1Y{+uXEPNj@3R;#X8q~guh5* zvzh%=6&UW|aWyc+4X8``4CTjS^2a)cR5V*Y<*w6|nR*=PLRHK(7Oln2VdifRvy<6s z9Pqpz9eEI|$5?maktPeBs;8i`)Sa%pfPPf0mvTo?QInKnhQne-X2o__WAF`IV}AjQ zj@2;wq8R=={<8F~wgCpbYx;szgewn(#fGZvL z?P5`7JQId{nh#UraZs^W5!5;x^3+7)owY@zEJ~PF>}JOZDh9jgVFKRIX!s$!3bH4d z3{dJFVwh@|-QEyMTCm9$hcKLtS2XHiE>0Hm_@_1))zIEV+zkwwD}-w_lH&qEUIO1L zgvsY<5&zz7ku!8G4(o+1B(-$16yEp>CD;cn!VHZJ2j^c-$ z82E#LU;Tb6J*yBOOlVY3flmh6_?x*2Od?M=VxuB`yD&KT#EYjmDbfwb7{~^uxjA*| z!i2X;_-9k6(P$o>;cAllB4XC&7zDn#i8LIW*ek%X%#bCyg)zk@guQt&nBNXyOy3M~ z$;McF(8%P-&~sY}7qc*nOZ0qe7{SCdX4(aU{vw&UIT>&vX3RHb##go$L$FX8IcFt_?wLrDAZQ5#8iU_6LPpbd%g3GlEpR0?aEMZaK!W z3^0=b+$fdJ1c3q&nruJJIjp|Zm31$L%Q-IlD8>?)r&>U2SlO3&c_j*6V`hQgjcwtr zBsm^HD`vy1Yx_!g_Yzwj?8js{;`per6*G7LA#>O}QZwdS8;=+-#tcS)Iw_tTuW$Ux z?1j*^Oj3ss4Rxk0M!7>mYY!J>(Ek(e$;s|qC&HLtuk<`74UEiGTIwx)NE(lHxVYPXZS!v(ywm^t`wzWQpuVB|5OINz}) zgDwr`k-xc`+`labb= ztjRuSFhPbuXkcE26IE;0`UX@6AAD7hM^7;A)$<2biKa^x8TvkCn_Sxzo;((sBFSu! z8j{suiGu6n30@vG=xZ zaU4nB?|XlWvgX`2V1am+$76f17PKVRJT2W=_WJTEH_)IS0nN}LiShXU?C1B7$gIlh zu5OTw<=L}hb}gu?%#4hTjEs!O+#NrsrYp`3PAcUtplxNR^SP0^1*G=QQz&YN*zUg3 z4kRG=9RE^F6P&C)$X_^X47)zJd1c|hZpc#AFAs;a)^zATD z>m5!aRO4?rhS3U(TidpolZs;Gl~^SOCSaO2hzhd=6oDl^>;@qra0i z^I_JVJDG$3q}^OwO0y0aG`W$fSBQeYO_vka-D#n6CFJH}{jrU98CkgvXJb;$taOO5 z6{&c>Jk&%Xb)o$9=AoUxh2)T#(L3P}yzxiL;uMfpnS+)fB(rgNE5l|p{@!HW6872R zr<97a;@Rjgj>9%k1<|f^&}T*MXx4qv_?SC*i%}Eq+qs0tcY=hMY8n&q74dDhe>@xH zRc;@s>0WmZB;AHpyXyB(5qn@k5$lIC`OGG0ars=*mZ$Ly>?*Lw{B3VYBmA~kWuRo~ zP$H}#GW2nIE`N1|#;$4;@dC9g=qumWSt=?ic`IK7LeQxMz8O?+{xDpYBNT2}7}leQ zr{xNBIubt}?l$4zF(Swg2y`|CYGc74H8*&=@5?&!6 z;VPQ_;n0_LHaBV9RlcOUcr}*F%$`+0i}`AY29J-2KCU&E1QSa2Qz>|i@&nNne-C|@ zdgE8;pPD`b|BXQ7~3&u;7Kq35z^Rz{XPPR(0gLSN}9aG@vmgwr4u9E|T0Ijpne!5cX^ zUOwRHf>@fYH&3LpNX3=+zE#sl z#irOToK`_BcXV7c5`geSlbwt1`5KxxnQsaU4AR=US0Rw`$BD^JFy$M_g0$2!#;B(-`(N!3@7mFo zU`UTa`mf><9r1cmZ@&)8uBgV=7_kLI)mZH_e-=*Cq3@&Cs=Bk{ z)0#F?1PwEBdluJ{EZ}C6i`qY_u27EX!Vr8U%|KWb{4!a)kahjg90KyNL)MF){kp2#}agJiO; zKKDM5h5`v06?xk4!7z~7Fc`gv!A8BF?b6r_&Vy~Y4VgAud~!N0$U~Olu*23D3?ZmZ z{hJ0$IznBNVm}D?eUtDk8%wdki4-`rHgkzQIGD3bNe}}^uRz&I7GP)@zbvhll%4OI zH${64erfU+m!SuKwRlvY*X!_s*7XoJ1ty|KQOL5X(@mL0BTWkY z4iJ#hoK8x@p+e+bda<9ABa%v`E;r+4YGg<_xxuGuA)O_CgT%^rk%yjGRtkjXob z;5Xd~)R9uUSWKLCa3HOMGX5)-JXhz3=pSC05+~mnY;liq>Or%_?hp$i5gz9#{cK#p z-pEtl=T^k7_5EyWS&6uXhTHZW*9pl$=(~f{>UyKy7><@P2iAZJun@e#(}0c?PaZl` zqQe1=(>q0vZ^Ha|6eeoP z!vDV>VWk`*OfrE_@bA$n6iBLi)tFI6o(ig~>c644CC-DLB}}Z~TDGb)N<5NN0riZt zkGcp%hLWG2Tuz<0@P^LnZGlq}BNOoXY@^P&Vc?k#aAD%8G z3Nc9Lf=Pb8+x_p}nD@^ej*6wXGOj^{`A zuYZk#FcmvccCOe$slmWuB{K2gpNo?t-k70DlMdf*e}J7wDIjm1C7MhH6OT+W=^_H@ zS@??FTT|Awc793Y$`q|d-TdAu^3vxRGdA8jLwQlu&#~yDoMHGlS;q0RESgv57<#Cg zpn;!g|-t=Q=%oxVwvq=R)5@`K$1s&fAiBK9#VMj%IH^`z)K{+v)6U zQgmBeOBot})l+>JyGb_HFbI`3@t)XY>RbP)54KrxU5I<$!-~J}DEr6}q9uO#CfpQ` ze57iT!dZH$!&fovF)oFhVEvu4i2M6=;q20R<wddiNM25!)m96U7FrJi=43x(vF5yAz~EMAYKMXJ~l|MyBEfU`QN? zh`G5r!50=I;nn-+B=>)HUqc#y535w`IFD2fa9=eq7s-) z43F{KOA5GCxLMp<3PwHVdhD975+%XqJfeYhu3HZb3j_;*gd2Bh! zAWk}u7z4iHp?(Ua3V%6y1X~>#gj(Ud)`UZ9sXvedYApYd< z4(UJAzi~)=Gt5<$hNCJaY0-wJXaKSgOxke~P&tfkV_01Rv^XXPX*@|n{;!RfX@ElF zl#HT0*GOISndG4;k_JUocxFA9w+lXu#pBcgz2723*%#_wAWvtXMlIKW#3w~Q>u_-z zny|M+mMUbc0B3TO;a6V)WUdHZ?PiN0yJ#lR^qb*p8Oz)aTv%`YsjtPIuv zK@<3~{?G5k`rVo*`oFrA-BA?;6R3MV$xQu+0RyA_U$JI{sNR?H2Uu?5|BvTa7Kie`uP!eyeXRfX4$Ju7pNfOW zEmw$nPa13A?L2)tCjEQzc2tD?P}ZEHkmA>F>YjV~ZE1-5dT@0lKcta|utPnQi_yaB zEG6%`1mK&Zw66XePC9rA%Wn|98oDEjXQ6C~Tf%(ua16w~NDgwi8ByF#F`{y5>yVY2 z2!s$lL_{AkAb3`(f7D0}ASakpN&9k5LaU_A=6q2$P&|sI3?LZQA_a*tDc%OP75*(2 z!k{s&Z-${W`O{wA26+QNIu)if_b3mzR5$G&&lWS~3MW0bgH7awoDtn=o>|~Td}S_S z9mQhBCgzQuO?eK4{v}~NjT4-v2y_`mo}d)M+~cs#xakV`Ec^z9|Bgj$bh3p-Tcg5t z<#S7%YI$emfY!;D?fvIreLi>BJ_@iY_Qp_RvB(Lo6ufEk2u-|&V598YWmmat7<|f> zO6$bD4_R-9va6@LQ80nZ{-ISy)+?K|ImZ9EJ!uDoL*bTcCt*) zzkSNj+$VCai!#st_Fl6=<>4n3eF*zId^hYzq->Y|S4m0q7bL@M7rxm>Oq9;yulcrUWgwG3XV;1%;SvNuL-${!ZP%?J0 zjNb)(MJ7dUww@)hcy~@0c0WACaE~AoOt#s7?r-fH?|sqOTQ62h{M20sh!H4O@0o`@tAX}-@kyjQ?1llPZ1Tou=+4tT6~zTE>l>1@ymzV;tEez7I?HU zzsSSI`DLCi&;NDR+-Kv_P=U*;c5hJ4>-)y$ev>1-I6>+^i)YF5kT+`07QoCSVJrVT zx%Nj$&1|UEv!iy)*F#~zx+-7fFz%Z zuNv_sQLHW(L^1kFB#6~vxncrEBuIX5!Wf0*jzlrqaF`${OAsZYc+3~uS9VYi&BS4R z!aOI5R5cr*UMQ#$o}6^=pznveE9(9*)4NrYfA1^k$A*6*21fNiW|hL%rh9K1pj-4m z^Q)^%RsH|M!-8M)81aVvUk$yRp@5?KalNRSB%T z|ML_@ge3F(2DYu|P>7J1g;O3Zp$uH3f zjBOd9mYgMvnZ7D=5R@HQx%V1w@lHPL>YLEE78xTpV6mpz{a(tcgjtAD4EvhC^B_;V zB-)!e%#6lKDyft+6{^VD)>j6z#+wf59ntg&+yuyeiMO&L?Ha0`#4K%6FD}Wj(Y?|f3Cb&A@%C@;VaTvL>EK$cz$4yg3=WPdy*S8Mk>5UqEu{! zX1WUB$>8Q;#FkW{n~`kr8f7F&7A!W|KSQ&`&rqgEd&UTxIB{ZKizyxfJvk0GsFW6N z9uxuxa=rv3BYlq$Pet`w<2?isxB!MAP@mEN7=r z1ta#PL`Z@@9Np8ogB@9YQTrUt_Q*Ww)9@Zq&*C`>Rxk%T(uENqlf@W&nKF6=Lv07U z@HKf&W^r+kh;angtdp(dPc_R;*@tBA0nB%qwl}ftI^A;izF2^ULUUyLoaeSi=@ds7 z#^-OloW%)fa9n{lCFLuW+Ga;D2qND&3VEMotW&%`n${xED7bXF%~7qjwME6GZV8~z zy69dp;s#RYS5_-fv1Nzxlv0f5-57_6q?cdybL*glcvE!?wYa460u#<}h6j_8wMgp2 zcy4KT)DCI~#QzFycjzq{HY{c^3t*6cL>3s++d^`l80?OV9A(*D+g8n8D?zPETPDAOO1}n(Z8hN77|Gu1n^%%xrIn>o&QW=);OnGT zz|%Yryb0yIf><5O$de=+f$GPuX5n{7X3*-{BkA-m zYKUP0*93$AF{17L2uS}w?`2f}?_XH?3d`VlKNo@9a{j>D9^q zJG*N;>&<+>O||tg7lGkV{vEVHfpB|A15*G+37Hu69OtIZ++jvKUWWO(CvvvY%3o}> zpKol3xA|UU`&o0!yNYpGl3b>yiih9EV66BR9zjEcOe+{YiE?6?-BxpdeFsx%tGySy zO8mw7;>qR?KIgmpt>*!l_*v3Z2?c&qt!8_FH-EaZEkE+15sS9Pcbey|AFXXR8{7N4 z31H6~t-MVNfM&bsvYgmdpaXp*>v{1B)1NdW`sZwM{3*VcG?KBN!YQGeeyJcI9Z3KW ztthn)on+10&AlzA-QL-ofU3!`wbpian{K}L4zYYzezwxDC7rc{s`n>Gl!5WL5+MSQ|-Ha zmv4&}!QI1k0uoKujibk@TxL zC2cjnBaa{}s?|*QMCC=k-hA5F-$Z}G&-~l@g<`{3Kl|=Q`5~Vzj`^&yP3fG5Mr^6K z(kG?b+uG(%Yk#k)G4pP~iGz^1(w%*)r zKVOI+=`*3@^Os&#=xljPyYA&5&H{JZ;}6(7MEiF zw`l)eS^X&g7wG##f2ouIrKYa0{T|+ovWpL=OFt)dKHiPdSb$d~-pQ9i@edP6*Mi{^ z*&%NmV~fE}mdsYq$!PV-U$T%o_Q(D;0WC;$U6i&6Y!qVIX>I&4OHG$pX3e~6V}fXE zzv7MfX=IK%`MgGsvV>Cojm*b{`=BV|+{2Sb3cCUN;6oI`C#4z{wG!3hx=6axiYnRk zP4daFjt|{DEXGNAAP+jcAqjs`2lxe81RTA#_vVTTC3TC;WiG&Qa)*sX99eXVqYT}{N zM>Otaq?9CQxXbw=W4NULu2s%v9A&j7GDxQg9UxsJX`jp9OdY3?F<8$*HaT!9x1u5g z)WWj}nNT=KxuS8QS1^vfRG^P?wHFn~iXb8@U=@+=Zr^mgscT17FRJp4n*SqI-LLh_ zODi(yV7sDFk1ixA#L2%>3Uyq0otVyV2qDWXwJRC1vjL$*LIkRBD+;G<<~Zn5e=#DX zKtTnP8Qh5nSCkSsrI14baa^|qkDXI#GBHgM2XmfH%`S*4N%>zEdI}0o7B@m1@+8G% z==e0N_#ZCJrWBPeLp2RM=Qp92g>|j_PZwI9J1ij9qA8?UYZex3+bUCu>_tz$541vr zTKU#E=$`bf&Rx+ia>UR|JjZnCtraORymz7;PzXf7Y zIZ+GG$`>aNFR6=9Cr_I z4iPhC+$x;IWa7LY$fK|M_R#YFo6)n@h?sh_jkfbTPdjd@yQRGj!(>Rg7!Ph~60MyA z(+V`ynK@-lG|efz-_tm1ve8=r0guf)Y{+kOM3L%5k;z`^%EUJ+C!8xf{fGmZlVGQ1 z6%Hx0aBzsQfgojvM>LUVGxj|_#B=NkXCQ;L*hnyHBoY43d3+#Yj)6z~4Lv*Ry*!N$h9 zF6}QT$!@3$CNRRLKnv4Nqb@@}!jhNhgp zTP(2=rd+&TDzOnaWrT65#Kzjj-rD}w)6M1&{&K0r#%A+ryY+nIX~fA=iH*IDXV24j zOC>heclI&KmSnJ0VnY;FfX8r4B{ra_^i8%@Vnbb}@0Lnz7)2%TmP%|uG+M9X<#LG) zqp0FVmMxdq5FI9OvgHyRC8GR;jD6JmNJVxEg@sKPPA@a%m zr9*M#$^p933T_^oQ`Cwmf2Q>3*I6-SS%G4swY?*s2Kk1hjjRQ3JGD)kJ5Q_q+ep>~ zAB^G@Pe*z2e538LuW8GXCPT*aat77k)CVJ#=XllCqxy0AS7=rq^6%8lC4h8NPJWvk z?Q~A~Jvns4^2fW)EmElyu(=ErMQymslKx^j8{C{7AaRo?moNs8MEeY5(%` zw00sYNYwRSG(qasffu~lwF=w038xl?`_s>VlVs=8ss*--!jgD5Zl;#e)4s@e&bw>`nU0)wcqsh*+Ab}VU39&6DDB2mY>HqcTtYYo>j@o5Jl(TBlIV2NAG_!eNnCQ z9&doR-2WagF30$y3e<9}h z;>JFjds%4VFm`&KjPZ@I=Fvc50cl~c8lD#mYfHs-KF}l*-lh25qma_TGY>Rc?R?F* zuP3dK69J}dhW`wgGf-JtvBmg2G`>`^HZJxml9E(Jh#4aN>)5x=Kcg50JxMF@#LQea-7Nt%`lxR*uwAxc*kSL-)QnxEb7VeaehpG zO>#(Ceuz)5AT=5P16aQfX+p252Xz+Ir=^1SI{(LY=hWVqHsh#&H9P3ygJ((|z3WjY zoC4&u&8AK}|GY8Nxb>|g5-o9U(R8Vfql<3mqKH(v>RU_p2In{X;i&u8{TsSx+8u}e zocg8I@79m{_AfBpDhDVYvT1Al1g-v!9sL!jV2izxei|z@Lc$?c-3d-)9}oelo*^s! zlVZW70WZMAw6F=ge1KbqX{v@l7dk?@p76Onsp}aFZ~U51EqtpmKG*hr*gZX^k-DdW z{+j%~wBqIW`sTfE@gL{eml{GUEhlhs#Jm%wB16n5X)0?;e zLP*_@l5X{>9nm9uSd3^?dRk%?SwIO1mtE4ViYa-*)!RD6UEtdCm9{Uh@d~-ZzW~UP z_X@zw3m`0LQIzXChdeC4FH|2gWQLGgTni%cxZqegi)%`3=fQvgI-&=!a|(%Q+LJru zBh&S9V;QQioxgfh?VHsoA&e!GCiQ5u7B9G_jPvE-N?1Pc+p=nZ*)vRBdKk7@ z360fJiu_bZ!afSit?m^miLW}rV6;!_;ojAa?J#o36^WczAN zEwQSWYby*f?dJIaX4O!eO%U2`p3BvytdpY8dq<&gnpVPWGc%kKv~W(Sr}}J0UK@wi zXYjtx_32eU^eQZ656aB-yAGQnz}9pXSX0Vgi?>6b$K>NCyKKfdLw=~Ym3k-y->IwN z<2ZLt^f}r1!r>eIn9?(t@$LQ)(Z6HCQyT+3h`Wr&E74gOms?EG}Bb)TQ zH`|6kynCzvBs=4~w|WXMd-qn7+)m%U8Fs&FqjbR{Ik>;1!IYaI0twFQO*QY)$y0etV#U`Wrp=OuH9=ws^-LiI~AgYt1zDD`(?p=tzE zz4tBS>C3CEx z9@Rg$)&E#5^S@F5eQ8DZ|E1-R_1`}TZPw|3Fuo|OZ@v1bcsZ(1foa9M?_vq?+L*Mb zjsb*=^&emN{n4YpRDX2NMYl1h_Oi0ANvcyb{up8L^i;XORl|C^C=?ad1>vL6RmJaW zC<9J3|7;7fiiB&CirZxVnt^b@_E85vmEV))ML4y?p?L59PfVveKl@9Qw6i-T!kIi# zT@_-tzQa7=k!tj0sPN`^nUqgYI_#zmsSx0gJ=k6~$qPUT}&G0Wz!?DTgW# zB5o%GAHIC%r5M2-!3$z_`Nd7K(2P=ri|}4jr1(%n5M?ve{*Fisql|unkdr6hF@Pq| z;-DWf&4{Wh#5cQW2^s|=6_vXU0M8S|M?ICK;8MjwXsD; ziJfst0F&1`*JFU)lYLz2sBy5@EW_&_STxy7f|Af96_H=)<;stPP31uh^ir46&N5XF zWf?Whi=cZ{K9;8iPU@JkV?bYw3oNO)$Se`eKjN%#M3`Mc=;h$?i0Pst3tV58q!Ny1 zV>jPwZc))^{-Ky#6m!r?ZP~V=o}<|D@nw&yhpy%x?ZMOQ(g`(LtcUad`Ruc`s9iU9 zJKBm5zR4)p;XfQ9WeZc$Do~5le%A3lwv&7pmz8g_r7-kRC%5;owzI$8=JXVR5r;Wr zUgVadZN$uWaf9!-r(|)1p)!#aDAm&WW4cr5Pmv&z7g?Z$Y- zXnfCOwsB+xpemF4#mPi%)|*fEpXK}8Do|lq+x?Nn%Pq^vlONkgsl#7C-D@`8mnQSC z*BNo#$GeU7_28H39}~&-1TUTDwznu|W{}hXCl^nvz&Q%HJVvc@W_f?sj2#25hH7s7 zcXO}WXPf8BE>N_KCeOG~T8DCl;?qZ>drO;B^UjV?R8-cq=(skgWt5C#zhs?5S9MVl zjiFmY7ge*&!qF6Br%*$Rm7FU1ydn(;-4n?nA-~_L%IcZ!y~s8h<(2_%7zj8Q_$#~d z2q8w2jCY9L2w&dbnQh3+F3N2eWX^hd=YUO0q~H;Df&M85g6aboQATFt9am;UUqi*V_gYrM%L9=%= z&>mMZNeKRNFrw;ycFr@IUY@kpA7;ta z{m2{GjAepdTKvi`i2d&1{BvdBGT)le8@%+?YLy0Te3kvgPe(kon2jyz~RRKP-vsm#t&Zpa@Cl2r9)N4{F2Pa#Q`5F zHu*}2X(B&rNliEmhkR^dOI`ich`;Vz-xVdL2*V)sQR`;#{m^g%gc;QJG8NusDlw?e z=;gn*e;DduLVJHzG*|nhzn@%Os0Rvok75P%R)1)rMN&r74z_URSCFVAQ!9Q380YEA zM5w~ycVn9(SsUy5({&{iz-=?0h=COHI@MSB##JU^rdZW1&4EeAXoT={@RUuRe-!g|Rap8>vAl6X<;bHAVVc7syagfLqzyDM(Yvz*u(xa@Hnk5U=*0m!D#{kC;@{W-Uvb>$0Cyqzj}zwKqvz54pYuq+ zUoA`;K1Dw zK=+zYT?1)H9S@>6Y9e1?5bM0cc$P>`I?O1Vah1d8kg1eKDazgDsL?LTXxXQJyd2@s zRnefjJQ&;ZGHh*m7E5`A%WV9u0N@VL*_GOP>ySe~v=WP~?Kfqpa{$gE#96p)U(};vjtMS@3IJ$fmf|7NtX` zU8V$>79Q1*!XBj)REUMsseOsgkzyl$+e}{B3!clUoH(3I=bX?(Hls2O>Z?G8Xs%`j zqZF9uhZm>U1N}d>*TbrZBLk7Ja$;0B*&kz49OeJ+#qmD8WL%rc{C{z2@o~)mU&MdS z&wteajYH?XK4({K_D)`2z3J+|mcftk0rr>|uu&mDs^XpZ{%L_cWbu)GzBf4Idj9B< z#6&hQ;#g0q9;LGn@ji+;AbUGfI?A3G-GlF};L3~k)33hTl$VgUqFTSay1E#A_2|)? z?!haZ&ia=pkI?j=JX$3M%p*}E41;=3v;O{3@Lwls(INT(?aSafp}u&E5RZbW=18;D z`t|zB`0CF)!UTLHWEr%0jsD@`^+k{94PA^6Zln+Z&7OcUWM}+m_p%S4!GwaR`9Dcp zNGIxNhZqOrk5_N{o?_Irn8g0jkka%$xV{!gXN&hroges@7_+@BF5Bu&qkN@b<`3rQ zzv7CeCFvf8CktPNjF0~JLh^-?5R6iB-70MrOfuoaqT%?@^kw_z0;o^RX&P}EJktMo z!Ll=MGn^0h$MmoBoR$F*_`DS8k&^S|w0}TnMV`jcj}Fq_VcA`59|yl4U~8$4nTv#{ zK0Cul1Sqp?ZqBY91x)$A;mNDGzCz$S*n%!>P_R zXlfeSM}9ocy|nQ}67?N=N?Vgt4!|@E-M7+)Lv*+pF_ON!pcWb{ zK_=JA91=?f78`^5dh*N) zT+UEn3s7qWFz}7=TC|P?Pf%y@5g*sPZ&@K)D88+KC)}D5!Y=XfFBa?yAWM**qR*U? zyrqSh(S&mGlIc%e2^`j1xY8*{%s=#7wj(N2RgW5WdMY zh66CijlkGeSP*Tg%8H}}@-C~uZDQ(EEzaaM`0Q-<+t=gpKOG!rBni`1NhU^DB3Qlo zEv%B0TxFdm%+0^P`|O+~e8k^1x5y%W&!HE04cLWC*7} z3|%&gIub0FyK`l=`lwQ;{%ZioC1}vSnmT(ZKPpA@^^d2RmT%sQ7Z!bL`W}+*426xi z-ZvE?1tf{*dla^e?G<*Dn-yZdV}wNQA?7|CDW|S90d+LBxtx}rnxa#N%Gpi+6nFF3Ny4^)YiNjKwZ|*29 zyoO1|uAHcimB#sacMJg=DZC%eON?WCF53(9w-or={*~`>NdV4yzvRB@$uTTRtieRh z@xo?-lBflvigTLjzE*A^HRrK07VlYiW}6&knsH%^TRB{OOkl70yT4l`rw;f8rfp&I zrVgkVA@_R%88WvLy?3t)xun2FEEvGcLLLF7fp|2@-WeNNHtExw3XUA0fR$XMzAH4; zOuOVv!76XC2DX6l@1lo+Zv<8WGa1|ptm6p4>M$@{>y9NW_D6Mm^cK;NkfoGqzu?ss zG#II*Sn!RSQcErC3nT>p+v~hB38_?}XxF^yUmnfsSUgJg0;U`|!b>NdeHR$rrC@=+ zH6O`_l37pL;oPFAyuoYUp`(;HQ_vjPo{v|qvV|{ps>O){bDQ1c2MJ9D+yxO)0Cw4x zB{u4OzOq$Bn~iIO^e~CW!DM4`8j^;Hq;NVQB@YK_sFR`X=>tG}fi;1Cc2li}Lh1`I z*P`A`5yIDifp?jmcjR-cy$DR6U9yy6e$Z_rQyfxIj*v}*?L$74bdSQfu`RJ(f`D$? zFWfV!e$-b574~pPJHU^a*+J)6?kfaOUHZ0J`{Ij*g{=FJYt#;eUDep1WaQ$)`>NM;)gJF&86=Ayp8Ek9|q8ak}{$c+wws zZ%tl!Vk{>Iq0u1W;rYXm=%fTkT@12cj?aJbHFGDoUnId%=SiIP7d|$1--Xidi$@wG z)f(w{HI0mTuaM~`W6Kqw8^=DbqgayQRKW0bIO}mm^|WUmpT{Xz9`?n`nB+3t7t1ph z7H4c{rZQo0YFmlHYeK_!MwYV{;o8}874C%Wu=R(yR(cZ_#$i>KX;WK&SV=d^t8`0t zMTPzw2sHt$N*?MT$s7{!#dj4{gLaC28qB`AByY`SHnsO++E1TSqd&O5Akz|hd3ROe zl1QeSs5%Pm5l6IL;1?Y5x`SGGPC?>MI%4hFEN3zX^l zDwxdIIZ}Qoo$Ra^6FKj`iQpIdFB7ocazGnX4kv#C&2u3W>g7-&W{o3I>=*g1qt z(?J|s({j^Xg><}h9ByoJ@+O~016kyrH=*^1G(K3Or?BoDMASP!AfSeM#n?kjZVzxoQ<_~12?Hwu;7%p=n zjjG``fq@EfqANOw(O()b(J$wy1=O!bAoS{?IpYCtqDG2ZN>&JWO}9^~uYl0X7+zIP zqhL!yK*vT|p7vvea+wyS8-N$@y|BBJ3nqI~)Z1R2>dkfMP%Ap8(4*<}pf4`dp?SWy z$tNaGdVjceys--jLPeq%7;TpbqN6P}=NIA^6`)F1bvs<|3D&JpwP3WT9WJ<-hp()l>>>n37NkDu%81KXT(oZL zm-5>)GEi|~bw7vaT?e(YebzrR6;#n_`k}hj(Zki=&Qw2?v@dM1qD=_z?NF)x42`Bt zUr5s7Gr_LPAD5>$dSfzpxF(+$AC-UxzS=p#L>`pl>d87~_oz7lZP;E?*98!=eT%H! zbmb7*VC)^l@m^uDU+>AqUe{_8^j&eZL*5eY?K*%V6^@^+HCiShM^w{yWb4W$UHHIi zH8_KGw;o*3hcIUHTN_w?G3r-?qB4fgiURp#W9KQ>jy4C&1CV=~<)^9dk{K%!U$xKtPBlZRQ!TL7Ki90s!1q zwy15Cc*QEz#@4YZ4gGdXSd3JIh1$^G7d|tddM>~_!p7+=VHb*%dz(%Gp0=OKBD@5k>y zqJW#m>hf}?JaCU!SM1;UMgLp3kj43B)qhL#zpOl7{h0qPqVEs=x6WB|jXFAGg1N)1 zQRjL5F>#~ooDv9V{?ci_k93Czb@v~h^$iY(ea_`1QM7a_r18}glxpTY{!5Tt?4&A$ zmiQf3aN?Iy-kFZolqx3Xmd%H?S9LrYp3XA=Ek@3}(sCbj7{)hVX|Y>SyvQ7<&B0 z{Iq;hF)8xeW7f3-u^U4Ui;yd@ef>paqdo2O3=sxUZcdrbbe$?#FNl@&31H9@Ueq0m zwUX(OeY~7oTi}W zJNwrqjQ9!}Rtd!PkH3)J+>8{K%)^mj3iMXo%C2qOy^eB{6iPI5QjpAS+d24arD(wS zOF0Dyl|-{CBd>G=Z~=6pzqogQNFx#YH5;*iRpY~pv#olvkx_qDbSlhf zTDIe20`7yBK9Q2|tW)cLWfk~XIyny`RB04V0~h`a<9udgjW7l?oow#(HVOP^H$dL#OVbO(>H$Xs3aRs5%9&?DVf z7=5nhuAPYt-B3p7!(dh0t@}{*O$#=eUi)qfbuGAT{!zE{VGjND$-HAC=uea!687k_5x>`al$pQE*Vc!Pdg z*VdGSmHHGrGM*^tvLz~~SR$3I&ZM6iprDD$89B4y51l^fUx?%0;VL~G!(YMFzu|p) zbpOl2@ZPR|Mtzg_|MJr6^8Bd&_hbFnTXydKysq2-Ydh=XZf5b-pSk0a)o_dH;RZXP zTtReXL4k*V9jZdeH*92!V;&%Wy~Ku!YpkxZ!5ogPioxq5WEfV754qsU#(LPjB*=*h zmKJkr>f)ujg+(s1<%L6IpSBhs14Bc{l*kjrgI9(Uhn^D3zwd$u(q4JEnE+H237 zd#&tCB;1*7x4E0GQdK^iQ7S%_Wsg}MWcK3*hcA5fuA@TuWh=Y)vz&^&yNT7#uNrCol|?_V)lGMD@1j~v$* zH=ib$BrvdvuC=|l(QHlGET-|I^=L)^^HKN<5uM!k>rHZv>w;SESgntqKx_Tu<2AXA zf!N|!raQ09z6-0m)Af(8PrFKFH;Ak37rEjK1=~ZflRkmyyKjR}#U??a&nyALn$}Ba zS)jv>?ahttX1Xa6Qn(XL``fz>qDWiK|JrX7^B;Eq2)=51es3sMsSQulJ0eXr z)Ea2n>abSr>AeARem=Ub2o;f){&ExW1j2u@SZ9#qPJ(!T5;MIsT4-rA>7I6kh+JF9$3l>YC5 z3&Kc%3g~mc5UwTSg>Y3}YjE4s$6pksq^pa&|x9>#e<%nb$KPZY{RnQj5#*B2cU$Cs)mZ{sOE$t#1a z7fbr&P%EXRT3O+Fi}vXeOJQdOsUCEh(2$2LTz7DRX@c99dLT%LAm%XJIiBcY`(+=$ zXT8$wLY%SVrhk2jw>b^eTZra!KbAO#I*54q95>`cd{ZqYq~i%VR2ZhO8s?-o$Oy%J zV5lbBwk|ztP7s$}wB+mQ;UKZYXz*cZpHHxq*eKqa*5^SAgau+K$GKcl8N)?yOR=R$ z4`}2vKq)MNUfq%|`)BGyvy8VdCKKTHf#e(VpqQ#b?~CapaRhfL>!V({SP*5TOBdmx zMSmg-#7SGVPV71^6%O1XqD39N;g&=+fM(5?j4KH66pfgZ#Y6Wt9p|0wfkE~V>M++^ zb@0Hj;;Rh`uv%TSNpF!)zFkDxfCdPH7Gx*$d=CbRlH&m|C}m0e zX9rZ^RuGzB^EcTPmk*VV%E+_+OZnu85zW6{9(}2*RsQnJ`SSL8O)c{isvPNBekmJ{^Pl`XHWnPI8fIO6 zEkjiJ8fPapNHnB|5I3UrqQ{fMS1*+X;t6Xq$jw!G%05(9AHf#Bn9sicChN)C+snR& zt@`)Rnr+hB{CBh3#NeJG(%hV){7dvMs7t826+ZROgMXT2$Yhe!Q?&i5_cIWT4J!|y z<1RR1mO&g9LpJOgP5hfo-KPkKri89(1I^|e4?l1sm6f#Jb{fLeezg_hNIK)BvcMGd zI$EUin2UAThtD|i_u*&%U^AD`Wo}kLERJt+7;QW|=|+apQ}522^)3e}XU@Cl{oyh6 z)G`T|y_)|C!Euv%XU>HKUexg05mk1B^^0jcaE~^%;Q=|06(?kSx32d zXI>-iYf@MQ5+_l603y&@z#c( z7QfU$!XRImQmMo#j7of}`U1A53bcFOPC`1$A~0B!C{nm#MkGo@w^lYxm$!?vC!Do^5!;l6$`R7F)K!5xOxRMf z8?7nl(gUEk(_CT2k_I0TYxPQp0S6KPCmpBaI;5CXE>uabor50nSU2MSe&lsHJ&2!w zsl&z^+Hpm`g&Q5AYzfX92}l&-una8+3cesVrZ;$H*+WCTMHEMacG=E*JC2Y%5L{N}Rk z+#$U@M?~k2VrTFfGUX9!XQW*yVF3CLU#bf!-}OXsL?_n@ON`=<<&hz`7_Nv4`a1zu zM0yTb!@t$bL7AyOtW=LmS4sLP>*#op3o}ra9=*mjf^B(t1yWh&F1zQ{^NrHSl(K_6 zBOwifJjIz$%ujs6hPytP1N)DHS5@I%MV*{Si>r$(U(QX(^e@_mf#>GmE7%X@O@)=O z;l--59lVcH`^8Mr2d==kGG|DTX@JpjK*5ZmLtDGZ{Gn~3h1P)*jRVFy z*uNzr(nXY`OylT?`@&#PsYoj$%|P|C77K**qilvibuKVq$>7*Q80KJLr>gkS1h6<) zER%x4l!7D=A(P?&ysi7KdGMrTdLe8azqpFI>{OhZUf~)GM65!<&GzK#r9akWiI$5; zZ~S(2NM3Sf&pI~ zPRVjA!B}|b#i?DHvb);>ORYi)Xe1m;!GcXm)MCBvv@}|}8p!UjMy||G04YaKuq5d2 z3dlRdv4)VB0>GbQ;O|MP;(zBzwK`h@ORU=;(K4D0eEPCN8JGIAW-@m}guv({{i6OT zsYCg{xg}4AY%SU`Cxdpk<*0*S@}0vr8IY=RHi3p{@!x?NXES$%;i+3TlEKMVFoucU zbzZVDBpqH-@DSJj`c5Zf$jt#sUlw&&i<`+Ep?(N9fxC7l3HB*>F;%!ot_2b~g6c{~ zZ$A`uwA&&w`w?*`Qa1jxE0>Vb6TPM635RyB(|HeI^m{zp4*SeP{q=G#ObEW!=b$5TW{87IZQctU`j2YqF*)lp_SH+$h~X zm82xkm6lDlrw+l|RR2O8n}l9E(WEK;RTm>^%B;%Uz^e=kqXU9PF9f^4_{m%TJU6KL z3(cvcFTSAQLdaV^h5u0$T5MDTG5?rRXJ_k2-Ptv-C;YLJ8Nc6L3!{gpbs9HMSzpmZ z1{B{3wXj9vqlM;)7);7O*<|k2w{L=X?;QzjdwohnJcOvvNiysqIHOb|g{^;45RO8N zRZ#T~!#1fTjFZvt!I?BbnD0oa_>?DvkTc9Kp?6r?{hi%%fZKV^uh9S2ZoV5>qF= zb@28KD;f|QhW>f(g5++_@qP*zvkgW+Wv{=zGeqMXl)c8;f0KoQ(TczCkM~+f6Fa+Y z>ipp0wY#&|Zm#2Lpc5mUQn&898U$in^TeIqqr6I;;~0}bXzuOFH)Z?xk8k0x_yoVh z;LkT&?Wv&l3DSTg%(#8IB16)_uL%igVI<8QT;nZ#h0RG+R!SeC_w3tmvW4tFC_pBW zB%9@ruW(ZS+fYYECsW}!nyEkSU%_ey!D;##c^JC%X2ac1CVa7upS+<_cscFg0(f=g zvnM~w=J)slm%L6r-AXx^sP(;=_XJid9d@NUfD_c@GRyJGeNGnp>sBAdppWiOdjea;r0vr9XP6!zNs$K}X^}A7VX0*wud|>hqp4}s}AN_Qq zC^6c>_+x(bTd{|sgL?F%35RX{#}m%M(FV6L$@&K4SX%wpH9Tna!|}T<`PRai?FOU_ zVZt1@B{tGNmm_63E!CX5Fe*_SD%MBJ7${Zi(&D4l)f zlhG1;8Roe%4cZmq!WiZHL^a5dh>M?RnZq(xj_airk(zON!Uv6uqWVK=3P4y6uRc}o zg`3!GFzu<+zblpO10^2BCn*^1<L&@Ajl?%q3Vm&FhDv3fLa$ z9>w<2HU3!#W_V%@nfn+(nd$Kn;K79@-UI^z@2oFFMerxXc&XwH(dI_3>61BZg58&w zmT{nSdH50?#^BLkSN`g@T2-}>i5?sDkS~k0VV`KJsT#MmXtn7quwv#SNv_AjM-L9i z&lGPz<7rWmN?)K3sCOL+-LEc>cE7qz5XZfA&jqX6edPMt%=`Brqewo{rpT`sR?{IO zN`gbp%tg)oV}8QWK^4QXgZPmU;m!yF%Z_4j9b#t!mL*z*L9N9I*43df8%C`nJ{>ip z|6WEd5$@v-tw{V8<0&j|#sAN=~yw;aVySjvM9`Nl3-hl)%91aY@xA z#R9=jfqzv!zh*u`OFJL}po3Eir5Y=4VcHWC>1HrUU7wPJ^H)>5>Jd{x#Jl)u{%4BF ztu8q}y;j*&e7YPPX#FmbeG8?nx1Q2JUw@sgPG|q32xjbyZKVcY3nUE9SOxL|C0u;5 zP(y#f7It@>5l^i6B)(AW@HoFPhqy-iQ@fT#a12Gqf*?#sP$GV=R;DA*voF5j+tpqv)qVMjMXXGDXr>hmTo;2$uouK% zBaA13A28yQCg^E;YL>62Eki1-TbY56vD4uH(Iw$*56Tnuk@SWI=X^A(rz)4h=Ld6lpSrKWeY1-Q$l|QvIy>q3yWWhW=6gQf1s`Re>A} zyC9>`pIBbV!xXxk^UAnqI^Po>M{#R;@KhlSWk|fbLHT42G{w7|+rW9II^#C%K- zhR1wh0#id<`r2Ih77&}1r`%*ayodu5B<2)A_(VyYBGo9~+E(cyy0|-|= zg#~K?uHGpBKQC3Qk@X8=6W+Eek;Nu+H>(FP`&W{{9=I$OmW}o*KjUuJco87jT6ss@ zkk3eDLptvc>0r!7S?MiVGKpLT=(Y*wvMq^ViVu@_oVmUIQ$@#Z0exB}nSkX!1;OJj zva#OSZEujg>P2I3YkxOK$GhK5>Ld@pe?qwA&eNxSAwUvLuPly7H7S(~kXZv|70yOfy-H@aRhLt3U?tE6EM2Ln2ddHnZd36!jfF{-WB6<}cu|dnHP}Irr`B4dmrk__&>8^`na8<&BdFcO7P`D-TuLdZ5vE zYX^5ub?9X^sL3W@!`?amsbI}ZZ2-9|B;>xr*=jEKJ_AV|inB&EKc`Ik2xY7hVzj>V zVmr{rN?9tqAdcdbNFF$_1J7E=Joe11ToBm}ZFtYJVcd}so7_u;Stdp0JpOysRd^1` zJc&3DqEQ|=_ZkopxxXRf*oKO92iU&NMnN))c4PR1HS&tG72=ICUfXav#A)$F=+r$2 zXKL1kENX`YVjba*;xzUE?mi@{7kAC0!9R(OT@l;8v@+UugxKxH$igo$_IDtS zc$9~e0*-SQSCfQOI}BP6d4bTBLEOQ&V7OhNjQ0gQF2#EQ zUJz$O36lH9{uH1WEq^EQVI|F6TP2ssX@=H6oOmN75crZKQ6_G8!sxO8jVYXU2Cv)$ zQZGa34cks8t!s8U8xZzc`cnJZWnu@{kGS(a=6s{IvzBDS$Ny$M}%rTaCKkuKnKRX6v>={$2-LNMU%7t8I_bwGLp)tV-WNKNnC zJJ>Y*1Jaf#-GD893Dm#Bve@GimdfK4256w_oDSzoQK~awFgH-AP4kMjKaoheF4!)9lDV*ar}`Jx87(s*tl&jJ+5mrZ;pKtey)d={FkL- z&wV~@f8^rVc|Y|$PJJN7JR-A1uJWEeB2o?r2!0#zYV6|F0}P zUJ3dCR#xVhRWsAE7Kk}ZhCb;cN(%_BMaY zGcR>*)a^Ww$W-e_u4TT9CsS@Gr^vv4#l~2M`T7%`d3%| zvusKFq~-E5qc}|kcUsDX`M+hCeUo0_O82&`$5I&C?Im`^Gt*~r+)1w^C4*+j73GxN z8+Q7cRfqsb?n~TJbZbEE$&XFvZT9WA8TJjd(r@iP_9ge50CO?(X_WWN1^9QFZh`0+ z=O0u8@ba(%(@H~UN}Y<6i^3wMFul#qCH7)&Kw3x(m`>Kvj;k2gCd_jQ({~^Mao~z; zoZbJrD3otd){tqC*no2xb)*a%+)zOA%(UxSb{KwYe#x#6v)S3wxTN^HpB9&YR^M2@ z!cR5`KiORP$tVl`S}j$6xWI|GdNuF=%U&(`|B_aN-i0l1XsH!4>!%G@bYA>YjGG<( z{ow7=Qb$8BtU^w@x?e~?%w75mCf*%}qUfXer5k>j*A4o9F55qBZ926LI;UeXy0rMN z7%h<}_{-TBklZiwvZ)!<1!OfN2>=aC?ScD2pm#P2fj}b{5H_JNH3sqn6p0FzmQp~` z%&)QV%HvoB8_-+0co^%0m>r2E*pRI*Wc)B{@%pBcRbd0jLHbQB+XSwlz+?3J;c{B59C??Ss9aK{Z7_e%CE6mi!`<-=%6jCd~w>Xdn3vZ+r^P}dgZJP>2G z#4)B_XCtNB_wjJV)i4Z(J^0hYYCz+p`3D+w&_?iCU7A}u))cgBy|3RmKG@K3G-KJcU8mw98~gIhlGe$e=tks{JS)E5e6s&6-X5UTVMr7m z@( z&inq9U#xCNsJ;PyqIwU@lSB~JMQ6{NQc}hpOE>0*7@15-q;;OXz@XQE6V76rFWWnp z#k8272K_C&#=dgYKl3bQERkc)#H?g0e*tJ2ru}eiz-*s=gN1`FKftXtlxhXAD zr4Zi+mxsEc#^XQZ?|kr2yE#@p1dH-Rg$VmWB?-7C!uS}vL}VKhdMbCUwEJ!6*pBY>+VjtYgM~db=0+&*XLpA3)1CW&i0RIC0wbSG8crn zVFl@H&-b^=BO=YvX4`$>h;%8vOP|xF&s2qet41Ssb5Ymmw z771!?k{o7H0Ih{G2&A=;$+$n-Ej>mlqPCT;{=-n@%BG{6zmAANYvwLo_pPW^me=7< zGUe>ilJ8z~%|!0T)A)>&K3vk@L8JI3$jY-IzOzxKuj0{ zKi6PG&`=Ia^qT5uS)QoRsD&KTstZ<%}V} zXnj^6-gV8WhICM-kMtu)#AL9v54{c=gxMgd>yf()>NkT*lv+27;sbejCc=L*U0%?I z!-7^qErMr=^-MhlS}GPV{Q0#>r{iYt{n)1o^mH7}kzcLYXm~tW+*>6i8D2S{0Gd7kRQ(&C~ zJv(KF8QY@!!fti(3X(X=Wdgo%G{75yJVLLfhS5`7x(OU8w+=>@%lfH;1rxYD3=C-x zEfzbkN$Oj+3C}35?0G0K3%yF&S-ZK_k}=-|k3I7|6kGP0 zu8~DrFLl!85*SL4sq#E5zJ7`@(NI431SxL(8aA3FB|po4rbjz!U3EK^R90q68}4mb z(*Lz`VCT=9jrH)PP~4Eh1~r*V7U|#(({*%`!i^*&-GBTo1AF*k%RFY{khv>^1oQ6D z{jK}7n{7_?z^8eF5rFQQ{?-S=R zng1^?FD^&>f5HFf7e4a;h`vAc-#Y%kxwHLj4Ev9-{ygqKqG>Xd6=F9L)~;;gRu0!P z8}SQ80AG%3r$PctZ>2<9*5=22yR}7%qV480wtZ`~Sui=2IxQb+&n*#R?XtwJcaJ;7 zQA+A3cmua!_^qW!)m>k@p;L0P$W@_#{uLjeD~<|vQ>%OVTERfNe<-?&I_vbN4x{== z0Ybs?8BXUW20WLK@FEz?9kqHG1U#_1N%7i;MGr{ZhR64Y{6y ztuSH6`TRc^l~qSRo8_L12?rbe>>vZCZ?Xpq4`!B^=I1}pJ_mhMgXzyT?jjt-a(|Po z@Idf^u9dH{g~b&`ogG@HbfpH@w|D6LZ<$mFp?}6Df#_==XrX?frGZEr0yO&Z@u}n zvA;=dB_$jwbdaxaJlklutmnc#9v^?%oy;tG)_EI8qO>1M`^N;=&@%7tnU3 zhKY+_U2MGZ_nNKe1S;XALO2v$nn;P;D1;iLC^t}CnT_w(&!yK2BsXq!A~ODxS)!rr zLGkez`W&K>NE;6d`>iv}O~i|Q`}*t&o5Iq%n~GElFvpMfwgr5YRRbyofTVQo91QxW zRLv=(KcG7kTb%N{Z#&Y}hdlAFm>H0QNH+WhV~H|^E}F)kM~nVw(c?N6?Xx9+wgiM5 zayAo?1JqUOV%}=VzV_-k!A(QLsJ9O_bAX6Q510T99~*&WDvf9UVuXLA5{5x~zq}Wl z-r!e5%-dD=mQgn}H5#5%!D0f+6xNb+5;%^`6QVFGsER zb7^gCZ)`V>bY+s;!s~cz38x>&S6XMg=1OE8=|&{iW4ZKh)ZK)J?q$|U`8dZnVjqXN zj)|8j>&XssN$)MYM71avv)OF#Y)=`Pa?!XNDB8$G1#3V=$A5P(`_3NKCO+LcS8W4Z z*3Ri0MbxHlIcXQVYwZV*k2Q|q@r|^fwVzzx*xdm<0R0+E&SN%Qy5p)}MB*ZMIKFcs z;t*<~YAMGJmm)f-Y`8tQK&ZAtD3caF3wsnwqtfxI2QsNt1eswZbAS`MB*iQ?;>aR$ zr&YUkjZ-&U$$*G9L#CNoKKV5^PN-^HF7HDtg-t18H%0VD3XjrKPYG?~Khada}$_s2)}J+l#O~I=zqj*YY)c zYaGdsV(^L}3vLtf)55J43l}&ryv)jOyhZG}lFU~S{*;e@dE$mN} zHa|BnHYfgPaq5BP_0BJF(WKY;YziJniVj5ILuBcP^rDXe5b0LM!qkgm@hIi?H&?8@ z>FK-qxfRu!;;2x-A=xA5H0}=N)&ifUD2a9JfKb(D(9_fd1Ve|BO1jAHSjETS9jaDR zT1ZJ`gQKkgsq%0E>T5N?^@f@1^dKP8f5EtWSB{;At@sL+4BJ)^QqW#BUNRx!!xtU1OWF2w?RYuE?q_cSW)$ij8z^iAeRX z*hwAJcDnN^qPwsZKFt0Rc@YOT^4>qL`&T{DS|_6_43V3vo*`oi=uUdjC8ik6yR+9K z4+#$)?i5}y#w3d=(s*CuI*z6Yict<09AmS8%p3r>_?+kHk_VTC)mtR!fPFeR=H>$N zP){q6(ZTd1WCZgzH}Y1Fv4U(O_5LFewt%c#;C#H(b3B(rbW&KSa-~a@Cp$ki{0=SA zQ^_A!UlHc3>nA!hW_t>i2%laEM{bOEarRlA*fQ$MY6aQZ(Q=^TqmJKWxtZeOARN#C zbkKkMv-XNzaM`F}=1V!aFRa=Ll1d@@6}bdBt;S0t<7VtLNM;MPMvOLWm~J{$y}*-h zpQzgOq=|L_?G0Y`jul(#7+3I9kc!;I|GNj+oHRg<+BJcVdL~1A+oPJR``u%lYopeZ zzP1n`dl#qD1gFg6{X3+rg7ob|-CaA>eogVIf{la2Uhfv#sJOePJA(W5_sW&=)8dMp zY9$vs(HOM{;uekSe6eTnV5lTlN)ay{8B!Z@%)Z0NctFUW(eLkysD1o_i!#u68W!WSjwiM}g}@@yuBcUWVN zKyj&@Jf=_#W#B#~iRdJ3`vRzuqocH;l?mYi`U zeoAjt09I0HybiQpB5db@6L+g&qY^I*f1(tKT)MbJ1MvPJ**Jf9I1WxpDYAHfFc{l? zS7iJVK)e?q_qi%9ySv{`7&Dnb4bt{i$8%zQB@Na5?7~+B&7D{dkD$3j$eCK0RUin0 zkA$IfisvaQ%{Ls=h%7kQ^ZWcm6P&+Uka8_s{0cuKD!9Ie<-v1scl7(Aq#X1y`ef;g zEYh><#2|iD-@Gcdn5~Ux+h{IZ-(^dMVbh-dcXMwiNeyMsxaCpP>U%sGi*L6{-XW(( zW$#w0Ipgo2xC`#=!EIcwW_l2Zv{NDA*mrJ>`SKGuv=C)km><-zix2Q)2F= zj(s@3P6@(qq`*W_;;!!*6{7}{G(aQ-Q4PtE+JlUE+)1dSjgD1eWn4?RB+zs~3^bAy zh>HnJVva78j|slE(-TF%X+0S!sBFu_%7;)F5<3;PW;Gd+z~U_?f+ZY<)Q=^qd=i0L zY?V2pka$VfEkuf~E@e}+IPT(nJZ;2V zkewHi49XW&madQ_P~VHABlm}HO%f>9E>a>#p&SsycHQouZwEdU_E5NSrs5;c5%@j0 zw?Od93>_5d6Ieyb9zhGzznJ}2yO^|?$boaG1oJ@f$|z(xhbn7AOVl4jal$E(gzCO* zTv906ma7&#?$*c??DQ(s2a|yA#K_pX86CuzQLWRdn-I>pHIJp(AgdC&m1^c$RW}`w zjx1RWQ_qb~1Q(c#=n5L)smJhVS+Vgf_SyatlQ$es96M4$4Ss*;`0+rw9fsX-3f?OW z?$cYHd#NTWu)i*|prw4fW%N37VSVnh0f_Xjkc2D#X0j%gs@N$~v%q!Y=1d^c_A5nq z_H5V;5;4ptX=Inz%upY9kCI*|Q`+Qtmi-&YLbzE_g4x^2vcf0C?3fsL)p${w9@E7z z>YQs-S2blCDT02_JOdWuniTSluwGsAJI^0(Hy-YxwY>+>KfPzzIX>5=tCIY1fKvm4 zRumPC6#k3(Cr0u^bT(uj|@~3+{ zTS&S{%Z=@I6M3->o}9bxNwp|!k@bi?d1P!b%OU)JnAKy&sxEPiWpP1?q9An9I8QfY zdUA>3x=PJQFSiF6{wFuv%2BLG79J7kF_0|QOb%RiAnUXJ%ijytkLqlA4Kbblmd>pxaa3_THdgVJB#+IU4L5Gnp)W`Z1I2Y{@9j@)g{Orm6N@+Z z#cWdW2;9EnaqGT@V)1{d2ju_m&PV-!&3VxZPINEt#}9b2|L;65bMpUPdAzbbzq+up zp!n~Vh1HMoA0L7;>->Ms{g8yQ>zm|-tq%hk{$zA_2lK-p9>pCP%ku_l=n&l#pH7 zltO9`Ii`k8kl+j5I4Vt&}+=bm#WybEODUhBUeAT+0n6g%}nJLV~`ovUK|-F{{j8OnL~H z%zoc`1mbAyL4O?_pt06HczjRMG3&c8&=tf6bm0v4BlFed}o_7U2ETor)!1m zNGL|zY}-q{84GOQz%jP?%<_8-N17Bv>UJvuCh#()xA7u)F$f*ITdVRxZ3|4`#}?RF z0O}-GoN1#7#cp*i2UfX($I)4`OB1JXxpIL^a#q(CE@Y!A?SRLP^`q$y>I$u1x#jOq@E9RmRy^l;vYo@D)%`C4>ZDJ-K zO+r5-v(>}88OM*)96hx&_YO7;dURFYYWim;exW$budR|zmOK!iAWJqtx zJ=MW6E;qkd45@k+=Islo2X}=WkGA&4R&!^+owv=|9VI@Ae^lZWEX(^{T7C58XT8Hq zvYT^NJ+e&S8bm*uIu5Y-`}un7*`!s}d;J)`DOOOna+|7GNhqJ{o)m@2sdM?^&}t3mk_@^k35S>)pzBF79^&(SOR#B2|{tf&GjD)TFV#U@M% z3q0z>pcYv6cQQ``6O=!pZ>awqe(T--7TQBLf&4}`Qc6!1l%yPDCi#P{8&hD7=`-Pq z0@Qf0Wv_x4J6!P*>*kq#iay!(h4F2|wzfM{xY?vPqPb+R)LoW_@=gi7;zA-_e3;R@ z+zTog{UAxwKZGFmCyg;G|6d=TD`DLIi2gRKTjc+R)yHN2r;z`52L==B6q7%b+)Fc>vC>W zU5c5hI+*yj1@&~Y2X8Lr`gr*OLr4;yUASN%pQ%M$M!lO(w>mpBRL6-6fC74H?n4xW$Q!e0 ztW)lVCjg2$m@8H=sX6TG)FIbz0v%k~6jj)Tf*%pT$waI_n~B;Kv#?n5uvPMH-q`vw zy>QV*@E&<Nel~yDP2HSamn5re5kZD4wFM0n z*9HL>dm(R03FYq17U3UnbM^G}v_pB1&qfBb?;jd2t)KNB%F3=cQ4>99iGt2*f4gRk z*!%vurZ=u@kMc|ALe)j*a7FKxzVoL$d-;pKsWuKk zl26b~@He9u$rVXEU}0T&?VngQl19VPa_~|b*Rw8-&U=G1Kl$nI5N!oTv5AV|yD+vZ zThX&x2~~tc8_r9QT6YE*5V9wN3oM+0&bT;bN0)t>PUT*sxHQ6yHazk`#bl-MkptNg zE~A$9acE>jsMAxIn23SmBZZ{V(}O{`tKzTx;Yjf<=&?}p3?&?0p=qdLuP^ZM_3;nL z+Teg)pQFBE>U0M{s<>;(8nTm@v#ax8&J(o5K@c>=TF44dGHtF_j1W0f77Y*#8Nn0w z1@?B@rTyj*tlehnLG6kVuM!C@G-tk1)34UGal&_ zpjAg&SSS0{?YLP64MVeAFJ+E`#<2>2?ehWa|fI`uUJJ8I;eKB3QEi5bm>xQ*z zG}ieP8sz!~CP7TZq-79n*Am0Kh>=k94-Q_QXjpNxNzJx?n%_6@bI0`q$4A~5qPXTW z4j(IzGUYIG?m6<#!&BBRr3Vk{q{gO8ySPZ{RyS%TB87 z$y%NMM90sNzzqxg{|VKzE&tQ@h~)GWNGa{&<3=_7`d@^Lvhu$j?);YjZ6nnF;z}<0 zpRq7MT%WzYYR~{w${&Fa;E$01aI)7uJmg}~TGt8esvlMG%_9aDz&l>yt3~u6>(a*y z99#~gID;l(j#Qz>K|{R+-Fo^w9*Ne}92oDej-$W8d4dNxl(`orPY}d}!jrGz9d2is zzqF%AsULc2z^b~WmiHEM+iu~bV2q4L;>7`amR-p!R5I<+c(nEAgo(5v6pJ{>aoZ{v z0YrEtN(Xo^Qg-xw`Ubwor`LRO$ByM%DvbQ*B%sL2 za>t09q3dA=(dp$Sq-i87IKTMr91jkqgQ5V@&CZUmt*;-@>Ki1bKD~mn^t4`ldHVkC z(XsUzp41nY)>r0Uzj}*oh7GN*%vkS{&#yibQl1?{9)Wz&(E8%~TTp;#hLyKK;ZIP1 z=q_)0fjT~wo(%hQ$BXy!BVyu6{0~fza-lKpw1kKH7V7m0oRO4cJ9XWiZK;kRD00PH z&xki4nxrC<%L$wjMlrM=*juOf>dkg}KnS7=V`~Bcd-L}88rw~M(~{!$I=2^;n}RoG zKsr^G1T|bOzG+kWr@Ql_yG!9&TGPQy;wVtAh3N^=FXb^Qx`dUI@e@@(JHZi- zOQzjW6;IF35mi|Y+S@zWf2piV^gPoVM~0%hgqSA1nC`?ityJMVD3wp8h90kUHUkd^ zHXS$+u2bNgbvPUq;Rw7o#<`LQ&n18rApsaN;mHz;rmPSCoUA$C=t9qvt`_8b`9VED z#;a}w^P;k75aSCRzHlOllbw4))lr-0Rj;RS2z?0_6p`Ntt9ag?XGng0Zb*ENc`Qoe#+eEEj`QofQNEXXmtA75q)W(`8 zd6@_d#v%a)#oG7Jss*>QKIn)KX4RZ(OA?VW(SCu{Ko>rwyq?O-%-=ct0FkhK{YZ<t3p`PLOO-6Y{-D2x;eG2)dSYWkw zTk?RZT#IG3N+efJ!S`y4R1T!@jL_?()Z%i_IndI+Ksc7>SeTs*6dt7Z2 zb*oq)sabs*V9_V7mlKfcx|MhBMgZxHL|m(R-M&3L{~v}yVQ1X)Ypwwg{W57f);0f> zuM6&AM4DwrU9cP^X7Q|npX#{tdsO;8Gd$s5HT;1x`x=W3Y(@!2y2A0lso4cx8iRgAPXG*;KjLK|2s=0?vbwlx9Q!s>Li~oO7#yt}-74>+(s1g; zAg|;5LzuRW;{ZVoYF=6~O?l;| z9YBUez(EmV^t#q+aq_iV+PzITB`w%2uI`0LtlGjPq%<3ghgzH;gw5n6furDkmrj~< zDZlw}ek|@`DsW#^8KZgPy!IThsOvxhsHQkJS9c93)eZ!L&bF(eI_N~_Rj~g&6{+zs zsge0UsiW-Wn1G}ips5a#-pn>DFD8yl>-Tg_2-Q~6aP^x`?0Y{#9 z7ngs7BTxO*Ko;UR;Hs}4@Cs~tqEb94Vf>TS(Wx;j29 zd_8elU*bLAvhzf=ryVJMA{je6TTrt=tfz4v;RsFYbLE6f{d=#1%aLL;#V> zu9Br!=S{8O=xp|Wg4YHWesLT6AaSg3>e_JK#lDphl8lj?D?oO($uA34I-utcf>*+64A_OznfFT_3hs;CC{VD5(Kq0>t|TqZzbn=G zXsc9ua(eYYt}ot~ys`hdhHaKCRo$M8p<%|utDEcmqSpWJyvN=?9^PKvCG*QPwXW=W z_9JW1@sg4I@t$9fZb!?f>U~u=Gpd^!xwe#dkgbcdxtK z-T&?V-!H-WrfqTkihe=G|4>eFnoq<_8ubtwEQ{pg5w=e$r4gl>2PEGTgYQ6|9vpV$ z8ooEw^WyexG(IrcQ$~i)hu$vmCPA3Dhr{&9te zHRRD0PQj+)wG&=aZhfiBa^-ccwYc^$5TmQTm~}Vm0s5V{M}Nh`DBY9dFzfIrGjaa< zsu8lQYoJBm6iF4hqsD6$KHX@=OFd`cg5uIt%e5V|&ik%{TyAB!)B>CN7*sf@r(+gm zZDXAtU7afQe8N}^RHPT6+^TUb<*8L!jB>lCnYk9G+|F>^O6+t!{?h9%e>tF&M`9IP zYp+>dprng%r?7@y~oBj z+x*{TfGr?aG{~{k35Vd4uof?;ahT*QXV5u=m)A#D0+k+`cvnvgfKWeKfB)I7l@!3 zwPVG_;Ya={E1irnh%l)>*|F-NT=$=ycYJ)#0_j`<0Sy}xAHu&enQVQ@p`Ufsn`CyP z!B91k1ri*PezBaFN>?@r+XW1&H4J(YpI2|O0At83;rnW4B(RyMy{ZXA1?lQiisP_= zsWLN8iMmz8Y~=x%6k6T!Sxp%lmXvO|U!Zi$eH+S>zg8!FTDaCKpJkF0t6NO?b~AX1 zODp&=8?ir$amoI^Jp*{kMb%M=w3Hv!D-_@xdJy64bvoA5GeG_CW= z5JwjEa}x3M3$+w9ux~jgArsX>EBIT1jM?xXiulxL(p&x+BZ2BJb$G5kMUtGM5VYdy zeEGnPzmlMdnThMkvmsk*z@%;)WOxLORP+Lq0nk9GKsl1Etzvqhgv+zfEWIL34`Pbb zvH?H2?}I|6te~8RCfbKutrCWwf33DBkF;VbbM*0ZfmJgjsp&vDJCG;Pf^weNdRHr- zBF?vEj{2N;Opkq=SvZRIl#2N^9!D14s+#Pem|uRLv9`hya8gH=9<+#ZP{E-3TwZl~ z)6F`N@R5sNFx!Ld&SxSZ;_eIm1%%10uZlYB1WA_zO_4Y~@nEHiBoW!)fhZ2tGl+ym zaO>X{ttjeNrR7Kr0+_-;jvlF{{-uy-$fTz}<_Ie_hBBSC_^0$$6B^nTxHVPu$I7TW z1Ocm*P78qt`~pA!lPScs3ZZ;~(rhBnCkrcLHJgDjL67duc!~d?(yIo}LSOwNhSJd( z{Qu$peur|v;eq;lr?)5n#wYosNL*i-B6TL0c*@30pC?{Ig2 z=QsWTuR$m-!Qu_nN!J&z|BOg^O%VX9HGY{U7v_Ard;CmYt`=`hs6J|`g$F?VKluYz z0i66Be0~cV@(Nrb9yV_G(R313&WXeigJuaAlYSG>YW;bPk@z>)p#SLi@d5xPg1M@i&NDr>QqBN zvRw5BugWJD^Z>a%TBZ?#Nr1_0WY0Pi&JJqRK6z zS$|gP2dVc|huhdOFeeFN{0!!@a$^d9ox|Gk6m-fs2&@9pd!{&xTW zQnYW>-gW=iW92q50gh^AJ!N5mrPtbLyy9^J-ywpoy51$okB*^g-|KU{*1?5uYAasg zT5QN>X%sR&k`Iy2ZT5&{2tDO2<2Y*x;$mMT27++ULs^E(d6b z%#O=zi9bUhhHJcg#bi5uUM%8eFzG+=H7mRq(mBRqUvR{Du5S-kh8yp-cn{ph6BNPf z53T($oepvrQ~D0zl>{6Nq&~xGh+6?Wqie9ZAsC&}%_JQ_c?hH+M-Y@x7+pY8o~_#1 zm_7l|`FL>oKrdA32+EiR^<5ZBiV=MP6?EueK!xb_x5x+I86|}=;r!zI^bZ<*@F>c! zW0pQo=aN#MJ`FCn4r7bB;*;H*+a*9)_ZM)Ptzw2sU_8-Aju*^E`*Ro+^T`aHRgeKa zhVp4Cc*P9^KM&=StCPGa#|!G2;5tA=b7~nJH69Si2^|YU#SwRk)KAHL5(_Rwn1Ulm z!ms@qQvPJ{*DwjgG_908YG`+v!50zkz4Qf9gAg|M2F&+|;_8ATcyYiPT#t)|Mp!`! zGhUc`+WDUM9pcq9uAL&t87gGV)76l@Cmvs&RwMd+#WclIHLI_A5 zPpD+(D%>JWL_jrI<9tSNO@X40p)5LE;4T@boBk@M9@{F7b(T&Q#tQEuc<=Jrb7#3u z^&l@g->FE!3Nygt4GcZh1OjESQWe7{vXE-cvsH#SlCVl>z?Hu=Lv?{68fEq^_w|Ug&a< z0cb=UhW8aWDHcMQL3RhI*rVDGnK(jWT3BOvk#WAf&l z&_p9gF$cyiFo>7y81!UT0p4_Ww-SL42oZ~1K1CdY1dJG!BZLP5j${7YHG($33Pt9K(mE2=JB7>9AH%4F~~8HUi}tr#)Ji`9v5e+4m-mmt52@Oy6{4 z=G4Ehp91o>f;0LaT(>%y)fImdqe}$U_`bMv^ak%2*aE_fgptiT5SLC} zSoq|?BC0gS>LmyD!HtibWr?4PHVokG%y0o9C8)y@b7sNUJBnIm5cG(E)9H}=Oy4GY zo~6MWXL_{EXNt$9KsTOMsucyd@O+xWtiP4Kh+n|S$={23i2Lm+0W&}v2oX06#p+yX z0E|cyvQzQI4d#zE$8)8YDY_IWfPF(U>W;r)l1!PCfy@E{lz+3r`wws)zytU5hl4T6iOhKbBtVlvi|Atr!>t(|Gdiv}ogP$eFi4da(g_$j33I#(e$;(pjjK-w9_H5L zFNDzVKd>_!FkYf{ylfTW1XL)OJC0{yUbt1kb3DS+WW~v<bo}r8rVg z`wJZI>atf|j-ESuB>M?*MIE4;X+r-CzL7^!pUA)|Z>Ub6HL!Flt3~d6LH$b|ok(QJ z9KnA%)$-%ceb-od8%kxixkZf-9GI8~QpP!!&^N;VAl zam10grc9!O%r1?8lq1Pcjpdscn5SzU2Yx%zQ1X^;)|;=V=rvjEtuN6b?jo5TaMX$n zZkhJMi-8;Wo?!J$P{)N3jLOk~&Lvre3m*5rY=?z43V3XiML18S$p9Mg?ymih>WSv( z5~}{{CY+6Mr8@jI=mw$X=pgjd&Oh5G>P6RI3<Kk|m_JhVY51pywAXogWmlbOfYr z`J5}~Ubx;?AIw43eU0hxU zB=6sTc+K+S#`N~W!hCsgFCAs{qS8z$FGV>|cx{nMWDs#Gxb#=!G6`LsNdeEhEoay3 zS8d@&MpkAKGf5IU1Ba3&14OG^D!z~Wa51z}{(!5?)(}r{nS-aKghgcwWZ^v0GU8CIV!iULr^>uR_U3hUX~yW>WKa9B&F4-(q&Us(8yapvb6DR3c1umzVKY zMH}&iIh8Q?sc2&lCCmY1KND?vPMnj|*B`#Y`J-=jFCMjhoq0`wNdL{I@C&{PmPfpz zc*C?=Yys!VE_|t4>C;i6Li{$9#_fv=BzjzR+kiGZK1kiS>Dodw@{^9xjWnkv|-5*vZ8biyX$h5D%D))6|)AG zdh%T8)p{HB<%UXMF_qq2o#7xY>R@+oMkbnn%BSROXl;V>TDulCPc)#N1%48rJT7%b zPNA3r9l8>pYl;{nqjsRVM#8Wcc@H1~Q8H72d0rA=o{Ip3@G}r+;N=a?5U53IdmS>2 z=}zl<+C&)#pIyi?+_#L$EGZkccc|(A9oc3|UZNW@QAX<2F159Jtg|gk17`=vpb+|lEsWVuYSIdX!25jVzSYu4LncddeaK+P54q zX|}X|iuz%Ix`SonDyu2ZimflF&%gfowCvRETqejr7^ke7mW-{1hx73H*RnpSIIVou ze&A}gpMPyA$mqh0x=l6EV?pLmbp(5rTukmzb&PV4*_co$l2ya1bFo=Z+@MK)wKo=y z_F8DOOZ18>0Zx3WfMxCdQoGP)Od=mk zf1l3Xd)J(C6=F1piI+;Nml3LhWDCc75A~@^`6}Y5!W){_vy>-q%QEY8F+T`Dkv1vi z*~A0FEO4YYyH3w*wy53(cGHMhAEFqPwAnKKwG{@^eLk@E%;Y>Bqq8rL{%L41(z)HC z|5P?eB7oB_(|t}6hPOCOK(lAsvTGOzXy0#OmYUH(M)M!G57ql1b=aFIwNk%}{}x+_ zQ{kUtD=9CG+^Ih6746~W)c;b`5o+P^-+&>Gu9Q#G(PRQDFENJZE=EsH{LqG% zOGyEzC#I!99^D12r4(0c`VKfiqnLB4K>1B7w3TRCJ&bLlNL8JyRK=d19ra%-AI1MB6B9_&-DLh zUmI?TX6W%aWUC2>;F*5@gmR$erWUiC0ogoW6zv_gZ8_n6${R;ZXZ+}%J8t9?{r{&* z4*!1`2|m-qKaEJg|9^M)@W8zP%in(<9_;@1{_oeIL#O}$(c8C8KL5Jt5xw|BylX_` z8^irayZM8`<(Yq$e=q#|&3nJvy8vN|(Z;&rSKy2YXcL zcaGnGz|#oKKrczg-u8A>#!!QHfmx+});2xn1$}dLb&cFKZ=`wf91S`fF+pVpv|#8Gjb~H8a(U!r1hn0}~(L#>2Zec&|pX zq;6!Q^C?z{+whkrt%A|Oap{>7tNxver-{je3$YRTdL_iM1mPr;nm=V#J<0n$J+ou< z8NJ67lNhQA;@B;y)rV{XCB=h1-HU-58U>wt*C3;_eTN4iDn+3Ov;yfTc!Z)6|I0z7 zV9o4ZIaoT2!Hi#Y#=uX6lt}yx$SXkFVEj&f72m<`m5$!HX07vi44Jcd?QL7hI zXS*0%k5k?9W^$cv+Aj3u3bHk$s^Oe{=cha54L@C$YTm60S7y|EW@M=7`CUna`fYsl z27zYIMq4a^r_5K|odG=kq1YPH2G8wIf$!g?VOgHj_BFy>DCv~wq>Id`rE5tqsdR>D zw%lOIJtMoSV_HE(9E0`HjqxlbsnTHdRDy)X&+ScsJJR0Kji_TF+ zwJrYc25luG-g+u;q2hbK00p-KWw5L_dVwS;dRdB z-(ul6h&b@KnG4@8Sm}#~hjDm{%2^NuIb- z!#(cI5#tif)cc2m3?joRG5uimYmFkDBh7cxx4PzuMWHCN3KW@d+GNj`%^VmVHKY#x9avl}9id2z;S$C~#e+^Tf z?y9Iy+;-sIq8dQt8O|TJzQl)Tuy_fJ`Lx(=|AAHHz<9?6StK2AFvzQlD4I&nON+%bvvamXNVe zIM5TF4wQ3pP9Cu%;aru&u3vegvl0~fAcX-8RD#5OnhQWaBr^>@p%#%6KBA;~upxvD z!vi+LH%o2UH74|d0&Oq&czLM`GGG`{Oo|~%fKLdyBrl)`ufJIh!yt&z4=cQz^ z*vaO^F&jZ0sTj}or&PVfKvwVi4LdMAns%ls_MDsp{6BRN6hi`e`q0p)iCJG<=d!HO z#bcTzSAB4qG4#Qpn~?;~>D!Su1-7DduxeM}U{PnR9JtI$C2)!Hqg4Hk7O(Z`Z;+@5 z0(6AZ)nr0Ki zn4j{^%ye~pg=2#NtzWP~z^Xk{K03mzZbH1rfM2oUn-VW4U?d6)Lj_WGcLqqdQm`Q^ zYXwMfckh)0U4!05y_c^s6Po=gz=!5$gS}d+Ul4nFMyG!{dW&~SR5Br_CmyXGM`v#$ zW`|~i&#j^u?#$0*G4&cdH$B!H988HLD;w}W2l8rKmR#5*;{=Xuco%(`&G8o%EpA(9 zovr&c9#GUhWzl7O9+{H>2Z#nwbgT}DAjHNx^JuXe8UQRNqEqlNF0(+gea%;MoPOef z=P5F*UdCg?Fq>{%1Dmqa5ZZeCN%lF>-t#)iO$)aD=eD2SE_sxEHX~4r{by&d+eQ4R z{lopeorCUvm+}7(4}Y`&tEvjl<1zXCS~Mzx%{ zfPy+B7(!o9b`7Zt#2S<_C}e~+KMMzTWdsaxUORiAw!L_sf96 zxcW*xu(5gn!unS#C&KmVyXgG%uh&~=_@y!oUE<{baJscczlK`3PgONMhhx}iK84Gz z5~jy*PmfNvr1Qq!0N+zx1w9c|^hAk?1LnK`5AcS^A%JY(OPHY?SXFaJGqFD5aX_Vc zw0tpG;k}zY{gwrio)Wmgp)a1M*7T|$G}{M|R)h&zn89odc#1KSvwR!h%KsGE#2`MO z3ZhJ64iT5YF-<;jNJ%jQ87n}=E%Bp}XncYI6qLsl3!YO`(KR}CP+O_Ij+PP;mFBj! zUB#YES@8opU|>F9-1BBc_#JlYEg3=x@=fG!3`Z_?z2x1|`^oetAL|_==mDZ-AttBr zTaqyx_Lcad)UFD<#`d-a8j@t{rI5aaz{{B0zSYJ6lE=^dQ=1)t%x9hCQ*pN6f z@{)Y>79dm?mf509>s<6zG~h2Gs80UNRjo|Rpxc75U6sppDrZIcLu&&$Iij{ErKM7a zW2`2q0hrdgY%4nkf5;q6rXdH7RXhcYPG{MJ}by2 zed84T7Hb5;){#FH2?GR>Vac_zIKt|{2^1iB$MMD9k7YHJ3`eWdqEq=dox|~Su$$y`6xMZlVU;+;_=iRyJI7*t5x z@cL2bIyj(3#EedQ>rlE^3YMqT`w@Q4M``~Q5|5Ixc$%vGLF6)^G-asEv1zPzhtS|M z_T`W9B9(BS@=8L&6eUYMJ1Vq{>`f0b1Pt(Zp~914IOQ!-ejsU;Qr6iK=a$ljcI-F3 z0gu71!iyAbyVsxYwYDojM4KCh(3;3EAi zl`N0AZ(IM@A3)5&Q?t*e{%gzsxx0VJ`0o|{-@*QG`9FUZs&VQ6j$U6~Um{3egZi&r z98mlBRp$*O4XOlxqmIsUPWFfSRz~r?HVD@3F}P!SQfLMZ{;Kg zE7nQ$5p*KK|2mo!2l}bCxlVI%BYxW`dCQl}t3liFmMi zI=Na6ppVX&6-w>G&Nf2)BmOwj8W*2*KTt^>({~Auf%-87;R3S?9$hD{dC@tMM;y1D zyJqf;hYj6;&OsRxYFX>HAk%ezkMvFqXkq(gS6(GxEG5Q)TO$mNkq8d6KVdc>J$z{z zc?un#AzyW_=wuA$F+cL;tcGf60M|!4_Gs$FFJe&=V_-WVAb`dYHwQqlfOkL0s6cKI zshD(q>Vfb$@&R_c_;~ zsV^7*r!tim_b=Yko#O&NhewJj3di?Zx%+5g$us`p^uQfLca9?4Us*w9_OROCtP`Cg zc^{r8y?@}p%)wedQE^k~Q(2TM=wI47)4SnpQ0d)DmRYGy`|gzlZAAM>+jvb^$9Pc2 zGKoJ-8#N5FHLVa?R_RFPcfN+ZHC#vd1zuRxkX7MVp800Bc)Acn9e{q-0ed!s$|R1T zFD2gwL%Xe#$>NL79p7Ak*q>Jb=29XAD@%@ws&CzAfB?nI3<*bVoF z&}yBK_TdxgN({7~Q;B0H@1rYgY$<1Ewlto?#r=m+{U!DD*r(04<@j#_In6!Se z`6ofo^RM$Ss;(YS8wj7W&;P({kUx@_yJE)?Vx}Jb6__VeN*nJ(T)F~q!4c-DB`0$EVOReU6){p zb@-aW0}|Enszn}+71Iup>rS@q|H#_HtMN!4i^mHrTmfC2r{^L*T8LAoBtR9xYm`WZE#1piG1WFe+D&6SOWWkqj`*1GN>qGB4W-W8#`;v&dj_PK_#{3LH(Rau zIMZx5UBAJ<+x*}B0hIgslz*6^pa1Lb>>uvb`M>VrZ~DJqg*IIL-}}G4gIk*0_s^8s zF%@8CfVHT|%ZS?t_k}*Eq%fKx*tnx1;*(a}v7ruE?dy7OW7ni4qU;GPFPXdfktGfA&^c0x`l530wS zOw{x-Ns&>BhBn@!o=IHA9>`a*^oV1CW_bSS}zWS5e+JPF^x_jA+QbRf`pK|1xeaj`_0AW>$8)SQ$5TI zNI>i%@e5Z7!tAwolOze%85PAi@*3hHgOFp>;KbXxKYooDSNA8c#baUT{>^J_8j#e0 z`73n>Vx<0La$?jyk-8_WOXyMkgeWt?h9o1LI5J=Xvd(u9aME?wIljC$Ya~d(Ek}C? zr^mY(9i5HU>sQda#CLdyeSMa_UoPGd1*l)>HCA(YbYV-N|8-1XD$t_Q>eLnh9pewd zuGPq~ZEBp_>%TUmseN>trg!p58z}0h6Y$Pg%>-(UynohI%|;ZO$u~o1i|e2kUg1W~ zTlLP)q<+3KsD5&Shr>t{3cCh255iDAu?YPMGBYlay(e#9T{m>{hMj;!s;ZCDsZ}B~ z#cU`SE&7COJy#J)Q=zmH85Z^alRR#R`ksq7(O)jkPNMg)tae@!D07-_>86OBEuUG$ zKX~(+mz3ho5?9s#Y}V?e_GhzJZ%(TJ!I_I(F|Il7eU>?G29=r0X0^=>6zoWWQAim2 z*2=dJ^|DWld;;B#eUpJ-&#y3H)CGnzk4-7Vb$78gVoVuXL*HJ<>1>?bATos9)J^+J zPQv%lq3MeRuL3WQj}W&8xq99leRz9qj#_>Hn;QkJrD`hIPtgiFK&gq$9S3Ppl)Ye2 zxGo`8FJzfieNJbJA5v#VUI<4!;phIln?*PwyL_sul%=23nc|p=gH&C_>Wn~O0_4%+ z6*|VPr4nR~;z=Lwl6oq}0>FENSH=lTad8xTSxH&>IFJJ*$DqFOTgIsmqqtP>Nbg|+ zMmz^<=O1XnW`qG;f&!Kt`8XLc=WL0!b4=pBYGLM4c_eJYwg_?Iz03fYDNb;V1oDDc z_UJiv;}iF2a`Vnt^om6~aZVq9Qvk)02sRqbU%UXHD9ho5Ob@pSWEV69=e^<%UoF88 zKxs~NOY{pjjLvc2;O;C2JuCXXVqm_`wi<~z!D&o|gI7O^2{VsTZa_2olV3%-v%w&? z{!fV;Pz!%H9iU(Tx6?h`Df|ER@c#F(yVLv4{=32Q{o*PmtyxZ^@c#R7e0_ELe>CU- zE9Lec(5KA5TsueStrUmKLbNMjy?DA*d{y8WmBpE@W~v%1uJag7$|?-O4X=iCKcw?` z%^F6+td*X`*^quX*-+imD#t#ig`@d*5P{wx z>Q5d-u;~agY(W7(4s1YbzSoA>98!ygrSY!TTM1wobNwJH4%GGkxNdUfmw|WHRo^T? zifI`(TI&_OFc(Nx$2p$I5OcUDu#jR+Eci8Z=hra6dqOXnp$$J8?8$C;iGa>4cSvdf zk^A{YXMlbFBRKAAnM|_JcKo;5e|8VLy}JMR;m-bV=l?HS*bN(lwq7#|GSnnWqr`BqV9*cOayY8shXV)LD&)y>DU7>O*v03Hfa5ogib#REt@Qy4~(KqKG zj-%*lo)DZV1@q6Hum9B9e@ZdE_ypO0@2O;>(2e1G03j`6-Iq@VqgcKuqW2Cij>zEr zsRVKR*iR%P6e4xeH~5?5KHK$FDX|8@W~4KiT{X08iChd+ z^wd?`+>WVo@rr{Hf*wjss(q+kz`t{b1Deal(s((UJFq~H6qm+@=7Pus4&S4lsC1Jl z!l*GfmkN2IVhZv#;OzHHOa<~Kq`zUZ7_JZ&qr)KT*=;hGFzjZgTOesdh&&}9z)u3< z$Iy-K?7Vq{V3dFSBgnR_WdX4}YQK52vxC}Se@!AMjwBxm?&B(7!nNb^Q7mZbO!?dk zy4&+DC7tHZY{HQ4`g` zGyz~OclIW{wg#8oE0>{_2D10z4s4% zwBTr3(8S)(CpFD!1LWkVz}l!K8fm4(N>kI&ke8n#OHGT0rs;+Nmm8X<8=U6J9GcbCBEejcLYIh(`06D)1GS{tq1Kt~c1}>D1DPPZ8m09C)X^7GCBx^*w4I4O>-YY>a% zkWm?1ZdQoA1>Gyuw-OULXLCfavW7hn#I|OR9W4DIfiKQ1UEPSWP#iGQ&aF5qNUBA$ z3EKQc;~yiaPmm>t&z@#@)r4n3Af*?=nS9brT{pBf)rp>~&Z@@zi-z;s?TKw4Z181g z>+0;?2`t$lGdxWs(kfg=RR z08;FL=Z4Q1(p)k2G=0Vp@EW^9x#%zuAj9k7{8W5ArFV%6VK6d8J`S8IGunmNBWy#k z7GX?=!p4D#{$PejcgHy8p8)Ie4P<&)lBTIRt3By#9lv^Idi&r1>)^%ii|&i=ya#(X z3@=jzx54l<)-$v=&;0Mv$@ijAlT&P+jNsI9gToW_EaP={d!|1w^|Q|R_us=Tk3Ege ztVV|81wg{dE}h|Kp&v&IbLW~!5KvbBIWHv1_++ z?Co^be?EvT+QarOL{Z+zmqmOR>tuO_ zop-79plXVvbSPBNzr9||G zqI+mEgp&}?ea2`yYw{e`40`bf9@=cQQQW2a4QBRB+L+HywvLa^k5AveJw4GPfew98 z8eC#r!1Ft!!f-Zx*iwCf5jSesQe}Z^rlY`Zfp>;4?la^X6%h0g77TCB&d;vC4GRKH z3yi!^rac4-{(NDUU*M(J%{!=e`!^v0i2@=a(y#w`0;ZGr>>?yA(b>goXfvZB>`SqU1{31E zilY8t7%k`GehiCWxV=h9vhWZ{Ok)@bkitGVG^0^yd_a$klm&sw0`cXSp<$pdAT$h< zNoWXW!3mOf%-~pnw}-d_1V12!h;$zsj7LD2YIDHD zkU@bS13rd`r}7x!z_a<#Ooyxu;dl%<(Uz&OVgZ~DmWV+W{0Kw9*U&U7zElV}mQEx1 ztRk&zKlJv6^I%}400P)nL(6P{0s$<-qv8>q=#StKB?#$wjE4xJS0o8en90X!8d{o% zCtTo#cntcj_=nI)2(Mp5;I`1Gq=Zxqyb|KCJ4!x=216W^LIaT@DCBKmAZ|iaC8V+7 zD1fpvI3oo-5Q+q;ogTrGAfyZ5;)Rwy9*3w7DDdd;aiGHh0>8J5E_s28E{j>+UPxHwFZBPX~M<)e^3 zCZP#}$(sW24hUewTV&A+u0(qjlHfGK>sz=_hCQ*uTLb~YD)`kgU8E5Vg9`*V4!t13 z@E$;(u+Xa!Tw8_=ULc9kRLKOEkARg~gf~SZT5f1lho3 z#UpqF1}GkbSFDN#9+=C`Ch3yo8Cpd^ID*4~(7BPrsu)yjAvwMlp~Fn9xh{u8V zRd6wj!8@6*Fl5|L8zhTD*0!KD zGKvw54*^-obc96682KMbq%^=G>taw)k{B@~QNQuvZLc?26K zNrPe`iZV1ewyRHlYRLUx<3$$d_;yIPLYbx_VTHx33}kL6;%Oz{3^gg{38 z5fqp;lQ@kA616-eAi=i@5-%F|0#*hn(A8OOk>E zYIuu09FGAIzDk*eloIrlxmVQ>yf z1B)BvEe}cshy@WA0jnuagupO-42gmaC-gGD!AXr=-a(Z|c#ITwGX_;4BJlMhU5BIu z6Kgpn3@aqzT);-ScoYl_vVucdG)+RX41<8nK?V{YvLTsB=F3`eqltQ^>lL_rbOyrp0W$+-f8;Ome~m{v37 z)_`(4T_DI|U>HK<0B&z!+OjyLeQy|W4Tf?$1_dC#AI75o4hjX_+Ezim$%cLS*TG1_ zR2-p&`(Yn;w2&5oVLz~o4)xF>>5SQ`ZM5dsJ(9-cuJ9rEWXcfTL+Ct0sy&Bk#I)ylOdC?NArxUvlmfvl3u>YQ3^@z6><@yvd;=C< z7>uG3c8`pcMYf#4-)R=o!I$XZdw{^WSj2qx9vF(dbvm0oJdU%Agz*TrO89Le;cmiG zC%xbMlfcA@xWJExI!$QYu#S||zGdRlzWKl-($7C3k zFq2`>0|LXtUpu4=(kNjv6-2*?5iEvps6&!Na)THC!2yT@I|+^i4G^V+Vqy^mH7kyi zY}j9fML`CS3*`R|triC==9B&dgF1(#h&vduLXMvXy{S^@*^=39ya@WXW)${#L_p?I z68K;Uc|pZUT?wDzB?4v#+zBymY>6ZrC8Ll=B1jc(!4mN>Pw`qiC=ekZtH+B)8q~`q{unlXh>Sn&XCXOP zxPpat8zu;doxBQqN6BlOz*0o+mKX9!xToF=iBRa5C)wA60uxgE16wt&iJ;SmwHhJ7 z?&6@2BF6zy0Pl@tk}N}7i^4mQjN$JV@^BpYr^zJj63h^dFeDj1$}<-7RKPo4M8*vZfdsP( z2|^N5opm!w2Vo!Z-XOOLlD&`O`;d!3AVRXtA>|eoxho*{b*jNE{vjw3*==YO+bv=w z1=Z&$0D^9WBqR-lbzHqg;?IDyPex%qFl2m~7o*#-jyOm>A2u*~GVO=tla(fMl7tM8 zkJLs%o%$2FN#fa2&}AS`B-~NRGhr6e;UJme#Sf#1AVfgW*H_w~UI#f1x*p=#A2?E& z&X^b!50Vl(k_48I01hNL4a&Ib6;Yv?9|1l>TSkcwzz`G@poq{9^pe>;sH+YV2p|kC z{OzZq!-Z=|=1EB520$zqD|uue77Q5eLu$jB7HyI7X;9E;0|G|iQzDBNs~J)d2jw&~ zs9{686ebyWMra-{OPP!*sNp)v(g{-q&Y}q{6+y4BEO2-*WWElW7>mOPJ)^@qc47L9 z&_V`5;9dy14FABRuz*bDKUP8gHtyn>S$sn-z+D{jV#4TjH4R7sfFLOPVVMg#`;b{% z@7CR*Q||>GU55k_<{~pTgz{~-FKy1<^=yOQGAg>KVO3;8IESm@bra|@i001#b zgNDKc2%!ZgtijvB1|@(Hnq|gA$P95Y19Ht%ym$%ySe!5fPMr=AZi5Dmo+iTu^6?JS zpxSK&@`3OmPz1dslG!R~H1)6n!~=Z7oFF7gOqflDB=aE~4VTEZp+ zCl(?`4+%+}fC2e6kai|R%rUqmr6C{bNj_ad6Y`#%1wJl<>vm=csS}5EjG4vjM-dd5 zaD*1?0YhM8n1&$emJFNBH+&*^I*A~}OxW~&0z~k93PC+);m9A>LnUYuhuLZp(rbUV znht`#Lre$%2*|2obUZ2w3BWjTpgDE+L}9aUX&|yiNY6pMq2WOUi*P?Aqn6^~IHd&rqTtRGlFh~6_No4y%tu>8$2xwdK`X?7ZbQE1=VAOj++rOrO_O|2q8}aT*qMp ze$5dze~q&!;GGyai3EnnE7y=G;XIB*K4}3*kbyU5P~M<%P)l%3(x3tZAqg54W1c*M zJh}6*)R0b+VMuW;Y&uko9`bP&h7bAl6~iYX9TDaW#HCTjoxV;H9P)x(B;eMAMu(X% z(i`~o2W6Es2s!{@NCU9JMud>ncm)|!kOe5qkPm-keB|;Dxjn+3ltB+lxXT39Y5uTE zm+)mCggh8Ro&<}2$mke>_39ixB$LAN0|!VF5Q3T;2?3aukavIqAv7%n_CO&TXyXZZ z6#|5iH}N7Ktzc@4Qm8c-2qhmB6T$6$i+F-uM^8!y#5$-oAAqoT`Ee#>1l&n7q=@pd?Z}FA^ zdacl!Bmo4k?Ltaz!C##MfE6`*5p(Y)13bG5N}6HqPZ@>}EXHN=1Wt=-&@AeJA!MGH z3_)fC-VS)z6LfU-?kp$>;L;Y+ZJI6lz9>8-Sy%xsgS8=SY63(t3LH}m5X?hop8^b^ zgrMXI0B#TpH#iK_eiqVCgDZh#y+MzL+kViOj&esRg1&ajRs+6&4S6O#APFiL z1FXszd95YZ)M}1UoFVCx{&Qi4x@FL0_u!Xq;*eLB0w78-o=syN_)ma7g z%vjAJB8BvbSj})1W%M1qP1B%&EJDhvt1KRk;k>AwN(1i5&^eZ5{E)0NUEITZt$j=< ztDxHUbw61~;FJTB;kv(=MF>7QMkH9BAT=;FP`rmEOCltE$h-9*Y&O7kSU-0TKkA3n zEU#~qpm{0Rw;{Q;b+Wt-oOErCM<0QoIuL}Aa;uCVQK`4O37O|9WO$*6xF6Cr<;T&l zW)A=77hi36PG_S|f3-}X-!S+sbQYryqX2idzxv{L$rzs9ohTa4Co3cgU1eYV^NZi% zDM~Wde|6BozrTNo&d#q-FVBzOMjy`4jxSD5FJAxo^!PgZ=JfpZ^62{XB>I;9{oCe$ zUVMeH2?+expL{ic7}8k-=`qvAg9>2&;&$_%nrrUt>>TXvb^Z;1A0F(>-#b0^Z+t@G z{%)tcw|}_5w{y_l?{;>&_<8Veot@?`KVQj;j%ony22y0|=@!+-V^6uvWJgVI2Y>&+ zPi{EuR@KQ?`ohQrNBO z^#0*pe}uf59q3=zmF&IOYu!JE^Mg7wK@TsQ2+%2m7Nl zp7d|BXJ2%De_*^H$Y=-#HS82W!E^td4PIS@Pu$g;6Q8~Rp;4u&BT8nFVDhu-mz)IL z;*I;iySsb1bBO(a(B0WT==Kf|J3GDZ!Ore)`+xIQ{P{IYJ2JmpEUs$JWIht74HPO`5Ct7J0z%Ip!HxHM8%XZB+L@Io$keSBiPD4Rkh zfSt9>WWdf(oxjVW;(+D({?lik(mikKuD7)3E$#YBcYKB3mfk_V@Or1}#nOQf+CyLA zOJCtDUt!meA}@}*ULqS)8i>a;`Rb4NxdR|=hytwLlanqBPu6Dh++V$dU*NdxNFRpgIxZ3sNYS)XaJuj~I zytvx);%d)}t35BS_Pn^-^WtjHi>o~^uJ*jR+V|pW-;1k#FRu2zxZ3yPYTt{ieJ`%| zy|~)<;%eWEs{=2t4!pQJ@Z##gi>m`Kt`5AoI`HD^z>BK`FRl)}xH|OW>d=d;Locol zy|_B`;_A?gt3xlY4!yWK^y2E!i>sGjT)p(->ZKP~FTJ>W>BZGcFRosCarM%RtCwC} zz4YShl^0j9ytsPh#nme>u3mX@^~#H@S6*Db^5W{17gw*mxa#hBk=5PtVynC3MOSyn zi?8mE7hw?g{amycW!)Vw&bm8Zq;+@vSZib9{a9;Z;#;`*7B;?xk8fe*TR8a^R^HFc zcm2$K*U!y&{p@_#&(C-L41L$n(RckUJycR&O7%1KT|Zaf^|SR|KVRSVGxkuec_H_+ z_FX@3-}N*1T|al<^|SX~KY!o#Gx%LUhu`(H_+3Ab-}N*3uw!^}?q~D6em=kJXY{*% zPQUAC^u2}7ety5}XZX8*j=$??`MZ9eAF60CTKrsp*U$EM{d|Ad z&-i!!oPXEP`gi@jf7j3acm3Rd*U$cU{rrE|F93A?5HTFT~NOe(Dh3JUB4L6 z^~(WWzaY@{O9EZLDA4uG0$sl_(Dh3LUB5Wc^~(cYzd+FSO9WlNNYM4m1YN&S(Dh3N zUB6h+^~(iazhKbyO9oxPXwdb`20g!Q(DTa%J-=+w^UDT3ziiO+%Lct2uWZos%LYBa zY|!(|20g!Q(Cd2d(4Jp5==o)Xo?kZT`DFvxRDH)@&o3MF{IWsMFB|myvO&);8}$6L zLC-H6^!&0x&o3MF{IWsMFB|myvO&);8}$6LLC-H6^!&0x&o3MF{IWsMFB|myvO&); z8}$6LLC-H6^!&0x&o3MF{IWsMFB|myvO&);8}$6LLC-H6^!&0x&o3MF{IWsMFB|my zvO&);8}$6LLC-H6^t5d7Pk7msEbW5_L=%sOw|E*hgd>t|Z+|Tnj=w#+RJQ*LmtQZe zoa*($ik7TiSdqZ%g%u51y|BWi)(b0VdA+cruc#MRG-LI`ie9W2zdXSMa>se0*IZM~SPUU^nqFRrQ=UcGJgzOzcb1XaE5thQc~s@`>0TQ5;nZ#t{3 zm#nJyT)l1enzKs1bXC3NthQdds$OwcTQ6NzFF32Mm#(VUo7L7!SJlg{-nM$TS*2dO zs@`l?TQ6Nz?=`Efm#(U}n$^}zSJgYM-nM$BS*2dOs$OVTTQ6NzuQRKym#(UcE~~AV zuBvJ-tF4!=s!FbU+p3PsD)rJ;Rl{Yq_0m;Uzh$-c(p6QvWwrIvRaLiDZ(CJuS*2dO zsw%duwqClbs zOIKA*metlvS5-Y$y=_&EWtDpAs;b1Y+Is1#s=~6`dg-dFz_QwU>8h%}vf6s-s%F`) zH?CQ>?P4!!)hyd~YcFxtEZcT#FL~80+jeU&fz>SAR_itv-b-WM7AC%`y|TylW38&a zvRnJHR@GkFt^HW5YOk!;erCRA*|uBzY0b~i*DTw1=ic2}vuxX~z5BCU)n3`H{X5jp z*4HfCR_A`kzGm6BTl;CPs=cyX`)RGJy|P>TX|1ZgvReBY{F-IkZtbTvKaXFtY}=jt zX|1ZgvRnIUt*X7UTl;Cv&+6AK+g9g(X1`|Hwp;sYt*X7UTl;CPYT341`)RFe*|u8y znf|Vy>#te1?asYCN6oTrxAxOo)v|53_S0I`vTe8a)0&_CuUWRO&iw*F&9ZH`_S0I` zvTe8a(^}QCZMXK*TGg^`we|}FHOsc$+JCP3Wr3Py+wR;?YgNm(-P%uURm-;B+D~hK zk)USTwmSC<1vSgI-P%uURm-;B+D~g$%eLLxPis}ncD;4YvTYZ8x!js%+ivYW*J_q+ zyS10rYL;!gwU^dvmTkMWm)2^QZL4+FvTYZ8cWBMBZMXK)TFtU;xAxLn&9ZH`_R?C- zvTe2Y%LX;ew%yu~HNR|7vuxX)`)RFe*|uBzY0WPi)GXUp=YH9sX4$q|`>|HFY}>8< zwC0x$YL;!gb3fMns{fj0+v?me8`Lb@c56S@{IWsKvTb+nr!~K9P_t~?o%?sF-wIH( zY+IdIE!%dnA8S?1w%yuKYg#s_nXZwc1X1^4wC+!8W@~wne|3Gi)t@1*8{^k7HS4p^ zlRui@Uq8v6e5#fCNd11kU|s#QvFfBh9gO;{seUzAOtKt+iT>SK8EIdT@0`7=UyT)K z2v^5y=O5m_ZLFlT(z2fZ*<5wWgfCK4el=EH&F*IDdd8air>p94l4fxZJ2{rh575r-p9kF_QYtmKf;2{Wn*Yc-&u2mKn-l!0|zTyQ-z^;f1>-nxqKl zEnWOk{d7$k`3IH{=)}>7#6CbGnL$6}+P*0N+fn@=8R<4=+#@28{{-yvPe<*$wV4;Y zF8<}HO7e>kL@qGsY>pSVVgD}}Qv7^x}UNgG|+WdXyC@w9LqqMX-j?z+G zV!~efr=#*}IgVbU8vcI%Qk6T71SFzHV3u)WIkU({KRekvJ~}@>ef#$GqT9?KVMy(pUA45GAxf({{Hm*yDb(K0g9Z%#rKH7<1FFr;4VRU5oe28!|+V4 z($Hd-B7GskxHe2^ann$cJtfESF`{Fxi1Pi@AVgHU#H*!~RUt6u1i;!B7AqmK2tF+T3=Clr+ty zvXp4H=$^l&L7;Zc=%<=rj$-29LN%^-C1!IyQhTj{UvxJ7y3~a zFXN`7MjSRGhz03*44%T>CsMmQ!AL%Wt8o{lL+AJAeINheZqRfZjFE@J*kM3saV%pR$oA;Nu&Q>>W7(mGl)p58qEGpWtEE%jCmX_NC(aDf7&Apd>+XrR1 z_kB%Galx0Ey8`hJZ==S2>!Iywl4WE>T=Q-sdnKc~n=H}@hxa0BU{0EL z1(RerJDfFrc7u%LsiB)SBG37X8a8Dk3kU|VVU7(%moAbU4~xa(hSi-TV>uXoC-lNG z;FgCIziSB_Z=`wJ(4b*&&lfRMY$sy}I5z(vy`4#CsS6!7kCBE3aCS`FP$=%R*o zT!0s%eB)t)F+=x8U4TnR>Wl_xy;sh{bLEhcXxa~pMg%mrXdpWT(Qd+X(>cEIoa5f2 zhSPiDJERv)hjbI0$?uKShQdb931y8uFUp$v7G~Z}$IktgP0}TSac?G;Hk8a`H|I+z zpfbu?yoTlt%wg00X_$v*{=n2=mK^6yeQSuanR_}V-HyB0hdFN_q}5%`c+6pwvB}5& zLb3_^+L#jwkVC!YI{eTRJ8!K}49yIN&DSVXWT3-Ed;=}o!Y$CP8%67jc!`WZjOtKzpV5l0QF_C#YTC>oFDufCL_K}m!F-Me*BHoE)8C`weN2@s##KThH zC}t3D$21|3MOnInvYe8z-R>46g664!Alcd#D10Q1KqslAMHVq9E%-p_uANiW47J^K zl1vAV9!VECAmpzm1W<=&cQ}h;UR0=>P4S_a;kFeNXhQy3`3TQ~T8F4~NiQ+ttN3WPcvn+ks5wD1~s4CtS6l z8B=BiM?JiSZqx466qBo+*>vB(;W8V*q&TlURpvTYRf$ap_}9Fs-5P6>1kkO8j z0~e_~(>B(Lxg$$m;G+Xc3vELIt(|K?3hoX#uyc`r#x58LD`J0#ZqGeUQ50s>x~K&1 zc2mg9jw#Z>@2HEfHucMKK6ZZxzh`%olJ_2BC)t~Sm_{_qKrpZgoMo;fSH43t>THoO zr^>cvEle5_ag^;`aE_BjwxoT0X0t08R?MW%c8i$8jT54~bvm0o_%Of$8^LV?Rm4*l zEaazP;&v55JJbe?QTn)=V>b@n3_Eax={JGoxHg9{W-3|AG0=1h?$!An7h*Jq0OZ^( zu;S4w1YXFswhaV>OB1=IZ8D)D6H|go!d05yC9W~%jx7umAvg}l2n!d9x%WsTAZr^< zjxYVC&BO8Vq_NoojCl&{=uqw|4WI4PM0>C}>Wjw?1z5*ns?3^>MsvrJIf^snNrGVI zW;sVtox&XKmg88sn1X}Oz+^ba`^enp77Ca+&GnJqXpRD=V1lF+L%1XN@2GJDNu=R( z3X*ip(F+|`><;*K+Rtph7}u0~1XglCVzp zwZ+d)#uf!&?IsRiHPt7XK_>Rt~Q?pwj1EtNHBPEt& zJtR+7ls!PVHoJ3lCC()F2=oRLy{WUvsi&g?kD4Q#bdGC=rK_cV_R^Qf9s|mJ$4BZz ztZ zBXWFP*$5Q3T&t>b)?Ge`M}=K_GjpkF;GkpEg4(&0#8wSK@#o+VkoqnKL+9v->mw^= z!!xrF{A9+&nTp$X@PL||(TV~`kHYLomgoBAr}edtHloLk+!`3&DuUwKaafvCH!Yh| z_xa0GlsMqQNN)Bsnav&76^WD05 zylQ;q@76X;R6LfjdY$wKj`dJaeH@h?(g~cGaX;PLH>ecMTsj9S5f?J2j!+fD7Ucw> zfr%!4&1`f)sW*1-T9>${>~!|9PNIeF&LO~2e6c&gN9~E@I*c1c9`Zd-AsVdr(m_iT zn*kM#mjfQRB5ac0C|j330hh56$|yKOK~e2us;032xaKOJdshF`{ymNcdL6ooAm1mB z6BLbvFS0ifN?gl}&&X%)8Ub>>V=YnNeMz8~w@#WsftxJAs5;-S;FU<Z8UkTChflQ(S;;fRNaC! zZ>1^h=#HM;T%6mQt6YxlPF7HQI?%IP-hdf&OchR26Y);5yK>4Swi<2@Ev-%R4Xzn; zyi!|ZM%HLf)e=5}yS0%%k7HYi1;#UQ84ysQ61Mv!4(D0IkM`2=t5X1z0>|;m z?^q%m7pA~HhVy1&6B>0b^r67Usq&^@L@74+0ty>PE9C|2D3~~%zO)CvtG>(O5#IPN zs91N8V#q8!Qr*- zpm3)Jm>>CE%^veEnX3aI?(^g1-f`<@6DV@*hAyYK3}zEYO?n$Qe2$vc94>wh-N>&aEU%~~>vG22> zWgp!r7(4xiQQ-1JP@mnNFD!F#cW@de-)m3!0g7CXf(2^xVc_lRVpo?Al|COXJB(85 zGarh;^e;z}4bswOfL;zDJG#V56i~Bfn>SbhPRQZeg%Z98v~aP6XPV<99&EVwJ=jrO z>EcxQD%U*Ms<>TmNeV716vLnmAZp7i8@t5eN-rC?*=4nPU!w#JrsD=d!7OuQ$QAWO z9vM0Dxpw*CEM4p55@kgo6l<%!Czn6kxWVgq#R*qW)4;f$$wmM$TfGE6@ zp2SYq2VCrS6L?+1#9*#{tK6-vldtw%$;!BH&yVQ@4(3+h5}Cy1EGsVwAZU-F570_p zM`L+OWdp&+P45fV1DDow?c!(n(dD1EzDE z^7s3log%(cQMs~ogfIc%?@<4XBZ_o}^@etVTcdzE2(l~H?@ zQbez&l^(bD(ru@gZackn+v%m-PA}bddg->)OShd~y6yDRZKs!RJH2$<>80CFFWq)} z>9*5Lx1C@!(wbM(lonCtF^wMjmmtH%)^xEmA*G@0J zc6#Zx(@U?NUV82HvfEBCyY2L{+fFaL?ewzSPA|Ld^s?JdFT3sZvfEBCyY2L{+fFaL z?ewzSPA|Ld^s?JdFT3sZve!;8d+qeH*G?~c?ewzOPA_}y^s?7ZFMI9uve!;8d+qeH z*G?~c?ewzOPA_}y^s?7ZFMI9uvfoZG`|b3y-%c<4?ewzWPA~iI^s?VhFZ=EEvfoZG z`|b3y-%c<4?ewzWPA~iI^s?VhFZ=EEa?nmM2krE7&`vK0?euccPA>=T^m5QnF9+@P za?nmM2krE7&`vK0?euccPA>=T^m5QnF9+@Pa@bBUhwb!o*iJ8p?euckPA`Y;^m5ov zFNf{)a@bBUhwb!o*iJ8p?euckPA`Y;^m5ovFNd0592y$si)pE7KFkvOh(vFXuFuZ9 zxt8YT&JzaRz907fevjX|?hJB#Q;Tk?MU~*1TJ%aSs`S^?Vz<m@oJ;}@v5{x@OzUnO8ev0M*HJcX@B7NCS#QL z$E%I@$E(u*!0%1QDD96|8|{x*rTu~5n~YJ~AFnprAFoRL1HU&IqhHz|_`OMs(*Edg zv_JZ#{ej<`j8WPj{f+iVzqCK_dy_Fr`=h_n{^*zX2YzodMrnWaH`*Wl(*D5jO~xqg zkN!scqhHz|_`S&(rTx+0Xn*ue`vbo>8Kbm6`Wx+!erbQ;_a zj6rFC;P)miO8aB5(f$~e_6L4%GDc~C3^v*ygVO%M?@h)i?T^7m`(serANak=7^VF& z*l2$YO8W!9HyNX}KL#7^k3ngF;P)nDl=jD9qx~@`?GOCkWQ@}O7;LmZ2BrOh-y4iE zH2Y(BQ~6_P_6J*Rv_FPsf3U?y`(tSK2U~2kKZa(1u*F9EV`%mVTWqvHhGu`T#YX#M zX!ZwNY_vayW`D57M*CxE_6J*Rv_FPsf3U?y`(tSK2U~2kKZa(1u*F9EV`%mVTWqvH zhGu`T#YX#MX!ZwNY_vZ{rTwv){4px+kIm$dQE7i{CVz}d`(rctV^rE7o5>%e(*D>? z{uq_^$7b@!sI)&elRrkK{jr(+F)Hnk&E$_!X@6`ce~e1|V>9_?{)kKaV>9_9F71!a+AALG*g*i8Nym-feI z^2fNeKQ@y;#{Um{Z`<9rk*tf(Xa5Rp&svG3Z7BlvW+$24B}gCil1B=c(?-3klLP8lCKOSF~ng2|!m@S65Y6S65XR5=Y5R{sK|Y?g{}>eH<7xYkK|wyu{^Pf2Uu}5pQQr$u?0Hic zjEedmPeZ(oubw^oYdk=bjTZ#HF^X;R|IdDV_E!`ai3j>04-o$2@)3t7*X^tG!_(mE z-T8IrtSzS~!JGDZ`|9wzeH^?cyuZ}_+4@Q<#h|3vSK~*NM2~{;qvY~z-PHxT&~AHs zdw+M=`wRX(*x!?Xw_ECOJi=|?dpn-LyYFxB?fb2R18>{k-`n;7;%zTLa`0frR2ram zi#YaV1$=xq7_4}Yk>E!Sj{p8&@&i6ZU8sRqWVFkly4PT>;O(=&;uZ2lmc3o%VHO*l zjJ)SZ&%J*cb|$ZzK3>1;oE`^97gz0PfAu^m!00`n-o>e>YIb=O)K@_EOUf?uk3EIQ z^8(0GZ#aW2J=a`2K-FPZPuaSA&KPIU`|2ykh@ar`DIRy383jb}umUrkOWFF1Dd$6E) z6XDKcJi&;N%`FwbpL_BpVX3L7rk)cnXhyzHb~US}X}-*iqr&=NJB zkz>(+!DpC^3?M=Ajul*8hF9TK+Gs59P~DGDBac9kEA2f;aafJWd?NzV?fMCT;Z_xo z0J8RL7&Q>lLqb|JZ$pr~nB|fx4g%FzfcJ=?{?Fzn3at|7&D(hRf4m#q#oEWM)VK(0 z8soki-B;?}hmWLGJ|b_LNS`e8Eu&*iEad&+zn&f+UwJai-UV`l<@4t^tY!aP!xgZ| zY-cGezLwE3a|iXvaQ!WMoFdzzNXU8mU>=aT;h{F!+*|_`pzwpB1oDsI9Sjj|j3XriYw|=u4q?yVE!f)k_ zpz+j`PqqvzXMiliAr^YeFfn~q%rJ{xEu$dGPmo=zgCgL59Nr?G?`!>DsWbWSny{^= zz`A*{Hpen*OzVXGvl0DY^vBAb6s%XuKMEey6}Zx^+oqxmv^lzAT2H{8{QGGHSl7k?~x17t6hvt z09j}b@T%xtE-qV4mU@>)TnmFvdWJWhTt%*}lR4!UDME~=Ulss}U8q#)7EMeFAXHAw z)IKg&(`Px%Ygx(GJ|xHmeD*I%(3(MgGMn2QCXgyEXMfAoW5DXC>M81wU<_;P-$*-E zpTJu)E&fK-^@6-5w}YxT_#AOab68zI7DlH8YlCRcKMhET61uwELSKdPr_!->xQYse zUe*@-^fnYkLm)Fy_0;>D47313&{v4AtHD08@008Xt|wptj=C8qn>(*w9vpn_9ZCuL z0>wR*ij1`c>DeghiHQtHSXZDaWwyXw$|lmr_s;d1Vp43G^5ua47Ch#tMw(mrI8jHKYU9JRkyFAoTj-=vK@ksr(&H zzBZW2-^8P2e*q8NnB}2v8*WYz&SuYMHgX0~^nVjg3-tUVf4%Pg$KSm3cc-V`7hjkT z35b#b6NK|w_ZG&7YbeoJ3+&=KMIYwss|rGA{V{z5iA}lnzr=}>5iA3;N;!; z5hhh7J$f7KoT*MO&l$>{WA#ZT1E6PWw$JVtcvW}UjiwM#-Ww=lwTKch{iZkq(G!)p z1Jrr)nb4Nv)k!}Ot)+lYklx)HJOS7A*_&8w0xvcBKqyob+0!hp~on=tWk6g7*623sSCP>D3VOVg0}xRP)ZS0lPi=)F+!S_GZX1kAp z7q;NR*~Rg@)AqVyagJ&#{Eb@VP|b5nc0=B$xExQk^*4o`yK)A=ta<(Jje!PS=LJR# z9k76^R@Qq2IwhIWC1-NT6)?`QwQhfmr%T(;DMHy;r+2t7ukTSH(K!&nu7umP4{O zP88FzY%ND=IEcQ6Rp(`UbY<$1iInR_*o5ozs3@TZAlFzdxfA7~DDLmo7u^WVI!x!* zR}KV3A?VVK@%g)vU=a1!u=iiEDNCP=vMSLBv^EANJW6&BQqF1=Yns6ipN3-(mq9wDFLNw_xgd16p@ciPs{k2*HFs&gbb~72%6UHiX zG=;EQLvaMho$lf5Q#cfqi*;e1M5?=RL0;cQKhgntI2_Wff!#?LYFBn%8x!>x)Zd)d zs+*%W*%RB722Yslxp{^kwHKoRbIHbWP6{w=XKt^R?dhLDiPg>LO?T=>aj>*UDQ=}X zfdzOiyoc=(t?0F$*!DfxD&7W{{TJoc*4xnbkv37GDAv>y?o0`xXr9wpJHah@!}es7 zOxW3)z?5Z>YbAehP7~!8hB>tm6QS?1n2>NRm!~`24UPn_a_dmhu|hHgIt4#2dNg^#Z7ynKFv>QmD&r_USZ&&7zIr3QLJ9Plo;+tji(&MjsMb@Q9kD zL<25nP?SnP=w>Asotl^OoM@8gC5nP&vlpOt)TkE(FU+j0uPgW7yuDXg$F%Oz)?Q&M zfF8VUzn`%;F9pitBSEP>!AqDI(G+|!|M5G)bcy{p|M3Q)%|FE1x3&cJ2D4;08pij; zpQiM*Mux^ZEH-s&iP=^=5b^=}f5n7mW*)!QW}}*G zRhe|hIP#myDeQb}9@wr%|A<%gp5bC7oFMhyM&US}hoNO|!Kd|QFokgnGT}Tr9QML- z{G~U)vn%?fj8{!L)ruqp6Ca~E*@})gu=2v5GG};7r(w}eAJeTs>!bW~>qcLt|+DyD@8H;|Yfa*9b~1HO5q+Oj(Wx1M1WmCd5^`4xC7I8wS$%^kP0 z+164VDNvVKJrIqOfhdgID*#dg)O`rYiaQ}Ml@@{G%{hKTy?#AO66qCJ8*-Y7RnB1ZR5d@21{>T9lfZ@&R1O&5MK|b> z|0lEY^r9!svs@Qsi`!eCUcnpm{hBfJ^m=yrc*eUnf>Z2LPk3+O+85a;Mh`+cqQ>Hu zT*~o6)G8qb#~0_<`b2EK+|j%9Rb9Q~?GNui(GONmOhLAAim!8~t`#s&KqEM!4)mE8 z8&Pb>u7B!p!!t!pd5D6YeubtN zche0+6|l7H;E6Bk1Q6CCjIypVrf=B?M6b{2OL%wW#4vOEY-!!24llIT1#U6m!834} z$?-9I*XA7;c4fd7zr@K%Ohd_hX7DIDhJ*nX#TDO!Bxs@yg~hjAqqAMfB56b?Wtali zJOpiCh@%J+0ZA+zTB80h?pPDHOjO-S6E>6z;9&k~Zfa;VdQP<|F?C{U7&QmW1mn#% zZnF4m6Xc6E>dAuM=wJofSl27!=6BiPXur%kwZ2ea8M)p3P7j>~i?&KFB_@$I9~Q&C zJ{7WbkxX2appWFV<{yySA_YU@UxA*-$AMu|D zzJKs%{O9k%P!MVpo0$a~U;cFc_Tn7j#!DkW3-1?|f;RJ<$AId7u%=U^!yXN~vMip! zf37ShWm=tdPTTO2jE&u--r)(pV?}HAoM3uGT&EUK&4)*DvaznW7(4i0?n=(>*h0sL zqsKp>S6TabarKAj6u0$#{+Ma4tQ=zQWK!#6H{rzxhiO*|=^M+{5ENHBDAMK4ly54h za`DXrB2br*x;HQ{4D1a6$IYtaN7}hhScw>!TZ*19GIjlM`QJk%f7d=1_5W?JF~zS( zvReNm;vezy<@o=--QAr(_5bg|2vz$3&CwFYuU`Cd8h;^v-`J@tA|E1;h2W$V@-JuT zM>rM2{s$M4!Uw|bikw>Zz1MB{58C3kPGmoT7u+Gh;V#G1yf*<`YyDJv8_~-pE{VfF zmkt_Umf`8f5_K7Ql7=95IzHV-pcx{`i+!Mf9k|?kANA$K8Q_hllK?B(ZBH)XfR>}P zvD_KYj^YHKlNBAFB9aqk6VOZctc97%dd)rr3bq?VV%Zt$iU z--bInG||;L$88T=t+h4&v9ZpVn+O2X05Y?S}8@IhOrZ{{vm?FB1Z~ybtsH#5jeKOYmaCN`dWoF-f=M@}NqpV8?grOahezM*NM+D&%~zr2;Jxb! z0@%SO;)_5S!cjGVlMeFJI{$4zthF)#Q(na+CpieMMj)qhCsaducaFg6qqjVtG%(eYp z87K(F5Ni+?zBW(`*G}P=7mV~SUXpuQ2+;zL?NXSHv+^<|9ff8W! zTb9Dx-sX2ku`z6R8}GBgnSxfS91lkUjZ^s!&x51G?sYckjTauir$?}o&^mu;fy9iH zpyPHhqwWU`gwG7=bbrKZA?i_FYdYi@>J&G>Q2S?YI4@0*Wuz}Hm1PD=SqlM93)6OX z_*3v2A$Upvvr%{($<05ELgC3RELlB%Of*y^Uz_)ZiNyTyFBMzKcJL0(5GN2E(FNH4 zh`qwHrpqYIo{CU4%lM+8matZl_$ti0xcp_(fp`q8#46ZMr&vA1p~FJb7kz~zglywu zp8F%za6FsfwofK&KylLspopO<92a<6=%5c#(*Yc4kj6JLF8zL_R~SK*gCRnlrff$i z^Csb-E#pKIah3z<7USwLugW+8y2wl%Gb9mlnruj&OqGUD$)jh{eL(XAhYjH)6oHOP z%@D$3kMZS!=VkBY0(W4pl@2R1Mz$nAMR-+nBfId{<;q|4lhPf!n<`P$ua-+%c@X9R zFbCglU#}X*obLIz9NQY@qdF(ARbpNMy?}b*-E!RLw1j3>9XXuOdYMTx4pl6tSmtR= z>M4d)4H0v+geO-pB%3PQrY7bBYzd11%#Wi&Ml!*QQK<6^#5FJlou)4t^^xuSWKZFD zS=lq)0;+>^!SkP1Tp4TXRnw9~HI%0^s_B%Io?Qo(bKC-Q%NgM5%xn5I)Pj!AMDYnc zteZc9l}$%W2AA`-`EdE@3RXwij^tuY3!gI$8Q{xp(Z+~27NuIW+i0DDb)e5^SLPZ7 z>~eY4{vkL!?0z2CX(Z%U$`v`TV z-*`xk%SSQF_E7Aet2z^t(=jg9rJP_j-_SX-xV982wNT-*w^#f_n4*=`u?O(q3w#^?t{_yz+as%%}+^INKFZt*gH`-xk2n+eS%qGml zQzbC~ITd4jhnQ1AnBq+2rU{``BZE{}vkz38JO*j>RV*O~{m7A!#sop_#D~a#pQtor zEH;S@rW*P$LZ%ovza+QAr6ctkfs{i|Mq_&7Sd-44GAtS&krXJEAtpT zZ;5UwPyc!Q@7IdqE>K6HdwF=&4t&}?{`~e-ShM@Kb8^ks`lq#wf6WK{cWrGg+Y4ih ziQUk*-|9VY4pf<#oFv)+{pezh>mtGA z5Giu`55esTxRl&ZX)sCbR0+K;^VbptPuqvz=g{~Y)E^fvW|N?>gy;l8{@;3OD1Tk` zR}n&)0QF$#Q9pZT{72DfcAtBXus(Au3>W(CE%u4B2BZ4Ln8ZLP4SiH%##66jWY`pU zFK`soKD>O9IfXO~t(W^s&!l&=X&*HRDq45nzPmoY`0;!h1Z?m@dp)x_p9fD1S&!6Q zBOE}Jx`5Hy;rYA6Q}~D!{jQ=gO-67NVLPmyP}E~Ern0RcM0?H^C1naQ@o$b8JK()W zU?rY6(~*ARhlLdS^1hR*t*B zVbq1KsoD5u%UXFGn?c-Ir`wNfc^MgF{Ax6~R4<~!K-~;8xocOmMvmJ|yozujWsh`+ zoP;pNMetMTDEa6M@BdZg&v0I6fG>C0{J1nc04)7o)%M>ACDmgqOl0Ow)UzqJLvc3e za0sw9;%V8FW{a4AYPO`q0{-&%!gS5K9Wpv^tn+)>vVL;H7O^Q!7y+4`cQ!lT%;1+j z(mj?YOe6iI2{Qn*8C%*IGsbrqJ7P4-!_zxTa9d`cEg0%c_V7gfDLc#zu(93qNRy2G zpLP(#JL0pl)kLp=L~zLHJ?HJOjno@JekI9lbZ=`j_Ff|c7#>% zDsN&jxG(-@^oKb68W~*Puq( z_gFZJ^jN4H1b1atg9ph>*vlZcPZfDkn==DWyHGV*fFqUx4w3FN8AjF-iWjKLRvg$v z1ZNw*4sSsio=;&na)Ut10nezo#xJ6jrJ-UDx%=QEq&y~wn#4gJh{t%;c} z*>dSQ_*#snc-@oT4ExQe2e@NH1VE}#O1$)|=wk?iC|ivn=CEZfC`T#YR=`yAFFeCK zO!563-*}QL59^PWog|h5MZ2cR;n8d`iLeJwaV|A{#1@GzT;Zz)IP1%~WlQjpJs)Nh zYKI4wYOsttM`!|-Q56DVZGP8A0;(*aX}S_ELZPxG;)e=le(;-r<A`{E zxfSYD0=kKiHc3sW`T%OiKZE0(Dwg)vgwQJ98*tKf2}>g6O!+DwtKn)gP1Y$%_#j6T zB&3;oibat?N^tIg(m}fm#;+Vt*2MUOyhze z*!p53C&+#1$(&+LDUNCWy1W1KWu^c*ig0+FOfbLq(gr$36fn47AHoO|>&E`>!NH#R zi(mpvkT*P>1qb85II_aV3;S(EH1HX1WTxD}2RH10LqA7Ofcyo`67#hj>gq2s@1$zJ za|e;3W;1ydnu{wgng{>_$$xST#l7?3or{bJAk-H+yk)-S0 zf_wNH9MpkQH%C{7iJ_HDb<_XH;7CZi;%P3@;*rfTR78--ERo1eA7%8^VpGMH%9XBQ zRlcX=yY)-fS(qOM6nS1-R0&gZVASWFxT@>yP@$mAmSdvjXI`Lwp4;c-s6tgrEDE+( zT$!_j7w*YKddo|l#*_p%!ZyN$>G@*Z8>hUa59SG+GnBT)vgWW!H30GvD`#Q}l>JXU z`j9XRNtp|oZspDc^nrDBkCz&^9DW8xb5ucN(c2nOJF3nLZ(y#z52i`bPcpoIyo*tK zm8^_42~(|(J0y64PxJ|qK42%!YKnsK?Qh^`^@?&+%$p1Nn0pEDfM+rhZK$xujK#m!9TRaIP ztB0mkju^uIXmU%_a&Ca!S{nR0F@t!*tt%@==lqtfvxOn(ld;^FvwZ#ws-I>Eyw}PP z@-OLFID=0&c{DIA7#birjE^cjMB5@(9v$OUX#0R?q(|}&Gza5yb8?V_7W*{7KH2Mewsl9%qhGUgS}}|? z*oEmrzN8w{5t(Ck7Y4Unp{gkdj0*{eVkZ7F!pu*7KFby0u2kIQWHrFNA*d|roXa41f?S0jhOHyX&|Nde_S>QWqQAPz2TWIqCJv5cT)GlIs)V*v zcov||lwR`?LtOkFO+=k3omP4&YvUXm<45@IdehZtP?x9zkVe=7)PpG1bb5gWijHz7 zI}jyeuGYLC;S`}ikEH{om{KOO{797e8;CZuvU1k5D$1V6)JE_5$B!Rj@L?wvdH9tJ>9_dzJ7XU``LnI9t*5r(cVhn) z{9oP!h7V_&xcz09mxTi}m;io19HGqa$3mbYWeAK!gNaYFhAC=es*(mMG8f%4Ax7l~ z4S1deOA+=pOJb=~nJ?&{XWh4uMbaPc9>o)=Vij<40r%PWj1+@cP5bj{Wx}W$v8=qD zlA@ld+6fg% zOS?;t6FY(oIqB!QhxvYPj&iWU$uPt3s(P=-(p6%}&ac(MF(xu&Rh4cE7WW1^&vcb# zsD{D`&oRX&jts8sY;i*_NQ>SA>(aK`rB4#DZkQUzm6YyL z3O94gi#h17U`e$l1R$}N^Ri4jMN%BQFu&&Lu`V(o^QVTSwl^rq%9`mMy4-ytoN00r zI_L?O0Q8y(KNrq$PaUY~O|H4AVTHIcJceZatd3PL_eE%@>?N~-+~rkf;G ze))_EAS>LT!o|pNNAzq|nks@MO$kqLzcoyal{s)~3#*PfA ziVm|OfY(_3`iZEkuhl`D8Z)brKA}Hh9rG=%>?f2@qy7~i06fvDA=bExO!AoE)EQ?+Him~OlQ>n7r4a47gu|DKc)qx)oVt>v zE2BEC68O%Nt(d4|aF1%LcWLEumX^;D+qPJ4V^f#)_rk~%+0^V=6rjx0_*wx;$i7)Ts| zrYtNC_?sw-;|ar?@9o-M@{ zRwI?s`q%$!gge@Fia-wH&DF)b%OaqxAf5MSlFY^{&Cwh%oh?<%`t*j%PUdK^-~c-A99=K* z{g_vax~DtV&XCdu{z|Sby45 zlv<52>CEC_G`Ots)y>T(-bNp%{`p@BhoaUmuQCDxNuC{G&|%pOV*uRGw^e2Ws9F8P zg>Y>HXCi&MH*dWFO4(fO8&r7y{RkN;Z{f*!4zp$whX{b<^OEN-Odm(RI9cs8LyzJz zlOf=qC=MaWpcnf8+@X{Pm{SBkmV44Oz-IamVKn>)gR;k6livmIbGTN_{gDbyO)GmZ zvQJ^?W-2?lxip(Y4cpLbfk8X{oWspW!lE?Ap8P|nM=p`xhGOa!YJf(HD*%$X6)2tK*}Pe)7(3e!Y#AS&(`&})uffB*H3A%p@Dy|8ylSA7It^H4-@H+$ zJ=ZuiE-;psNApd&b%!3P6t^vBFg9?0FdQt4eP8iZxL+bxmFp+Q{T$>BaRW1c2h4-e7jY*eL>f0x z@=St4q{33Ql#uZt>W>(g$!x8hvkVh3B8Ems2|3f;7-Af6^h8R^JI7KOrI`o}ry<03 zj|TZ{dZ{!psMPdm^avP)kKxZcf{12C^RssI-_zy71{p`ppCRURq?rOy6bxC1s^JZD zCgN*0iV==YX9jhO@lY#YGWdZHK}d;UB=d_}F&Qyi%&=ES6Uz5rhAlqkU!w{9z~bOw z7&bi|ra3#M4}Kn}qVt0es+|^#)G1lykZI2TphiJ*sYp;Uxz*~$m3~#6NDj#ml_TRQ z3kpU%^UUXVd&m?+6(VYEWCW>`BkGNe^|IA}9?PKt)Zw# z`T6m?vok(t)cssmQT z%9hVUVF+=opgrolNk-}^O6n>5(#v1HJMSD_9JlexS0m0}buUjl*Lcx_MMUya-F*+; zqd1K>GH|*Q3EkW3$x@F%gc(j|5C+3USEx19&QLMS(hz+J(x4lX$_vErc7 zFu85%pzY7-g0ajuj|hTZ3R3iQAm8N3lX66!W3Ipj>A+GDRe8ZchlMK|K!8);<|*|q zu(l!^dkUM;)VnLf)%U7!jB;;E7By^!sn=IyC2VeDolm7a5b9%Kd`vn+@)(>}s(J|5 z@?rlG@CbiHGty48iyMuZ()-Iaf`pfkA>k2NWwOv0nf)<33dU zSzsQ_2ymh~U|qX6U52!h>R>voC*MF^9q`vqOca3Ne)H@1kz(rJBbFk8BZRXwTbN} zo6t}*gGJ}t9|tJt$oQcRRg48_Zh9>Zeeqj<4dLPCyP(MiBMyV znnb6^$wthmKG=hKj&5rY9)|&tXD4v5@-S0a2}D4)JlUIRykrp>zFC-5t;9fo73Whr>Cy%S4oRi+a@Uhga4IU64#&5}4}RYCf59SF81@#PwfIalseCBpz|+kU zqLKJ&4kRI!moBk)Oai4@Q*b97!t|%u&T?AKQ}$4P5+O&vkI|K%0%WWo&C}#(9~oAh zXEQb64G=zKE_L7F)encK@7nNCy@!hmGLWrruqhzUW!d@8+bu#fY)ZtXU>PKx;<)aw zfBWrRZvZEk{}hm!YmSm!6>XsG2J;1FD1+D_;cK(Q$z(3uL}NVKKG19ThL&zj6g3i- z^Nn?kS_Z9O!A|2qNP8^yc!+HlzE*#)`z*kw8wi!;1G8kaq1vE%YhP%1dea543pH^v zghff9M&HxMqN^@xEV%x&%u;TCX`@XK#Nad6*?N(3pcB#K?mq5e%o3}xsIUaXdCii+ zt)O}ZrYO1mG)#+{a-#~{A`_y_rpAjrOkb|?UIn@z`8i6s~hL?=R66T_#HC5K+KAuZc;gnZ>1-Nw% zODuQl0rl1XF0RJYpFy0Uu~qWe)~#$vjj{Ja(Qbj-EJ`nQS$uc?e&V3j?@?@?Co;-u7)--k?~9^v zUy0nR1p({eT5;aZ%EEcNI=pHJ?H`~uq@pI3i2k;(+;_z-k=Dy>x5!<^9uVhGjzb|F z_&PZAx}J!_M^#qW+FeAaBoi1d;)U0}e zc7gp64rka0D(6$Wu9?=X$;QyzSpPyHgDSt$;AiuvF$`(N&0|qiP^uqh?p!CB2ZhfC zjoL~?N_2=n8oabH!OH*EtFqxW>r#?v4?%(qo{OgHhT5{AtmRS?sm2J>(4yDD-05|HzwBFuC00s-mZwQ@L3} zjo3R=+Yb0j)h>Ns4-i6Ae1t6`)0?wyiwLQ6X`ZwpQ$HJeMiPS#jx!S;L`ZT#GNNuq z=`j=t0bDd>NO^&dIZyzCvZULl3<RH3NfOsIB30SDh=VD*QzD#i8o>G3-XQZJ``M-LDD1nrT7B{X$-EF zP|WPFLmcwzmvcB}eVVUfdzt-B_Dw_i&FC6 z&q)o%6irYacbqrF&9P%_Men9TPY$C+1hs^#nzKYSi=j|&6iP-jEt6d~)XG9PP&<6( z%9AS}WOp1OP#m`RyGtPrYuM=9os7tn#+)OqxQbA|^;&!T`n4v0@wFy5$PU*ZPV5&T zi?WB7&CIpq2&HW9xIC)fwFWY%^sxdLk3JH$*lK08DwkV)T&2qi#gu3ypZjxzMi=9M z;bKElM3WJ~tK)xh+w1;9{`bzG`QM*All8s_yDo(PcCWAAw*S5)062g7$Ati6>ATWL z^2pttPH;qz^@}A}83n}!0vwE`1M;6@kx*&9bihw;Z)O9jG+v57GWHZfLQALYC`7<{S)WYu^=*ywa!Y=DaHjZGio!9xhzZ2Y}iU2C2byF{0r* zXJ;2z*Px?%Rdihq2*Vbp7Yx@~7audxjfr&f9O` zcjvLh=H4%QGSJRR2fMC2*m2!K%XJ67;|{hR!*`9p>j`kpL9Ru>wG8h&=AQ${@LxKH z|H?6Z-?;$0mSEqt2>Y&O*mo_&zH2G=U5l~rT8@3!g6zAN7^mTO6Fxt8RX zYe{aomgJUeNp8887^9oLe)<64q;TubtfYf0X5Ey+8sC3(lSB=5MEJu4_r&buG!et|fWbwIuJlmgHU6lDz9$l6PH8@~&%1-g7OG=l3%)(X>~FgkW`EnYH2d4G#o6C>Ezkb8Yk~H+T}!mT z?OLS$ZRaxmJA`{VmudF_U+yEm+=qO*kNI*R^yNP4%YE3F^SCeHdEl4tJo3wT9{S}w zkNxtU2Y>m_qrZIT;a|S<_%Gji0GRJQ0?cFyDC~ znD0Ci%y%9N<~xrC^PLBS`Oc%keCOd{zVmo6-+4fo?>r*RcODYvJC6zTod<>a&ZEM7 z=V4*K^SChId0?3DJTlC89vbF5j}7yk2Z#C2qr-gX;bFe>_%PpjfSB(*Ld#C+#rV!rb@G2eNhnD0DN%y%9t<~xrS^PLBa`Oc%oeCOd}zVmo76yutMcOE+CJC7anod=Kk&ZEbC z=iy_%^Y}5}c>tO3Jc7)39zy0jk0JA&2a);CqsV;cVPwAZII@=WII`BZ>zI?4^Ek4W z^Ek4W^Ek4W^Ek4W^Ek4W^Ek4W^Ek4W^Ek4W^Ek4W?_8#x$C0(1$C0(1$C0(1$C0(1 z$C0(1$C0(1$C0(1$C0(1$C0(1$C0(1$C0(1$C0(1$C0(1$C0(1$C0(1$C0(1$C0(1 z$C062yklQw%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G z%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu7G%Xu6bZnk&5PCJhyYdMc2YdMc2YdMc2 zYdMc2!(D%tyriy%;+0q-ZdIA!dJkLfT!d)r-C2Zao9Vj<(KfSp5u$A-?;=Fo%-uzZ z&2x`!X6_=6wwbt#5N$JW7a>}^NGm`#FE!Rq&-iUwf=QTZ>p)yRs@kHox{*JF6*T`x z(JHWPe(kZ&H><$1`L)M7qpSkU=GPwUoUuhL?W>Jf!EAo@v9Bmzg=h2YkA2nfDm^2HgD3|*Tb&D(}5g0h(oZia$Plc2kVi8Pz3wh z)fHoR5R717pSlW92XbVcJ5+_I139wJ530h`!Fpt!15|;hgP;WadeK#QI#`b!#3k5Q zfvy_6gZ+_phE5fp4)#aZSvXaAI#`b!geTb7Xs#H$g8&8lI?PpgI#`dab7ZRUbg&*d zh*GexpjfP{F<~a>dvkgeutAKCZ&kfgD-aH?G3dfgD-aFs{PWfn8@^r?>)7 z2O$ggwTP?mbYRyxh+43(FkCfu2X>ux72zs89oTi&m4d7Abg&*d2wt$S|64J32jL6$ zHGZq`bg&*-*YT~w)4_UVU8}bWPY3Ifbv@n+JRO8F*w@^x!qdTeqSy#lZ!n67H$iC|BBA)hjZmVE6FGu#ZY^(5Wem%0US6hW=^Xrj)P1-6v zn_rLY>&{l-=^&oLzP4-?o(|;5K|F(f#n`H`H@_a)SB0&@v-$PNzVd4oo(|R{2k{K{ z^;|2)Ze78(3TE^1b@o+StMGInN7j{CtMGInN7mI=tMGInM-Ji{>}#l2jNL&zgMFRU zDm)$7bq?Yg?5m$vjopD9IViDUU(>W=><;1??CX|R;pxDxa}dv9UyZbC><-o=2k{K{ zHAbt(?qGl9pxT0c<2wm0ZtW16joy!vhf!@HyM8+`{;f=eXNZv6QgcPUq#xC1s`n4pGckm=38G1T97fT@Fdo$>ge^bY^78a?90g+(bH1<7 zPqXPDcv*{|I>&2Ahv!G_)6@2GT>vMY^G^4zMSF-+z4-(7|JA)XKNdD_PbNt{HUgN- z_W5z={7qdFU?IPXYA)XB{W|(M?Ogmg3CG~3x;R`lUMjXR6dG`y`1C8&#f z@MApcClBdSGD?$S)VZh)DCk_ghFVlhUB5n<@TM0`$Ae*bn+ElV6@)jo&8MvLejiQm zlD-|Ne&1>!Sgxaq9fXhZ)Cxdsi@|Vc)&Izjf-vvx#y=V*+*UwFtcEeMWa;#A)Qgi~ zG8<841kneu1`MKPY=zZBXvZBihOFpvlO$H`aWfkX>?Gu777zQ-Tw!WA5P+G+c1)sn z-WG}v=CKtud*O(hFS6k&Yf35rx6u@u=iVBVUSvrUG6sDO)|iOH!WJ7iJciLnD_oM% zG_+*YUNU~P!hz;@8Vn}MeUQwiVu#zn>qk~>kLKZYYPHS+L^QRM8$hsScw07Q`(!YH z`^IWAVmnI4AK=BZLLlkcG`F_7qJLU-Z)va%;-M8E4u(l+HU9vRO#1N$8zKyn$-N~O z={TI;+2C+%MMkV&cAdlMv{OmIfFR7xj1qXor#IfIdW)bDCJ$Qt>!>up0lwUbIqp;|lIH zD|EmdR_!faS@9IkUQ5d+{%AL)cE;Thr-&B*1vI~fQFRx?_hbWRXzgWyk%}WUD%ZDs(?lbe4u0Kv|Lu{|xoS%Do3*)0yqB)+wGWfO{>ZW@QZ_c+nCnNINu z!lgE`^2*!#0LF=3_jcyjcoKmOR%HC=EF9YEm5Jppabmekq%+BKiI_zFnccC5hG3^& zCh!vToZSA@3Y=kO{Y-FxI7uTbKkKB4-TTCT_fA)#<xX3&E^5%nmTz z$j50g8_|M?bwJ1;!pU@osDlPT8-o_aNBpsc$3Uan6r6m#GEiHm3XZ54q&10d5oSHH zij;VW!uJf;wm+69q)B9@4ei!826y6=qh4+a!gU$eDURg&e1o9Q_(T0b+i)1)jx2i5 zLJ4a}$TIWk@jjWQ7FbMfDb+R*QWZJR-MIxGI-+qfo6fMuy+Lxvdk9T!(%cN+bG@(M zKEWyt{;HQyM*dW5(j=OG2#59Q(vJ|thX@ubk=lb9M4?cXn>>WB)`t^E2#yqfv4dg3 zZi9FP!TZ&QHvpm^&>}g zJT5P_;qc5hww<0hso#GZOc~)FjgV7i!y@`NDt`r0$wJ=Ufo7Nqes z#<5TzaYp>5KBNbxIrUYDd2J|mlQM%Lr0=C`?(JCbezju-8KpS#7K(MY(qCe(fcd2MhfK=ax)dxPiu~ zjl+PpC=RW9cgW_jhQd>qnu*e!%Uf>!7#Jv#OtrBY#$o*_g$?-tf#}zk1*(fteNs+p z>s{==k>6Z@>e|31K>mL&Qmb(G`a;NPnd94ACAqu)wcx^ZaqmJYGZ9e zm1_f-AzO2T9pJ=Xo6M#OY@qwNwq1NvcNA2D@YU8cclBfb~f_+)4U*Crqnf3s-_ly|3gO$$jQPLuk6LtAqmdC17ytxpVwebsLb8!VaB zD-?uPw09%^(%Ri4W59}wBM(doUD* zeZ&r-q>@!&31Lj`aiW;QVOpOSAL3qZMaP0UwR^UyUrj!YS@{fc4-tY=csyJmkz1U$ z+qDjCTzCDL2w=gjkHaLWzvJu2X)l~ubk0gMNX50Xr-8(H7(Ox@xi%HvW202NaZ+D| zP*4mfS9hq6T%U_97!Z-feUzN4&j7V0Cqj-RJhmu}Q+wbMgjj-7gScONXNasadOdAT zUx}frPYlZlEQ>=+G=;yzcK<;rc1{99u-gLx0(PZ!g%~_Ac^oG=MXnE__VG2Ui6`}) z8&c1-Hr6bLdLO2>RVSQPR!&>CN6>HmO3=z%W~2Qxe_L%LexxCV{0}UWHtFT$waNA)AP26a1!-yuc?$J(13OTWdyrkwjAypM+#n2!*4SDS9Nr+d(E z)$g?a@zMYx*tPq$h}jmoRQtx-D}=2xUE0@nu&}kw(Hz?2Sj0lj0s!`m7L$k87PLnm zL;b#KRmV){OP`4b|v{&g;77&;?Z_!-Bl4`e)YuiV+!{o-| zg!C546i5h1QPA4B6GCl;TYIrk(NA@e)|qi>^w0VbP?gu_bo>rsJN3;J)w({}XQ^yusRuwN8|dA6;#1y^=T%uKtI-A4>Xu@Yy$x$Zsfq*0tU9; zwNJO0SA}YKR%h-4!-gADb9q;7eoA48*7nlHsL~6O!g+E1{u6aChXnv!w1c>ZV2GVT zeYr|);-grYto8uVwmu76xSoLY#Avvk)7jJo;G29%(@TR^tvADclx~?C&0B z0XqzosEsw+>JrEndq=xa4SLdH8P`6j#!2tA{?ah4eS{`S5A`W;l`=6*ZdG}d+7oY4 znh2=@C}kd?Fi`y?gnQJqu;4hkaii|o@57Il|)Z?(h#^-~*di}qFu7YBDXm&D^P^U&(}m;h{Yqt#{VwRu18O^_7Y zOX{1&h;V_@7ZCLYARgf|#oB|YAK`41jiicfSs*<_JecKKlER@r-4SA3UvJjW2(qXi zhhy^k2WR(>z51p}?QQyqsmpcw1jh)h6`ays!d2>*0^prg^x#+>#uA9t$_)TE zyCvPc{_I)gl3M0i(>H-25{qmzfnXO>Uq2KW?s5ySM*Sn{(X4(Un9=P1rvA|zu3)RH zvCtIfE45q8t@C!RgMNKioG!1QPHH>jaHt{0QEf`DBsiQgmj=|P1qwt2Hm>Ic>rHJd zAQ{`#9ZBHnuRU(GwP1Xd-m(hRqhsXWqWrvN-C3*3W$M1a+Jv7W@vQb?1ri5tak5mO z;ZWgzD3y(C<8KqIP}_evM(SYgCEcz(?HEDi4`7(O1ha|YM_Zgk))w+{6xGi0f`q4V z`XUqpsg||Jo&&MDN`Sc3&;B3Bwwaz|3koE|xL5l~)grkOO>3Xdxt;TQT zwIBj4u2xsT(7v{Cs;hb@K?2)wg5dD_IH>#E>bD=--07e%3%2Op;<6*$FKj`7lrpIw zX2>Jn$%p!0KdU2RX>3u7!>*38zLmNDl&edf>$b1GG^F9Mw(BRA(#^KkjX=RcRqaK} z?r;&UXEe?VcdXW)1dBaRincbZR*Fh8te#k(5u#!3e2mnh@`FXybaU)ff4-osCc#~j z%1yzRU{Z_qDus1#QB)3rH8v4ZXg;<|IV2hIzG_@??2C3_z_?<;c2_RU^fVp;}_9Otntp1uiMY3>xyJw1xW_`r_ z7L|YCjIA$iXlp?+Wb0Z51@NL^ToX}ydyDo1o9i6W-lFuVYHd~Gnjq?*+TiL(o>A6j zyGNunk^;~h4HZG9W6a|CZdyNs8=A+WOowV6#3@y%*zUEdI<+XEH@!i)lSRN3+A|BJ zcIg4Ohi|?9{SE*yvIvx!;=+!)+>D$;_)F@WjW|%NozQ5LNU5%uvCslm_XSdWTC6=% z390ssq3`KzTtEL~TK|e>vDB>Zi?g=(004n?^&JIhKT55*ep0{MCbH2-JsXL8u&5b6 zt$($HO*onaCrJUa+Gf-A*V+Q}D~fH@KFpZ@YEw*SR^O+93)xXIuI`{`BgjZfcbrXK zSGW~xBY<19kg#ujlXbyZrOim=+8jrBwuMLQLo&p5zIDgVOT`0=JNe{ta)bpA5YDO7 z9qXeq!PV*YZ=WJ6mJwzTHx`*^4;DUcwzaq`^hVRQwaZ8K)Z1A90v`rT!THUCYc&gQr&(~j&4Sx& z7TkWb;0~Gv_p(`VubKttH!T(o%Z1;tVE7G7hTpJg_zla3->`7_4NHgLuz2_l%SWqW z`DisPAFYPvqt&o{v>KL=R>ShqYFIv64a-NXVfkn^EFU`!%g0W`^0CvfeC#wVA3F`p z$4@+MNI}OXnPQ&uC+pv7>HY^{z4a>)F!}77)uzc(`EFZfK%g1iR^0C{n zeC#$XAA1eU$6mwovDdJC>@_SOdkxFSUc>US*RXu-H7p-{4a>(~!}77;uzc({EFb#~ z%g27h^0D8reC#(YANvi<$9}`|vEQ(K>^CeQ2Mx=|LBsNK(6D?QG%Ozn4a>(t!}4*^ zuzVaeEFT9A%f~^(^6|1^`FPo|e7tN}K3+B~A1@o0kCzS0$IFK0<7LD0@v>q0c-gRg zylPlJUNtNquNsz*R}IU@tA^#{Rm1Y}s$uzf)v$cLYFIvAH7p7LgH^XEG7PS!(!rZH!LUqcEf_=Z#OL|%^#&VEh&v3rZ;|?-uQ8Pe$$8Qe$%Jwe$&V5e$(gbe$xl*e$yxGe$z+mesgE*>Y|yS{nkrw z)5Cw8imA%rzH$S1XWJ26-x1uFBe*+`;O;ttyXOe*z9YB?j^H|yw;f0Fw(CgVb{)yv zt|NKdbtG@Qj^u6Ek-Y6XlDAz)^0wujRFCEF-OGond(viHqbR=)D9Ld`&NAmW{k-WWfByX=A$=fSO^7hJ+yuETHZ?7E5 z+bcKn*4lO>Z>?=N^48jRBX6y3H}clnb|Y`CZ8!4P+IAyvt!+2**4lO>Z>?=d^5#2| zH{X%G`HtkxcO-AVBYE>3$(!#;-h4;$<~x!%-;unv9LZbDk-W7W$y>{jytN$3Tg#EW zwH(P?%aOb}iZ5xk9Lbxb_>$I+BYE3#ByT&8i5^YmNzdFD&^!Gfpiz% zPXiR|y${F1DCtKQKs)Ex?W@Z|DO23i6ivo2Jn5r}VfPY1{5ZumneU@fa3A9WGj*%T zZ_oaU>)_&n=Y92+hjOaN?bq+#Jj3+0M}04xO_RbL_Ap9LrV16%;+1wn5-oTdE# zGXuwf1BCwoI2qab;c4*Z^x`%Cxp@6w?W5~%aMC&N1aI2s?W@D<_Ax%|TnBFn`!9d~ zkOEs@2@8zE;aB6w9&gd@8NI~4KyGDq#y}iAU_4hCM_Wnl}}cDi_Zpw74JDHIN|l6ycJ*m zk9q|O8&SlHB6QsD9$hH`3f?~ZD_-L!mQ`OYN-0!8G#Po%kDhz~(vm9DZ2Efru5)@E z99>+spZ(SIaNAWf@t#lb;?%ptJ;Tw&n?(Pd;r^SH-R2*A3YX^@NM3?K^ikn?)|;X) zmUT9jn}fIQ!{gEuZv*#)j{{uKqY6NqFZFZW_gDG69}TOYC%ww&yxl;(fN*)}4a5lg z{P;JI2tP!8pszZ%WHB~PkuQ6<0of_YrMbo>a|KlVa19HH3 z`1T2^6RLPld6s7WPc8XBN>be*v#9@*n|s`mko1Ch6i*vj0iYLG>HnR5IJy1ZedIFl z?fKYPLjUjYZ~v+P-OP9yYL))Kczt$wdU|oBt)xv`+Md(9h)YU1 z8%{l?kUio35LYIDnPOGJmcpYP#NiN^$xW0|Y8J%n!?kge4AU3h$93<2$wIRxPsZN* z+S&}d|79@c^#SU$tr@Ow`MI^R_Hq4}b!1hwHh4i@GFTfw-beSr4P@>;zFuF?34Xl> z+Z@2Lrm-{r{v!VsNlO2AOISvI~2zjm>cX1;QvQ zY_EIVB@D6ISPIwQuLYRYSDn0kLMN?V>rV0x{rxBOxYKI3$GrR2?i0G--FDc0j>}H# zDY(>QoxGQq;8-);Ab$P_NRj^mT=GA5pTc^3_N%O?{}E~zZC4h)6Q9kKC;8h4tI}ctDbq* zI%k&`SMZ+YR!70@mcRVt48gy~quy`^*Wq*R%A!TJb@!a&tA51wcs7HF5+j`Z_%2NE z3yg$) z$uNE&d0z(qNR!c*MZPj{H;UZ##&Fl+bUMj!_e#ircGtKuFXGj_pV@r{cfRn7vq?mr zX|G?PmaG7mz_Kf~j%!~aCixnhyc@+Rnx#{3O>HUq*xV89Aimw|t#6sB&)`C=p%T0e zZUOO&^66=i_Xz|CH*`nP1#&5-7viwvb_7P6CSC~sQL=g*4Yshm1FPeo7+Uq?mT(fL z{hAI>{%_QqME3whJrMTpJPxA>AfSR-Tj#euY)~W3-T)!DA@cb@;K=sE8{DAmMYv`2 zQNe)elPMNGc-WkycnJM?773u4{Q!U?E`dFo;GQ3jWi0Wwlwd-wdW0>;OB62jhe z77kT2FBy1i16*RP^qA3R*(|JU?1@{Njhw(PM#D!Ki??C#(Octz)iBx|L+`7-|M~(6 zQx7WJ3;R$S*?<5h#7R~{P}q24Fp$XESQP#}{0lK|m(E=YxO6ZXd29Cp$>x2x>20qU zAz^`F4~nnB6yQ=)=RU+QN~_B1QW`|gOvYW&$87u>6Q5B*YgcNj^%5vWvcf3YJ4cKG z4ilX0^}X3xpaR1!oDTGZjLX(?F(PT_R#0yvTTazn{+1IrRZlRpmtpUHcnfY&hl$?i zrVO9=t{Nuh?8fI||^7La`$9rU=> zS&O$IJcerw3}GJ8GF*;@aXc^>9&^=5IJKBF2wHHS;G75481<=>h^B#|1|XZMDGrN2 zM58aEZAAFMUpG+%zSKH}E#)Mq#Ha?)p&kglYoZH1EHs&f&gn_fcXXOjp_1>wbp{Tp zi{nb15RDTYD`VL%(O5`4kHrfLCdQoc`kGREVx$Epz(-*M7KY31$2$y)6q=s{gMyGW zm$g1Fb_N=X4Crc_YLu|l@C=9p&cD8SG++wj*J}QS2pUH?h$mnmSq-p_CFOC%Gg1u1 zg`xt6>RHiNdc~m@=_dr6fneEkl?+Vt&{I2LP!vlj_-if2dH~VAQY)$uXiH1j)`E)9 zmB%%3Mh6fR+PH$U!lZ&Huy#_PrQU2r6xq|zAVg&Ke1ggupJgmYZ3LDD?;Q0$rejDMJ?XMBE;~A*y1_daL?BN_}g-YoInMT+c zRBHM1lLjy#dhfq6=bvv6e`p8Crx%y)bD<|e^oKZt9Sw%zEtVV=l_KwQLMjsAqVJJD z;Kiau-qvEGUW@sX&BB6(gHzhO2@bOZ9`FWY>de_NV0>jS4ihL2T+WQI)j+v&64N0# zOdmb}Cf#LI(vQR25iHQShYtq{{zLms??>gIGntfALeSfhjL-|XvM)X6LYKq>qo7Gm zW#5~DbKLw|Ne=-Kj?4;s(o}Cm~Z$ZKPP}%P*Wb9oF2aEW{|{nI8-jgPke29 zlb9v8(`OJ0u7FjZ!mR=~!6it0r`Y!k9z>`fHH7&uyrR#19=u`C%;%!~EZt>Z%K7$1 z2d1##-@KwzlSJDRt_14m?@mvRKa(%`v!%EaZ!dqketU&@3iSNL;c4eMFn=B|v=J6U zlkMW0-->@Io6)cdK3zachk^Xf%UrBPCj$b1E0<-2n9TVkj^I?79ASrowV@-rq3DMJ zpDT}qX^q)3I~UB0mLF_{&jDQtP=`CVHTIHk{Y%DW-tH<&li`Qx9H!}yxGY{8BoGzc&|(Df*K2(T9} zAe8IW0$$wBD(XHBsY)Oi+~>fk;*%$}H>6zw;5Ar{T=FEAD^C$g0S^IBpcVI>V9i+# zsB)@1=^_+kKyYPPA_}=PDyK15y-&>`h_BFvO>xT2$VBQzC=$KxKtTTvfJ=BV4lL*sA5p;%t6Nd4J9fTn+M!j zBq<>+4A3wRgsh^!ym`gQKfSJ0Yf)0I(Cso|l-Vg-OD7js->)FyRm2JVv;$q6xP_Tl z2H{3w>LFU%nXY(^i&&aXqj8vM?nN+6m}-YfQIaXb-%tgVeqcKvc8@xpC4TlIfMgQi zeii`Qm-)d8_Ga+e?Z9<&e}lLjwF$*`B$JQE()prZ#%8$5+2)WONeJx-zG zn_)x-l>?|lME^ZReWlQgv!gY1k>cF&eHkEU0;e!E$HHMR9LJw^s3jI#);Xk7G3Weo zl(%7AyA%^GC7+WRU`{Bx+tjHD;7p#z7Y;u8^7lA){U6iE;`u*>M<0VWh2*Es|MyU= zZ|?m6VB7!m{J$ysuYyx`(BJ*(?E3KaX?w|0fARGnci3-KH`F0l3@ic+eAnA_agFOB z031kd-Q{8S(P(z>sbEX-%;;r|W;b2@uenQp;T^%rnd;xwf~fuwV+DP^qgR^BFC2{$ zSQ%&u|L(y15-SP7!*ev9A(tl5c1iKGFL?k5muCDS+A`n{Gbo;ff%2G-k38tyeKm9yrn!qaftXmtZOBadA%p;aUh^rz{g&bZcSFd`Kqm;Tue+JP6~VF*gA+ zDubf9gfH8d-abl%ape7I8pgvEM+fwb(N6&C$Abafqp-*HFbo(E-a|Ned3gD#eOO*Ml5G(DP7laeXEc!5F*9NkkE$_UObC57~*K77vq#w{w6d05Kdmh zq1{(R5&n?XvGDh0I0^6NZ=`0>Gb4C|F|fvvPVm{lY$3;Rp`1iG6JU%+k#o+Iqv9*^ zdk6}rIQ*G^fEk?RpCFUJ(g304#55 zGMn^p4wdxOi6ec5lY8|$mO3-Ul0%RN!w?xiqeRUV|AreZn19wwh5;PA@OhWND?V4a zbp;C;s?sr+Z|w`~UL6GpG(WuVT$~3dR~Ki&VfT7%WI}q^I3wl8>+_x>$o`hX48%aw z&F|D-MR$`nN4N4(m~ZnZRo~|$G2iD;s=g<*{0q}&?kQYX+}Xf!t8A;qF?#108C3w} zUko$M|1Tyl?7o7IdE%rF2Eic0jtI=NXk=4|Zg}Dp_b{d24bE^xiK!@fMki#4s}(2% zwqY^_8^Fv1o`CYuXaOLtE#Z(8>FLBEm!HrYVYG<`eFmOur5fxYF03gF7?ynJHFve!> z8jB20dl2A2J%f$YGJ=$zZ|J{p*tmcc)wv#12^UBL4r@wa$T-lPUz6{U5P>O^^$+L# zD(a*Xj`F9J00GVo4v4vx!BHb3GJqEIlE<6hVZs81Vulr!9A1wqh$Gkm8kT_@oQ(n@ z^^qJoZh7ycv4r$JjYytIJ;sNivqu~wP7&+_Mk*a;KOK_r??m$&Yb6?o)IWqo8kX`qtm8N+I_D{=i0PT~-IcWokq z#gziD5Vc4gta{LxH|Qs5D;&hm@qzHRAzK*(^d_PRAOK)uTQU=GOk0XUuS*8dyNicV zD5G+7d1@r867N3pfW=%+zhSr9iU6-7tU?0!#WY402)-I(U|{xz7in(@3~z9UzA=UEK0(D zvg7X(YM7F6a+?~&mdu{{s%mg}2Vuqz3QC9E z0LsaYxQi6pkYCIC@zm7LYxD>j3_nq+CzTcxY0n6(a2+cmkot zhQq@r_b;qy05kZ5bv>lF;PYVQ2|fs$Q@LWV9PVFq3{R%p%Qw%D75BZQT*jov@c!PQ&bTxmiARSX{AWTv0tw;Y{p zC#;&Hnc5k%$rAwN+t7Ro!ZX>1FYUFwuT%<z~293)RTZw zIb!oR=0{Hu`L0*tk>juu#Qbhf3>`6tSSkZ2l-F%;ht!Dxj4^$@Rw{szeU(+d(9VVY zg&caUB}f&a8n<*A-N%Q0J0nCucOs0@eP#xomd z^}NFNS0;XbL^?-~a>u6#*qx2n=8z-FsMHWydZ81gzEx&u7u#PRULBr!Yprj;^_lgre$ZZ!9D13FO&Kh~NxbhDFplQ-g)z=YJ!b#!voRt=F3p8c>RwgEw5p&|o?K58X} zvIc15xpKq=s1p7KpFIJ~5!0%M(WLe_;&BmU0Xt1>GEnbko&@TAPk&0h$z|-Vg+?8k1j@QH)s;Zpl=)=wFrm6T#o2TsZjmev4o_h#g_24;&;2 zuX0C+J-);b;Mf6m_fpT_+4T3fo)u=LdxVJM;Ns+@+rGxN;Tb{H^RvV5_lhDKIYkB~ zp^M}(!pmA1dhJr5`q|&}RLgsur`=B0Mnn}^{2HW8{a)UME{bIUXuVhou#et}6q}#2kEL34~e_Pc;L#>ck-AN2kP-K_ z+duB~U&sIOc#x8#m%yaCFSKq6F5x_#cE>ny#zFe8 zvU*FG%-DxlP0pa8BTVGo6T+1k!B8pg|d?yn5p;~NhZLvd;kbVkuB+5>Ddy9R0idqBrEGQr`Na8U=;gYETHO+EK zh=ubRn-&_=h|U=iP@!xIVuF4|Oi>73c#qI$)RUyoO?i~zRg!IsIX2odqc*y`3Y*HY zS5pk}GoPqmu}Eb#cvQVmb}DA2^T~V(?w+cDie%@GQKiPf-+hy$%y6h@IA$W(tmU|< z|Kaje6*PV){f~;zxLm~4|J=p}CaC|pz4K@Nr(8)p{0)M8!iX4FU}s#IMCh5*JnV?E z?M94T8cn_m)BZ5%ZH)`Wh24K==;h>M~K0#e!=s$W%ER53qp0P8?uHN7W-u{aqKv&Zh0J`>f$o) z=gX>5<2j;Cs$c22+NCd-y!fyJi}*ujqxL_Sx52wA#M>Wxh)H^jK<**WX)8pHk-bY_ zs3XhD*C(isGC2mOONp6%QPu4-304*(?7klulu+ICC*<46|20Ni37{C&T>XJ6&RK=` zn`wLbi21U*(W*9;O*#Wi%#t_NkL9S8uE!Oxt6$;FeVIUXRNVReDj~M2N+?(Y61FF7 zy5(TyeHE=b>3OoMrM#|gxT3$b63zcg7zzOkyz=d7_bwi+Kt;c=!sRr)zv+k7P<4p@ z(wEgQknp^Gc+3y3ZnlbBPnU4(>8deTYuvMNdWLlJU zC$G;?p$Lq&`~xhKOZ)BO(htOcR4WPr-!)9U0tiA{mcL!D$pn6N!(_#DpW$WYfK;M) z<*U`R!?#t<&aszV`l7#d=|E9035=|6noL(MWRU(PYZ=}^9WB)<>UHHPRw)!}o2o{u zEUP69g7R9HC|Js-F+z zC4vRVH4nwBTk%|UIYGMex@ufVF<#LOFPAjJ-BU5*iK^aSPacWtvUkez+d+Y)B^K{B8vhVZu2(|G2bHkjq2$+c+Q{3g;lEo+Qh8h1j8zMlY!ZG%NO3iF zmhQ-YilbqFsbM2;D+e6}jExL}dE*7oan(%4<9jYd0o;~ptxKlG3vshI$GsWB&2g5aIXq*FNB+T;mhFy0zH_?)qNG>3Fk(~U=*l! zD4s=d#aPeEA;2{&w-dq}y+9(ogB{EalLgGDy6|jHCT4;O5(u7LoB>2!u%OQZg3)kc zvhZl}vr-!2JT`?`(K*sY9~Z78!y~4_tk`mr6bRo`bg5Dx@dTN$DuIzZ(Y~q}4*>oc6ePKFv-pPE= zD8zHO@NJ4JON(=m(z2Xyk1)Cg@-kqyawzFct{a_)F%1_cSEVZ~e#j(-g?V6EnuSlX z@K!xzN&&-jE8n3?=Q2x>iZM{{7yIWsE>_iEAZaSE7h29}v@Ka0QB!9(Q@T z02@qMp*Wa{ia=C@X`ycC=hWCKobm9O@_$5xo5RDi{~v%}JpWa<(c##>u{(f15&yG) zfO~Vx^?wdptv~Dk{4U&8b&`Ae(^>mWD$Fho0W5y-$3*~r`sT;%mPZ_VF>x5X2GrjH zZ|e&kCGWKqfg%|tXHbM!;lr7`G8>^>Wn$HE92{XLJ?lHT0+PiS`)l^ZI+ zOB&|gNtTTHR{e|c5)+OUqy}fT)V*ngPuI=v9$+5Be6!n zLSxzMyQijs2K5MNmIh0#$G_KpKi`PVtgPzl2FV(Ge)m{scPw?~Lu6!RWMn+PDy@)b z@w4alt-AWk?oYs;{}Q0ef6|QPFA469cwau>*eusJ_LMDZ&7FX1KjD%e$~d3N3kc-o zxHeR~=XLpVcn4(HJG>9JWwAFIgNfSFmbr-5r@3`=s^ri+KTtJ$0_ADq4C(1NUxeW} z{gxv>{phG~LMg(#JLqBUuW$-)?LYtXk@Pu*7oh6pvbhmyRyX8;lmg90XLAXYRZzTOK9!c^K=h&LWQoF-|Od3 z(=Urgq;^3sT9M7g%l_MRupwlkz@iWmH?dv;@RM= z4q_X8t-v|D>z!M`N=NCvz#1L=tv>Qc04pa_LcoJ0u|hhR{z{9`gB;Rw4y<_##blCB zCR?m^pY1=d_(tgulZsB{rFwoutUHUZ27aF#TN}NO zwYA^LI^bp3D@l5mj-@3qNSM-~JkNgZBeCuMZ+QD?-rxW7FSoq^A3u7W$Nwy={_o@E zKm5Oc?bhC@KhoBU@&a*E7A)~AbUq>hA#rSj%VXu-!@*)*%Q@UTJvsKW$1|Tn2 zxaUZ5>l>n!1qqZO7+Tpr+~(LS%jFdxxf)KxsEU||y&Rk<+vN5T53p&I#mFs`VmuyQ5I)J5PCdCruPnEHsq zYJ~}n6<3RHM~fv{hUNHC$p1w|E90uX>yT8-^!Wb!?~4xg_~s}N$RdwM40~^Ozk9&n zKmYflSX1MnEkeWSS4U%-i?l>P4M*2w(jANr$gZr^P_6r})kbirq^4XoL}D43Ow{QC z8+kGgRSSu+lmhC0%FB_qj}~uf7{?J3WgHh?$+?50oe(ze-z?72M6J>u`r!zcql&V_ zI*j5yU$baiJyTN-%?VK^kVwd;DZG@!TSaZ?1~{ooV4TeE z`nWpHqY{#1aw?JDnj?1xwZhu;pL5ccDCcbsL(v&5o-DEetK}MpNo2Kiqx8Ndg9gzT|RufnvVfH+Qb=7<3je;AxH&r;UW_Z`>5Shidywen(cQYNE)y{z<# z7=;H{+FuPpdbca?jg{YdDC<*U(zr%o#6>Kp{xZu~erEhqY|Hn;7)MB8t4vn?kU5dR z26i&Oz`+M97g&Fly63M1JF&j|qKYtxyb6kvY0p7@{#bB?yMGS@GA{o{XB2=_rAUx=%o7G?CTPOsp$) zZRb}WOd9KD29mrQX%0a=_8iLM``o&Q|FoYHKe!H6s`eO;%~qU4)tI7(rxdMsWG`qS zilCDk-f}F(;LtE*ew{^vNK~NjS?y59goIpbf zl}?OyhyJyH37z3Rq!df%#<8^)yoLJb17rQ~edu&@(-MVt;F4rHSN%66gl1u?aPHbeaxB5jNYZL4Fk!7RXMZKw ztaVp+yX%raR8^NPw-F2@@fv^cb;FCZRp-J>w)6<2GmT)lnDyKuMNuOS!-~tbA2;sy zPQv+b&}j1gC+isgV8_FgkM#tc{r+2i^zhM_6ZYSwKk~o)2D|?+|I%jvUESX8u5NFw zZ#IN%WYxRxm`kpkVF6#OxfD3+$L;De4|s(96Bm^1JS?JkS{M zyWv=O^WGKt9!AB3rRB$V@ry5;&z0S3tN-?IR`%f>hiPUgR&BgW2^LpXKJWI*_5H0? z?$Xl!V)Zr3q`by!MFX{en0dC`2k(0Xy>`frwKv)umn z7xS}YnJ#a*Bew1tle$69lC30a7E|}`?rw+wFJJCGZ<#OGw@sOv&fB|D-6qZGqSjUe zvBFiY9ot&sTu9Rdfv-ZNy#Gq5=z-F1d-sPDkXTM}FGz{mH7g1k9NnultO*aPw>C8W zD(qDMN^Qanb1ma^zef?N|5@E$?F{tkr&yMFF6xQ<4s zoqd@zXzwpn@j;v<5kqH5c@k?crS~ncm)=fYh}S{JgBsca6x$2 zE6Owxfe9ni>-K%QOaFcP6y{F)M~Jb(Tnmz9P@X*y<9_XsV27E z92Bfd$bLEr^^r=;^Seqky0l%?*9cV}o9&8uIqkFZz~LH}s^*~5qD(-Y#+VJz=;CHd z*F1AiJAe;>FpM5|uVu|$m2EuwIFm$ieiE}QV-dt{_)L_<04tX#KfN~r=`J=RYN~3( zcs~b+?NqcIPuAeeFN#2YTG|_4aYH}3JM7_WUWwG&QI3LQzWMd7K2zO3HSiz2yOk!= zIyfUO8=)`d%J>zsMuwcY^m)bzr5W0{H$QnV2B&&?rDyB=AkOerr$Q{FENVo0`H(xx zSMR9co70{!Hjk>Bw`WCZ-ZG%Y?7XOYuF7{L`9?99*94thRcI7eT^lK7VGe*+0Qnqb z;p(3MP4TzpYJZe)~# z%(UIyzps6(kmSNr(%ha!Dz-ExoH19R+qoR9o>3AOE%W;boz}MPRK(LzXIRKpk@hFH zDDIGFnP++T7QUC(<9cwf@Ez7X5^;o`A?55HOVyVZUie4vihx`*U9epNM+zD|bxpF;8b=m}jrQG0^xvF?b{#U>u1*DG+ zm>bM0HM21!Achgb1)IKS!_<-E?QchV1*wUl5q96*c6jR(WuP3|U^TfY6IoJ2F6A|X zjgz#JK4gojs3h|5)JhD0CO+%%y&ocs_-Jop>As9vtJN=4z*JopEvflH+hn+(cq!;b{|{7cCkNll-n1CQVE+N z?uVACQifWY?*;|l?y`R^CH zWKrE2`0~AMn4{s?#xcvREcQN_8|=dz%D;C?LK&%+FguYoFJP0GqK>pxqYX9q7Vk}B z(fsrMqO+^yzQfF>^}>3RI|6sWwP};Vwel0AJZ%yLx?l_!M^M0~utbph@oNUoxf@Z^!(S3*A@sb_?vV z-f!0VuQ8{fWUN*#%G)7rs*g8obje@ZYvsP<_`Ft#wqHMilea*u(KjsfR2V-Vrfk8} zdk4%WFi2w_LDqStWvp3ly`^2ex?bf%ux_F(C|< zYu9HN*@jHbGaFST>ml8ldFr@p6c%xv&fB@EanOpS;RNK;t)6`HvwB}mPqc2i#oZCz zy#4Cv=iUP;I~%U2g33&z7<%f4wwtp_IC&mq9 zpTjW@_ji0kMU{DZcyW3?*8hKkg3-0p{vSO`YF4}4LLt2WCbpFS8z-5x|Cx$pWWiD& z$pCnZ{SR-?$65Y&`G0=-@XMt?>OcGz^wehmd-g-GyNAPdcU2z7@d+#`Z;R4KTPvGo zqs8xM0Zi9f_6~R^FP;(^j!YBXcYaG8+I`@vBN#1T{1RtTWViiaQc-w`EsEg{&f_N^ z)8DQBn{MT(WF%=;|3=Tj88E<4Ukm~Wk=v=7uQcp4M&y!+8bL0Fof}`#8c)!pL3e!E zr_7UiX&sRV^^n(pbR-%0H!X84cZ%8WR*U&f5TWy%WOBmryl@QaI*54lWdzan*8k-D za6c$zeN1=iGHsyGu6&T4@qkip@67SWJg4~g;@|Y)oqS*Um#3*mI1N||bYM`uOCKdV z;}2@3k^^hgtuTG(Z1RW`c1&PY|9q!y7k){}<(paTVI(+w9er4Ky_l;#8p!H0cqiW_ ziT+*-GSnfHTkz=EJYfn_V1$u6_f&}Wba0PV<1b|jK+rh#TpDbLB7L@y@d5At31-o*68xPihH70&7LPGymya+Lo??gElny4HQL>-rd z3u^A#yM1_majFP`@ROJ2Ll+6vYN&&2ioX*s;7+94-XmmX5h)I_Bs#;p+`QB6U3)bU zk;@NmNQxO97j+n49!BEjukOG&Mn62h(%T$QG9Ssv*0c0rq|gijv^crF%0t;ehZfI# zJS?tx$NSShxFa*~fvyi&)5Xvs#B2Wg(EQc3{Hh&HH!nqwJ^xU-@h4nnKeLPCmUCmG zZgpaI4apsG#`&<^Y{w!ho%L2{gQD)2hZ6^b-;C(p#v2c%J<%@j@M>v!*Z4!mRS1po z{9UI20XE@aNPT(v0Fqq{XLk4TmVWaH%-%CL7Kr5DqR#D#9&E-k0(7LR@$!%6J_?g} zFCThCf!Yn;03wyxPWuk+F;sk3iNs{sIOC4D9+Pet5R8U0a7>R8bwa+Dkn*1gh7kt5 z0{XBmm6{|t2nVOeVl2Y&^9k1>WKN)Z$okm99x&|xgTyRtNG!?8+Qb|&) zWSgH_RNY?=sj+f>meFQv6o8Ig5&aRat{Bvq03}Wx-4wMNkd88e6X6!_q9;DSSQr5+hEBy$A6Y{@!t?u?9CiFL!4$ps+uy1ifzZ0*`E10 zMF(FK<=gwJSmIU`c>J*=FY{g!0%CROngFL=ipxmo7C`xA6+CEk4&THL4p@rd+vb;X zZv}%%Hu=v#K3;zC-zsff$2{Xh1ybL*4mF?>MWQsjmxl6Z@GD=YvA!I>!1JO$aS>s} zk4L@+j!dYfAPjLVbSf`L9$^!(s(~Ov9fz<*Hg%8z>M%>oYnwT}@RH#(d+#R;Ms~(c zb1Z92_FFOU>ke!ST|x$u!3_o54TSYNeDof+`!0MX96H^ZSAR~SfgJJPpH2_L)UFcr zO81(gQ^L&VfN$p?tl{u8x@@kuI`F1s$yQh*2n@%sr>xugy4fZ4RtRWV^O$iAvN6Iy z{34J@(Xn73xUG0#2z~NBL{NrMZ`+12`V@!)2rGd*<=X)z8Saf?IlIYsL9`V7;-h?d zKs1^~UWnGh5kBf)^}XANqgx2eo-eQL?34!clw~=hGcG3%7uMQs#JL4PVHYNlhnNqo z$(KK`zTuLvmEe%>IH8HK;1ucwv0&C}UUVW`txhK#Ok~Hti#4gE)hveKBO2kYxM^j3 z(8Txk1?Yss-?8Jz#(2C&+7ESuxMyv_?!b^#u_IyojQ9fy75*%;rT0LR0&l_=Dbxj7 zA-6~u!&<6_dOzyhBg)8vN9%i7NeG6lNwzY1{Cp2*p1;KCX3>J3=A6BJ zY^t>hj77f>ur7T23T$VluHgfe*flMinD{?HAvmf3J;v|g!?9ryM4>PY#@7hgZE z3d-q61Z95X7R~>lk#IfN=(vObcC!GTbgPT6?^QjdC9>MWlFh{sx9tHl{=yDOdD_1r zCUo(PYV*7UO+G{$a_;dSL5Q}N{<8c87gL{7#=^n7iMBg8>pFNoiHVr--VKRz7X)z* zF72yE@nrorjH9OHj)9bt4K(n(|FBlxG8V;rltAO#4N)PMx}wk)mdMV}aXUEFGbn}xpm z+YG7DQR_)0ENi`_J<#4SWSmW4GK@r{Y9m5ec^Vs-0tqMnkX=BRlw_aY}O)G|DH zoUWbs5J(!FdKMn~ZR@}9?eFb$@#>Vj&q8nrYJ4ew*ywJqmFv6PFM~IX@a>Jd-*;E{ zdvXL#-YQ`}e2_M~?nV^ckiv(}E}@~C?h!BgaKum$SfE+7ngs}8B;7JW*x8#aI{Ke) zz6rCZPsQK=UbKJ7fUm+&^5NG>3AFd3yOu_O8b_BKl`VnLk_@-(JJM#3+wE6d-8HOx zW$y)8*3yFGj?a@mKksgJcUOAbyR@j`B((?~%!o*%+J2-)t1#HrZ~4qdilpNH&tN!p z4k!LIcsDq_zLIIjn9v?d4LEE{`@zc?dW+g8sq6@VPa3WnGs)-ExZ+;|3k|GUEDtH0 zv(q}?B==F)2V(V1!5BG6b-0ep6Dx4X+2=ew8=**e%V|#VzyEj#*F&`Er1}3?14iBi zd`;}$x5NUxwsc?I71WGjuz}(p^5r(!5w1_>kRo>I!1pHwRQ=vroBn0QiND??5k$8Ie8yq`> z!JAv)2zUaHNWaC?yFd|eS9PzJ@nbgF!t#F?#7K$()UjP#c%#SrtZ;Pkk6mECmW3L< zOkIIQpeCu95}>g;*RJDjA(Gskzyfe0RZpLjd(v7uh#AzMe{SH!QygT1o$l(!%I2P> z2145KQ!9NF@1GXJom*I*`yzajm*pT2%V`D7odmY#-``U~a@M_{OP3#@f~| zGH?Tc1msm5m#~$StR?0#lc28~!rq8}Bo~DiOD`t(4@?Wuj%2G`cp4>{`ypk`Yl?K@ zI<8)pkHwlv-3(KHgYdx?=WjS;8_dD=!e2A?t(8`^P}#5C=c;vSHkF8fJ!mQ;f7Ozr z@+|)5t5fTWxcvRVONJZs{F+kM-UxCSoy`R*3(HP|8Yc&ujHAwJs&I%TeAKTD+9E*k zdf6Quo9GxQA&nWuD;jDbdh@@S{esYC6q*_b*{qUlzo}o?8n(XDn!LhTOSOUg?GPUrNuu|61EgyJAx(+z6hpc< z_v$bF`gQwiis9#c`=K*B1ASxG{$XVZ%)((Nq%1IH1<7_EMpk^BjkQ zcn5R;h!5g68jo}{KVMNg^K6>zwIUzDHf|c^+DMR56mW8@7GnGYz}E*9J2X&&BSJLx zcn}6gPD`(N_y*xq=U+ZuU_cm}Gm-5vN~;F}>X@zZcT z8z#ica~r^ox|}PyZyt7mp*r z&_wFEE*Q+^OLw);YpXIks{jib)o7$%?ghEq-P?cJ1^V9{FG(p}?c~OI!@{Q2`VLeFqB^#ag{OhZO>jBaTjVS%(3LW6I-z;8nEJW* z;gFT5YEwS_)EH;R^eTq^b)8`+5iGv24CcAH))$6$iB|?eL{ve&9ODUss?}5`lgFU- z+ODw4bn6;oHQ@ga7!8R{oH{=Jw0e8gb|%9Aa~%cMxsOuJGz7OC>8We!y@ukCIN*d= zX+=U1B0~0aF@FUiUk&0J4qKU{y^aTOj&SK3X_rsa0Y*SJTdaFs*?ehY7fe|8+Q?Y5 zJ^PP_+)r1^AB@-)#O)}-RJ-vKYxv6f5zNYl^aiuG#HC7>>=$KyU|BM*6u@YEaUT=< z{|--YY79a=1eH2h=XgwwIyQ=?qe8klI@dW-DtK^yL0CN&ul$<%P3DM_=r`PD$UJtQvo=?EHj-9AC z^08jAP2_$WUCtO(1JtqOeS0H~G^3+Gz0BQAV~9ys3cYyqW{k9a(I3NT58v!vaOt!& zSszo+@yKq&$xe4cC$d-BpqLXOOR_Kq6th%vt4uaH#qIYhHknI9{r&IhuTw@1=f0RR zYGe&I6nMyZ!z3Vrv>G$bZt91xrvkpv@7c+-w1;h@8XyglW1ONmlHcG8Ar82Lc!#83vx_ZdSeCQ(V%WEDOFa;|B?P6d9 zpgK{pxwBLyEiaT-fU9FEaQDv*Ix2|Oa1jNAsY(p^=XBM?DpgrZ`4tdMx9W1{xV#W@ zlphlSRqiA)MRx zep6{ZEFJKS%JOs)3I6-0Znm)Lpd&+MXPog!CEp_>y`A7%A#=3+^OKChDGi>qa0oO| zxzu-v2TqUadu1jLO==px;GIWTPcx{4n7T&Y9vvqXYRPb+6b<)NKLYDwqbUY z8A&%SLA>UwEv>Ie99?$Pwo$8BFod4xgY!tJ z>3}2l@H62YRAg=EN;Y03;=onHj!b)&C$EIS2E>&xyt>7eWOyoC0Nlw7ECcO+aWX82 zKBuEOEDOe)!H?b3g0jda?rl-p)P`qfZs!m1^>mfM3-OIfC%u2*FO&YCun~ODK2qqv zK|b66lkDHis{d5;|9tr5kNE$8;I3CgxA}isnE%d{(EsY=KbHrn7*+L0;ipmpPWpNB zcu?U$6o&g*+mqA-~g#jy*kUmd^_;Q4TKQIBUH|zawc;pq7E^j8MmG5D` z(!3+nR-aqCUXA)w7F!@zgzoH-1x^TyDH+Lpa9#s|}M4ad8t+HNCUf`e~bg zbl~5n_5Gjm!DZ?v)|O4IZ`1nu+bip7n9iT`Bndm6+}o>b3@TZ-d_E}5K2M#K(t0)W ztYDQ>=XhE_$`|SJQgyBRiB*Nq(|Rk-E6Rg-_-h!7-x}vi3M;Sv$MC9~=3@s9FO-1R z^c}xl!mmzab&F#`JnTF8aBqf6A6mgx_NeDiGKo)}vYCKnJn&84f{ zx!Lu+X5pgIaME|U9{>h9g0x(Hi6_edo=%Sqqe+C&!R4NAE9)SybSGB(5PB}AB?&Gj zG^lFp+PQg2B01$l;eVsIyB0Md!2T35S?1;1nur!2oLz8MUC~b4t?gb{`3;E;9-NNe z#$>ZH$)WL7A{tlJDCnS6QLXlnVR>l!^Y#jH?1$CSec zq6#f3)q^irCd||<>lzn5!nH-+^RC(i-zqw5p)|?5Qm5CqB48%(s>{*~@v%3}jSR@y zx#|9-7zB-68Zk`FFKZJQFJy4U>xBjA<{lRcNq0b?#+Zoc%4}0rYagu25#Zm0f!|^W zje-&|nchzMy&a?SfBk~p6CmsZt^x6j{>c_vP-G{V49uBt(MZ2~8Cxz%&*-=&5;k%V z8hmth7nfx2BLe|Bt7(-cp2OpENIgcI4owxf4BBuman=i-%?&KJ56_Y&FAzp4*Up!FzuOTo(Lo7l2+J}2Qgtf2Y*|nl+`K!pdidQ z#p0qVI(3@lb^K@EzF1Yffj?@q@dUS=SS_lgPpwEB^6+%bkoNnmA+15{kUQO-;xX}j z_B;l86t$6NM&eS_+0FQx*QY?0(WU7+EvxJk`5H`$wlPi7lcs)2>hEK(RjKs^C#?Ha zeM=r$Wyy+6_$+@UQHVT;S`ZbwTEmvnE2r%Hh3Z1uqBO3jWJvhu>($}JE|`ZH8B;|E zw*TBJMVW24>&SD~Zcmg*lr7i6>(QzFCRud2MZANq6)W)PSsa2)`I));Wy7m3Uhn1t zXq28-qXefxBGTb@$~+~J$Zr`=t`WJM)fqcR8|z-%sG9jL6-k;t@JswIWU~6=;wJpg zO}(f?VK!mEhH3e8xMPBEXt{L{|{ggZw8m{C5Xb`>+dlDSIaR~9t7p<5f>N6 z151T27|fpSL)twcWS~|yO-HQ-3-jl^ufk_A z`Q#BNad_lpRsFlfLTXn+`d~a6yZ^Y-5xGbQ~dmEjrX^{-NKu%px`^SEr+AJdbrkjcYmw5feX)_M&9f`Us?U3Tvf2w zgS(7wEg#;c<@XzeVBG=Uz24g9#;-%H1}^ z;Enso=EW!9BquovC&IlikK{&YDX#`(#ZU=V?j90mX9nB(?8}| zM#H#r7U(1iMyRUb9TOdY1FgaC6PU;N%Brk)YGRdzFK$kUfFkF=#fop)Xp$6eD$;}& zBq<_p2ONk!6ITOib9G!n%;PIO%Ssu^fy_IGv%~o}!6RO4f>_<{VCbw!&__;z?7g2F z0DO-F9bA(2$n*F|!Nui=amUX&zDNlT>KD(8QED-dE5YQT4o+(bkg*J?z7!+m4)jFV z7CA``(pfX9Lk;|WvOkFhaY>OYd;CS|)B1=Rx*h5y|7AQuUvy_@Z;!yzp&KCrnG<;> zF_p?G{H-NDe=VL?T~c|MeuS?z44x0u>U(7r>Q}W-q2nA8V)PwVs))RK(1`SlVhxQp9Do;VBvcAjha17#R32S?&>JICS6kos zI%yvuehni;3eJ_P%EXEGHDiPzs`)Zik&1}#v*F2WTv4Qa8OZw-556ET#Nkhdr*#Ec zQSwuOLFcqe|5$9i+}Ymkt!yDmPXFUOBDexEyQ0UW9P_sjtyF0oIWzrB?2wm48ClYMcF%Tmvq>BJ(^)1p__7UyNMg_f2F2_gj@lyY@E=Pkk zf9LAP(b?d;{cPXE^WGl&?rM|}pa;E9Q2DA)fXgZeUWrHS@m#*uPY8=YPMA)Jt_%lo zoA(DgBPow08tZjy`cb9yFuy-^GaODX4SQn8A+38lh?N@Kt@aXF~i4(JMpB>^6tg9z{$JzAH9d zWD)I(bStq2KMiw-#*ias(y$KyhtC@aYtc~WKh+z%k%Jt#uzvmB(V|O*!X+Hk3 zT^k8qD;Vg^7bwfqlP<-ntxuj}c+ebY#3<6V|R*D*tmImOMaU|03(x7k= zyVT~=?(4zuGL9g9behgq7apZ1FwogULG=YKrsLNV{3aM;??sfHHJg^bp#tD8m*ima zrqaodaMXRiCGf&!UJlNTvtnlRokKln%j8tL)FO<;A+phPKo!qGa{$@!ym}HHbffnX z_3Do193zk#C-Y4SaN0d;Z+kEz^_#>I{tCk388IiP9lZ#Tb~1+vHWnb!1zDd33d2Kx zd@-Wvw#1ZCH3*v*L?zZ75*RboiIj0gq103wHKB;&r&Ny`gDHq1j&cUOV2U$0z}m0z zs6;rzv7L|qPE2y!Y{s~;Q=I&&Brxzz;y5S~2My*fRbOJXKlXmu>DKJxfw{XsYon{K z(yFVHc(iq8gYJGW)P}TWH|~X6gRta?nTxu4mmQPw-0@UrA@HJ_t&(^*Gi*+@%1n;c zU+b+ZhlfIct$ze0yXySvum0MA9C%FS?aivc)(4|ia*4*sy)&Hr`BeiPYqg&IGGr*u6b%vNRXRJGD-}itsHJMM9j}=%kb1X1J5di zs8mio!cP0|kN`jR5eRIpltAu*>l?-8Yj4ilqf0*bvw_`M0}st6N+s0-ZWFv~^G_jcV_LeEe%a0xCS4ddIl~oK+Uw9{VV$P^vv8)7x#+dQP@wxCc&7ZJ= z6S;XEdFp-E7DWG2#!Vwcbh;FR8U|%jtq2d_Wm&PL^AqIcT(mbbB9APUd9wuZZ8X+C z*e0^z{slwDDSzcS4iBfV9WZ8Thht++3Je}$tp~Thx|xIEgL0QKbXccBLYb}^gXDOg z4#{|ztKp_~F?15sge7M?CZ^L44_x?_SuSS}cH8ZMT_6l&wB1G_4wxI~1&`^J4=vMY z7#%$rM!cvNvs|GY&1(pFU28TQUU>}BRO(=KTv0n4LhoyDE?2%?6KR4TDwgNjwTN|W zVm#r-*QQ`!yrt3unNVo#bsm`A9iUSDvl(zJ6=3n1Py3ntg!G(?i?+x%w}FLa&S@ka z@4-8wzc9mw!M#zk4C9MY4R3~KiBrxIy|9rwnps63Faa1y_D+>Jf~F*nye-a&Hb5Ho z;GtMUlIL5J$}$KAa|sF?+GkftxMq|oWU}Vb1fRO4{?XKXkVJU2PmMspcvtjDul; z5nEL3F%zO+>tBSzH5#NgvlKm(LqvTf_x&lXaEwaY>F*;R>U=|rKno~Oq-epuJ4Jw8 zwO&T22`0b4?R?**wr}$qZ|CWdxX+c>ww!S)cno|J&ExBCaVXZQj5Pk_ldimttx6J= z>ExVNB^YAgWjX3s9*ogA9;_ChNErL6KeSjBD37KmL%bzL2?{U?iod|u2;W6nEnX<% z4wjrxY8EOfxs?0XX)1bW)VO(PxQNbcG+xwTXt>^5{)l6M0Oc}-@a_=@Kw8x_ekZvb z+gOU^O6e9CV=}JzN@9jmujW0?suaD8c#Z7#);Cw4?@8g0dTL{<+srDu< z!jWo-b+P(+I~&*tD5Ni7ujb$8+{b~U!a+wUR%_NFueb+twHSOBsf7g23D^sF>OC|#(bk!b{%n7P(hIBGyIqBD z*gLAYX9^vuL6I|qbI_P0R#uIJwf9~&q&t-t_bzTQnW4wJp_=#WEN%@kQi|lg7kI0b z>zmu;TG{RH?26gJjoLm*A+%hTACQIU;R767_cwXR!X;g{`s5iAVO$TcpbGL<_$_#R zivy_18|AHk_f~$Y@>Rnh=O~!WY{=qWs-h9>{H%F@Pce`$xiWWu@GKP1y8s>L$--a* z0Gg*nLX*b=rhBx$QAY8$d`D5>CLMVCMz7_6t23-5`u`=|0-5Kw$+a!|9g>)P@4d5ie25g+q1A(5&$SYwo((c8y1@1Je= zD5ZT|?CfrQr*aygDmBl3D;_*t!W*2@u*2i@Jpi>sRSQXKo)p_Ju}&xJ{JAD0LAI9$ z&U0diUyu~0?g&`Cc#MlAVhqBtt%$|t%jBODOA%hrzOoyXE|2HuI}iB(;pY#?-Y8AL zQb8hDWyg(B2PEXL->?DmO!#A?SumI|w6v%8uC1Q4+A&^L?p_{cV6^AM#kSGo{>CY^ zsh_I=j0oK^f+(K~Qy#^3H9XgwtE`5kJiRrJN)MEnpj{KnTZuLk1r{ep;(m@qzVOuU zvAoIs(AUUb)L|Ot8FU|I!mv>3xQ`SsdU-Xf*E8oQG7|B=kZ@%MJEO*n-%QV?jshqU zRzZ1oonIaSI7jMlz@u=}84{#g&Ev~N<7PnI7qtN!1^hkw-n`*<5LW28(%Gx1<+ zWmEo!FAMvuAOgv{Q7Nh>GPo=GAECehvA@^Ty;KyREqvy`?QJ~Y z@}G*&J`cY=+g#cD)_?r02*0gthMx5O*YMkab$7R8-zEK42`6!~&hpn^qa=wk%Q|9( z=F`vUII5q0O8xvG|Ji5N&kysTePaFm2x%h-h{X9S^S%C`3{z3MF(F58+PcLiw&*Tg zvE}>+9r(xN0Jog~2TvX@H}gL|`SRf(@4p$x_M?4jJ^$pg>7i4c#sRX=zaI|>ye688 zz4BmmH~$Vdz0sviH&H_t4a)LVimI{r8~SQYI`asfhj09wUID!Si2OJiA2*x7c`N0S z3sUZjJemEc(0e8S_#MvSLh0%X*?~e$9Gf-YnYD0E1jfk%b><0}===Ug%6;43*s8%Ib~eCy<^DE=Qu+azXUYjg2s4)$Yc_11XlOY zpDqB$Lz7SvIl6knYBNFmiz~v%^C|;RZ?&W9IZ}HAxu0bV%JW6|R(J@*Gj$s)k$_8%Mw-{-x-o3h6s!7Co^>NU7~WJ{u6j0lQw?j( z8U$1+;oppxxe_GThS*dNNMXDw17PIlL1G${fG&oNZ;&*MIhd_N8qaRxa4p>?LClvX zf0OHr=>ybCaa`aK=aohqyrP)bQ*KOZ3@zW{Q_N|ZK#Bg@IC6|$<&HOxJFm1X`S9wR z|2rE&8}uR9cH*mdxwt>l4KR8Cd)ldwG`tNn`~F`>i0xj7F z^iQvuCfjy^jHQ^#_}V-n3_g!9;ZD(LHke{*)%ysKRg#ndq^x9}djSdj%h0*EdVq6jS{gh5VG(%gD=p%HdWxt~~8 zD#gr*kja$umc$0sVlroOCwC_hky`Yi2vv*pp&A;2&XRq(KDMvhQJJnz(VmNpHP*r4 z>a7xr=m?QK$~>1%5o(Z#NQlP7do_XU<<2cbRd9IFF!3|>Laxsaq+x%1sURmp($8^^ zjZ&#?@b>!p=Ehbx%l2YF%UVF~$Ski*_<40#FX9L0n)-@S7kpGKz@$7;MJ58P$4X0L zT4$ltBBe&0Hg}!T+SWM*&-N^Z7_Jeg1EJRra|~PNsvQ>(A1o;HGp;5sX0cFlG8bw% z6N5s z9YMYk9Wc_KpQBDAGI`Efl2NZUjSr3t6+F1Cb2XStT)vD#U|1SD>tDlGbaAM9?(2#& zHcOowoDis|tN@ng`6hXXe8dBJm@SK(bsmAp@>sFT+KpzF@k6w`qkJRsj*1(83>@$#aKz8`UA=&__+xGQG{Url zVv7HgFkqYD$0v_SS{}??Q@hWFr;7k<3skMc;~QDg*Q**^{VHRsGF{*vUE`>R!$e+h z$!nTZ*h}mNXV+&HxlZs^Fz@7+?~!k68ujw1KB^UN6ak2_)i#%_QnX#ejPOknA54^v zQN135ia`IVs70(TW-9V0Ls3qgLu9O)RAQ;T{E*f2bWQ@&PDt@h4--w5wd2Ya?cyN# zCY6DAc4`X@8yHSx%(@>Uax_97ffI)AA6hbx&Zpja;i>N&j~@(d(9Q0^l2vd8G$cwA z<8TWEFXgImlxMWlH5W6La1?xO4QDx4q0iudxU&ZT zlaNS-PtT%Rqk8UDV{y*(giUw6JQ?**|Gn6h3-4bOaMU?D?ackvq+Mm_JQ{Jk8X^am z+%{)y&{$5rW}7x?rinZ8H^<$<__EX_L|~Z+bQ|JaGho+-x=rBGk7YpRB#kAs7%FiM zvr`;U8mQAG6J5ue;C_*sI1#Qmo{Zue!k)ytForNFM{ErdWS7cf?hfE0LHD+f$$sEs z$TdWj_vq~du^$>ja-`G+{yv!430S-lv~v5#lAbu6dX@_bQqX;TBvBz~faje6jEPS( z)sGNc5p|%6`kpjSHmr(;JhRjrDUYtbZ$j@DOa+XD2D%D)B#FaoD2UZ%XT7LyiY+_W zdPuz<;#rK0eND!eLIR*gJBk492GDb>xKhSg$42(PBoZSO6s+w_uJfud+^=oC30g7G za-1CtA9A)fd6Wcloofe{vG(L5ngfk%Xh{H$y}g{eTB^*xTyB z!AU44%K@&j>&YcG51OxVm8((H`V-q&2zi}#a&5~IdGHWoDTB*6bDd3kSwiHih2f5};QH~LKlCy#8L&9sFg09c+z9zvG4nTbnxhLJS zC$0UAUt1cS+YzeC@1V0aV@=K2YWL4?U$>(cOFfwo9r+ z*l)SBy|?jw3@mZSUW9<=?}R7i zF21I`g04{%o|5Y0A3zEpJTEg+S@6(ES|eU|`@JqOPqqwZV&WOWd)7b)k&zgR9KF&_ zSk|kpUR*bkqqe|TaP-kzF2e73?iUZ1mL4LMaf_h`fmfe$N#UG`5%sM~yYt`9YqaN< z4S23t?apPG5w^N#AtR(__xz_Zl`3MKitd_E~9g4y7b=8Tx%xws};(}gZo}67` z!|;q=D_MBC`duQvmT#!|=<;+D7Tr@b|yRf4Y|h3fJuOC;y7#+D`ZTt&ptb0cw~W zykWBUCoB4vn0$5(O6rg*i&R)$Tb(hqoLLd#a}=LXjT8 zgu{NZH#qBGye4>oEmQ)p@Gpc(=%xy(1gjZobh4xENzm+U+Yso)wUgP%C|gi+HBFfytp{=Xi@8<9jXjy3nIgc<1Z3 z0rpP0yuOgl$E}ODCSJU%m*DjR*z^_HY}h!*iL=HEv3hejYgsT8XcCh5!&1A}N(B#8TMJUdITaX3x)$EOL27?P8r#hvk*Wr|#F^GR|ewQ zMQ@DJ>WTQ4Lv1&#oHQ|RLCQ)l%x)}Do|}1oi4A&s3s_(bz5mq0I1DPjVRnc&Ts)|% zf&-!GVG4}}+qbiMFyyQdCjXj4#Szfz7-w(ym1@>VZ*+`lCS2W|9sSivbTv@lp2`Nm z7w8zV5|>zJqY6jA zjgTzzg`nvR&Q%Mq<~BHPt}rfduBxR z0LdjlwbR`t#sa$B-z(QwHa5F!n2r{fDfgv4#+)UQhaCClI>Tj(^oDphJP&>|k|J&1 ze4MRqi!)@PrK#gcU_%Fa<+T!zCdSzQmhJ+>{SgFaKlNNe4%W95_xK8?(J; z&!Cs6gygm2Y6vZv^y1{+m#0poCwaDdo?kjwWL7P|BsUzN0^+ny^N_0+;jGzxZOpa1nKUbt1fES zF~^9O>2o0G4Im?s__&KF%#87Y@h*>Q-@zMB? z)|rg)wm{RWSBD@>X`gu1ovVVeJK+1SO|+ccx%w|vhjr+_nCIm|(#unP423I@(A?QLhOG(YUO?99Ig^_O^(6($Ik$4WcNa#stny+7yi!J-u&8zk+>_(nMI zF&J2&k~gNlkAbUHNlq3gG3pG%nI;#w~hu(LvMpz;_ocW7Pv!{KoE0Q*iAnu;^ z+tayUG)XX2-IFVwo@$YVUQ4Vo zPQ9F%s1`%4Zn#T_o0(a%Kwx;e|v&&Cn+^PbkIh>Tjc)-Up{EY|9!di@XJ5!KMgegL;l$+ z|F1uvCinZ7e2=SA`z4Hoi=}c zb~GCiLPqV6fU-kiPdCShgzdvO_?ut74nG9k$;$VhGj#Rk zOEmY@$Bqbe`no@aDjIYYD1%HlVdc|6H}*0c55j7WGZR8`G*@#=nD<0>=?(Z^QinM6 z2S^)}_kSv;@mt;hUp^-1+w!9)%S(@w{%?6{>5u#WA9CS025r0lw|A)Oyg?d@DN;b~ z`#+H^a6P&pyFYa`GFc!GTa%Zfy{dTj=-JAFJ0SsfFp%*C#9~DNAT~mk`QfaZ#wQOI z+TY%>ibN=6D6GBN-Fn`8QT%yni6iTF?ZUsHql(pVAE*1S6nuTGdN(8&z&E3eCrvzU zqw>YH%6f!mtL~n2S-$UYUu0Jb5io+*xw8GWWHJ6vIrvNcarlO~s!L)y(-_ZAN29ZK z%bKiAQhN8`F;5HKcEN*=2Ou`_z=)04Qb=ZS8SCkk`kE3vO=%o2^~>3<*Sx0S9b1Ed zy8%JN=8$G%yE!|cM5I_Gk82gpqFU=I+hO)JUa@gEK%O_HoEeQXS+Lw6U5xkaV|b>E zzq_u}RFF$5Ryh8u=#4J^LjDF+wq|7ZAD>Z~espHf=NjJTQFMCj*JN&m&^q3WriLqd zKzsY`x2l3$MLu-?G-vq+AoCa0CmYMcZjz2s!~Oa{i^au7ZhW*?lBSfT)t>hNjjUTm zyQ#$C%CkL@bwGuL0w4RfB&x`y;v8A9`)46zIi^~!E zRU>zE2au{;S_wTw!_OVnbj^>}&i>vDDs`k|R|9h$b!nFrinwqUoxIl`Os)95APG-W z<3}X`+OuqTA^hy0m44NJ-@X{H9MF%-JAPDuwrv9IFG+c5xaKh0Z=w(TLC4co75d5d z#ecJQ_4&vm=bb_zjCX8rGv%AlP*&jSo)WV0(SA+sdote=;KN-t$2ZL&n z{EiG&=07)E+ZXY+`nUa5M@QDVQeysT|5TGt0qcp2zn$~3Idq3tO46s23 zHe9wHjei4ixJ@iFQeSoTvDk7rh9C%MXadAHE}>byNObyPJ&nQ5o-h)UCkBbI;+p5& zadzkCQu5^zR773CH&n_#_dznG%^zO9tJZwmD43wK^?&d%+DK1uViPvT(d(&KUH`h7 z?havw=ayk7uCTigTy1+}+-iskI>R}%w6!+zI-Af)aG~Issa%dRGvXMz<>;|X)z@(qqCrMXhetHP5!d8LL@pwwV#AouJ z{ctw}y{b@HfyG-6U(|fLgx=_H(I;)Eg#|~B6+hSI0`m?KH}RVQW-#OE84s_kK@LYr zbkC#_qyuN)(!z*+UtW1LA>mwAh>`iq{;3(9Wg{d6tZFdvI;gfSN1{V~fjTlSZ8coI zfo}B+4!P};LS^5P-Q#;6l{|3qlL*{#vh=IX*|t;lYdU?f2v}! zA4>sri~VPrWUoR06XSorTz>N45B<+?K>%&~pS2C%db8ZW>J5HJ4Ny?J7(99czFO&# zUU7Gi^ovm;W8eIDB0%E=ujoG6;JuPBuVmAKcucoE;Blfq>qpac zdDTB5ugF0D%Y^ArFBls^|B?hVlFAA6*s#d%Jnw~q=R@!_7IlpERWF(3Nsxi{-4hV0 zX&lJIrQ3SM%3w8vx`*9cG2#fTzZ7I`H&-#s@c3pxHR9ks;NM%T9)~)|;~yuG9o`5L z;aTBn_pEXsiJE7|#Kd@0JcuqCMDE{(_wt--VQhm^6T<)w0KW3C|T>`&pxkh-;a`b`KtORbs;uu_Y*%dvd6vbV7c zi}Q5p+$!PT*j(9t-Zfn*1zZaB?SqY=qN#sb6`SX-=#d4(8E^f&vUIIj&c9hLEZ5Ss z2CL?zHSOz)m*J2}+p-wxx~hZFJwMt$)(RZH{mt#I=eDAiD@+R{^BAPEHu^%!pZ?`| z(CY_xU~j)=kw@Fdo5S-rwdQMspN5A6DID_Q{Z}BDf^|T^5n)%gZXxqSD^9-~ouU!j zqg(&f=k=j&*EZJ5#h;b!CfIK1BC@0;)1zO0f6Wp8jkwwR%|1~w#-gon+KD4UfS%jk>A{PQaQ z((prvNlGM4twA#&J5D8fdw@F zt%DB`EHDwjMRweJ@=IIO#<+J$T76O#T)nAf(tO>d$Bs5`dyL+b#~R@Ta`ZqM$MSq(JN)bIMJ3#qli z#unjR+I3y zk09IrF~sc{n0)_9<{2Hx1Ln8U|1CfH@=2`!nf$+~;rECB@AlpM^)1@of4v`ey4%nG zgVZNe9)9%(zvqjuQ4173*!5|m-g_Pwc*C{Nudd^t^b*HllIJOpeR$ivLlK3JNte&S zT!E5`3#n5$&=as*#=0N9mFGq00X1DHC!M#HV)FUs_A_E?ty8VN1labW77EqLj!qjo zmE*E4x(x{NLiY^&C?<0Y?&8%&)a<#I61M|MZnmW4b)cn*Q{$a*a4}kV^x(@!kG~=x zCbqMn7PQXfyYyrnmW)%`(3J>=Yc=K&^pv|ACFHX}9U3lfgWeUQwb;PsB<`?cetN98 zApfAhGVz#E;SVle%(GvqB0US{-&F(UIyxJWl!BB;))u!nK#|`NDDE*@>BU1jq*+YP zEc|LimU5N539?ZQ!l)QE^kgxh_>++HMeAC4TE}wKUFv1Bv01Kd>?!Yp`KJI0&f}ZP zdlKUj(u+(xP}#}k35=7M|6v`KlNb7Dk}DlvsUQI8#Z`(t*&!21Hh{%<#Sye8BgDM7 zV67TS9eG}u%UGn>msNctYRm5w%j9~Wz*c(KTwdcQDICW7{4+H(4A5(imIh4p(qu?_6(fG?y z={CqDY-HHH7vptFVIj3?JF4^ix++R+!jv*!w+fMgN~4>~a%csY zX$0-LC6=)=9Y@1H7GtS|WQ}nsL;x}Ab_CD02~da%00^rB!II((uac3ZwtDr{C{haf zy2?OKvaw&Wz4<{f!#lUa&(1mT%z}M4fD5A#!(QTfi2*3m;Iv!fbArolB07@H)i5H$ zS57gUB-tPLvhxH|AjXr9O=tBr7B|U+ZLY~S1Wu~2VDshKpBMO&k|i%HIP`!q%|r*??RPLHulg#Zs~0)g8p#}-YTE!f3< z)oN4HW}UdePK?$%FqN9ug^)v1YR@nhp|CVXAs86KLj!Z6%vEe!ja8bV)K?|yWv=vG zj4ZKpcsgDz-Hcey5wDEvU}8;0_*(r^(k22SPDt~3maE+gLDBTesAgh&j6PfTF%A!k zos(pQO};PkVA=kN5oNZ=^N(pr| z`glx2zQHoj*PIM1HcY5IXVRNWtwUr;jod<20<+L_%aB>$(@drjIz3|qGXJYoA?dV# zQi?OS*PRnEMEucw)kKE4Y(`rT%%}y=w1U`7AiidFWMcx2xqW)+i$Gh0z zG+Jd3lor$R>f!u8QRIgRz#u4&VbljvQ_CgD<+P7Z z;QkSji90f0>0{G~hIB6JSOS1ywPn>)Z=UMFP8;6wR~0?6s+Nr%j0?#x5Ae8cMX5Xt zY52c(8)`HKM;JgHzZyeJD-phY-6ne>SL&BBY)90n$t$YQIF_&*9)sarPg|6Wk4|I} zsV*l)Az=Jtlxo@}>)Ui<`W+gTot&dD`ncc!%fp=zHRLc0=3W zT9bsMK@L5mo2U0uLHZBt^bi)4hpd)>@$R8#B3i2Efe}ie~Iu68`vfbS2wk z2m%QuB{&SmVeKyk^Lja>bQn{6(XYJd_`?$s#ec zfLPngiZLyS()^N&;ymT*wBhlEmf$UxCtqW#ZN_veQdnHvTtN?IK%8hP+%7|Eev#ud za$!7Z6=A$r$#~CEz`yFQe7oCS-zzuCP`uOICA?b#U4y#k_3I$o5cFEQRM5Is_9r*j z1+z&|nSW~EOh*u<60>MP1Xiad$bMK*TuZg5q5PH@zZsHAld~=v!VbTs4VM3x)+tUv zd9?wx-Rz&9TbBDQCiF)kVu~-}Qcxg>1Vq?DFdXOr$bgzn4t48Szp(si+gNUZ*<$@W zntLv+IiN#7*@`VRuJ{zhm%W|!&6VePl5|&g^tKK?c5YsBtp+eAFlrMT&Jyb7*~B-_mU3_PtPo5M(;uSsRhmg57i@1n>Rev zYHF{BNl~pp5NJVj;#9s}P10*j!L}K-w6de-_~56(1v6l+H18`Oz`MNc?=+k?W9E=+ zURC){gE5eFb=`N8la0@OhTQP_vsk53fa4*70PQZAKMngyskbq%o=q8vYrI(8H}@>B zSb#a0+g5?cHB>Qxs((hx5l{hRRi`v9eV$2LY;>xlrM=(F-R(Q|vP>Njz`gVUYV@r( zA<&@pc+nrv>LKieLmaVp77CC{ql6@gH5q!gu__G1aI7euO0gD-pf$aWu8Qn{o2Ao& zVw+{808nhVHfSB^SE8zr)i?0Z*dcC`HHpX5&R}Dc3~d&`Q=X6y0}b-1RGG;J-ZV}g z9H3h>iBDWvv;PR!^C~GIM}zWgh=ff7$fS*~HYJik3se9UX{`;I7W(r+M0eh?+~v8h zWEi{-(`eW$YB3f;*znl!k>a_UWbMQ1)+~AZM5LtwmA0fFv>{wgTVi9F@-@o%+lQ(^ z>QzUOt#`iwRT|}v57Fdp$9djP^WL>u1vP)2vc}g&68Vnv)9B!Dci8i4pNvSjMA>}V z8YnG8X8LIu)k@4aiFn7G|Kzp8H9k1n0(ameTc#?D^aj+GoG~nvu4i4M>PNc%Ps!j z%a4~P?Egy-{>cAa->84kzuN5od);0nnDhwah@`CUweP>5^`Enxi;QLe;BZo8%x?=A zx8D=Q3D0XhG-o-@vhIpj`|P z-<%FU06GNne@X!Nt>}I2Vz0$oQXTSAHBu&#p$qNg=zuaB*C)es6bBui?XM|8!;gy> zH!1>hPN}1-uyJcEy_Ia5w0~>YXJ;hNp0>S;R+YB`U18O6&&H!%0AK?J9w$I*mRT%S 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 From 7e87d30f1f30d39c3005e03195f3d7648b38a1e2 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 23 Apr 2024 13:38:23 -0700 Subject: [PATCH 026/217] gh-118074: Immortal executors are not GC-able (#118182) Better version of gh-118117. Just check for immortality instead of an address range check. --- Python/optimizer.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 5863336c0d9ecf..0017965c32f290 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -397,10 +397,7 @@ executor_traverse(PyObject *o, visitproc visit, void *arg) static int executor_is_gc(PyObject *o) { - if ((PyObject *)&COLD_EXITS[0] <= o && o < (PyObject *)&COLD_EXITS[COLD_EXIT_COUNT]) { - return 0; - } - return 1; + return !_Py_IsImmortal(o); } PyTypeObject _PyUOpExecutor_Type = { From 692e902c742f577f9fc8ed81e60ed9dd6c994e1e Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 24 Apr 2024 11:02:38 +0300 Subject: [PATCH 027/217] gh-116023: Add `show_empty=False` to `ast.dump` (#116037) Co-authored-by: Carl Meyer --- Doc/library/ast.rst | 251 +++++++----------- Lib/ast.py | 21 +- Lib/test/test_ast.py | 141 ++++++++-- ...-02-28-11-51-51.gh-issue-116023.CGYhFh.rst | 3 + 4 files changed, 249 insertions(+), 167 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 8d407321092eef..09f2a404786fb6 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -173,8 +173,7 @@ Root nodes Assign( targets=[ Name(id='x', ctx=Store())], - value=Constant(value=1))], - type_ignores=[]) + value=Constant(value=1))]) .. class:: Expression(body) @@ -302,8 +301,7 @@ Literals value=Call( func=Name(id='sin', ctx=Load()), args=[ - Name(id='a', ctx=Load())], - keywords=[]), + Name(id='a', ctx=Load())]), conversion=-1, format_spec=JoinedStr( values=[ @@ -398,8 +396,7 @@ Variables Module( body=[ Expr( - value=Name(id='a', ctx=Load()))], - type_ignores=[]) + value=Name(id='a', ctx=Load()))]) >>> print(ast.dump(ast.parse('a = 1'), indent=4)) Module( @@ -407,16 +404,14 @@ Variables Assign( targets=[ Name(id='a', ctx=Store())], - value=Constant(value=1))], - type_ignores=[]) + value=Constant(value=1))]) >>> print(ast.dump(ast.parse('del a'), indent=4)) Module( body=[ Delete( targets=[ - Name(id='a', ctx=Del())])], - type_ignores=[]) + Name(id='a', ctx=Del())])]) .. class:: Starred(value, ctx) @@ -439,8 +434,7 @@ Variables value=Name(id='b', ctx=Store()), ctx=Store())], ctx=Store())], - value=Name(id='it', ctx=Load()))], - type_ignores=[]) + value=Name(id='it', ctx=Load()))]) .. _ast-expressions: @@ -463,8 +457,7 @@ Expressions Expr( value=UnaryOp( op=USub(), - operand=Name(id='a', ctx=Load())))], - type_ignores=[]) + operand=Name(id='a', ctx=Load())))]) .. class:: UnaryOp(op, operand) @@ -729,7 +722,10 @@ Comprehensions .. doctest:: - >>> print(ast.dump(ast.parse('[x for x in numbers]', mode='eval'), indent=4)) + >>> print(ast.dump( + ... ast.parse('[x for x in numbers]', mode='eval'), + ... indent=4, + ... )) Expression( body=ListComp( elt=Name(id='x', ctx=Load()), @@ -737,9 +733,11 @@ Comprehensions comprehension( target=Name(id='x', ctx=Store()), iter=Name(id='numbers', ctx=Load()), - ifs=[], is_async=0)])) - >>> print(ast.dump(ast.parse('{x: x**2 for x in numbers}', mode='eval'), indent=4)) + >>> print(ast.dump( + ... ast.parse('{x: x**2 for x in numbers}', mode='eval'), + ... indent=4, + ... )) Expression( body=DictComp( key=Name(id='x', ctx=Load()), @@ -751,9 +749,11 @@ Comprehensions comprehension( target=Name(id='x', ctx=Store()), iter=Name(id='numbers', ctx=Load()), - ifs=[], is_async=0)])) - >>> print(ast.dump(ast.parse('{x for x in numbers}', mode='eval'), indent=4)) + >>> print(ast.dump( + ... ast.parse('{x for x in numbers}', mode='eval'), + ... indent=4, + ... )) Expression( body=SetComp( elt=Name(id='x', ctx=Load()), @@ -761,7 +761,6 @@ Comprehensions comprehension( target=Name(id='x', ctx=Store()), iter=Name(id='numbers', ctx=Load()), - ifs=[], is_async=0)])) @@ -784,18 +783,15 @@ Comprehensions elt=Call( func=Name(id='ord', ctx=Load()), args=[ - Name(id='c', ctx=Load())], - keywords=[]), + Name(id='c', ctx=Load())]), generators=[ comprehension( target=Name(id='line', ctx=Store()), iter=Name(id='file', ctx=Load()), - ifs=[], is_async=0), comprehension( target=Name(id='c', ctx=Store()), iter=Name(id='line', ctx=Load()), - ifs=[], is_async=0)])) >>> print(ast.dump(ast.parse('(n**2 for n in it if n>5 if n<10)', mode='eval'), @@ -834,7 +830,6 @@ Comprehensions comprehension( target=Name(id='i', ctx=Store()), iter=Name(id='soc', ctx=Load()), - ifs=[], is_async=1)])) @@ -864,8 +859,7 @@ Statements targets=[ Name(id='a', ctx=Store()), Name(id='b', ctx=Store())], - value=Constant(value=1))], - type_ignores=[]) + value=Constant(value=1))]) >>> print(ast.dump(ast.parse('a,b = c'), indent=4)) # Unpacking Module( @@ -877,8 +871,7 @@ Statements Name(id='a', ctx=Store()), Name(id='b', ctx=Store())], ctx=Store())], - value=Name(id='c', ctx=Load()))], - type_ignores=[]) + value=Name(id='c', ctx=Load()))]) .. class:: AnnAssign(target, annotation, value, simple) @@ -898,8 +891,7 @@ Statements AnnAssign( target=Name(id='c', ctx=Store()), annotation=Name(id='int', ctx=Load()), - simple=1)], - type_ignores=[]) + simple=1)]) >>> print(ast.dump(ast.parse('(a): int = 1'), indent=4)) # Annotation with parenthesis Module( @@ -908,8 +900,7 @@ Statements target=Name(id='a', ctx=Store()), annotation=Name(id='int', ctx=Load()), value=Constant(value=1), - simple=0)], - type_ignores=[]) + simple=0)]) >>> print(ast.dump(ast.parse('a.b: int'), indent=4)) # Attribute annotation Module( @@ -920,8 +911,7 @@ Statements attr='b', ctx=Store()), annotation=Name(id='int', ctx=Load()), - simple=0)], - type_ignores=[]) + simple=0)]) >>> print(ast.dump(ast.parse('a[1]: int'), indent=4)) # Subscript annotation Module( @@ -932,8 +922,7 @@ Statements slice=Constant(value=1), ctx=Store()), annotation=Name(id='int', ctx=Load()), - simple=0)], - type_ignores=[]) + simple=0)]) .. class:: AugAssign(target, op, value) @@ -954,8 +943,7 @@ Statements AugAssign( target=Name(id='x', ctx=Store()), op=Add(), - value=Constant(value=2))], - type_ignores=[]) + value=Constant(value=2))]) .. class:: Raise(exc, cause) @@ -971,8 +959,7 @@ Statements body=[ Raise( exc=Name(id='x', ctx=Load()), - cause=Name(id='y', ctx=Load()))], - type_ignores=[]) + cause=Name(id='y', ctx=Load()))]) .. class:: Assert(test, msg) @@ -987,8 +974,7 @@ Statements body=[ Assert( test=Name(id='x', ctx=Load()), - msg=Name(id='y', ctx=Load()))], - type_ignores=[]) + msg=Name(id='y', ctx=Load()))]) .. class:: Delete(targets) @@ -1005,8 +991,7 @@ Statements targets=[ Name(id='x', ctx=Del()), Name(id='y', ctx=Del()), - Name(id='z', ctx=Del())])], - type_ignores=[]) + Name(id='z', ctx=Del())])]) .. class:: Pass() @@ -1018,8 +1003,7 @@ Statements >>> print(ast.dump(ast.parse('pass'), indent=4)) Module( body=[ - Pass()], - type_ignores=[]) + Pass()]) .. class:: TypeAlias(name, type_params, value) @@ -1036,9 +1020,7 @@ Statements body=[ TypeAlias( name=Name(id='Alias', ctx=Store()), - type_params=[], - value=Name(id='int', ctx=Load()))], - type_ignores=[]) + value=Name(id='int', ctx=Load()))]) .. versionadded:: 3.12 @@ -1061,8 +1043,7 @@ Imports names=[ alias(name='x'), alias(name='y'), - alias(name='z')])], - type_ignores=[]) + alias(name='z')])]) .. class:: ImportFrom(module, names, level) @@ -1083,8 +1064,7 @@ Imports alias(name='x'), alias(name='y'), alias(name='z')], - level=0)], - type_ignores=[]) + level=0)]) .. class:: alias(name, asname) @@ -1102,8 +1082,7 @@ Imports names=[ alias(name='a', asname='b'), alias(name='c')], - level=2)], - type_ignores=[]) + level=2)]) Control flow ^^^^^^^^^^^^ @@ -1146,8 +1125,7 @@ Control flow value=Constant(value=Ellipsis))], orelse=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. class:: For(target, iter, body, orelse, type_comment) @@ -1181,8 +1159,7 @@ Control flow value=Constant(value=Ellipsis))], orelse=[ Expr( - value=Constant(value=Ellipsis))])], - type_ignores=[]) + value=Constant(value=Ellipsis))])]) .. class:: While(test, body, orelse) @@ -1207,8 +1184,7 @@ Control flow value=Constant(value=Ellipsis))], orelse=[ Expr( - value=Constant(value=Ellipsis))])], - type_ignores=[]) + value=Constant(value=Ellipsis))])]) .. class:: Break @@ -1242,9 +1218,7 @@ Control flow body=[ Break()], orelse=[ - Continue()])], - orelse=[])], - type_ignores=[]) + Continue()])])]) .. class:: Try(body, handlers, orelse, finalbody) @@ -1289,8 +1263,7 @@ Control flow value=Constant(value=Ellipsis))], finalbody=[ Expr( - value=Constant(value=Ellipsis))])], - type_ignores=[]) + value=Constant(value=Ellipsis))])]) .. class:: TryStar(body, handlers, orelse, finalbody) @@ -1318,10 +1291,7 @@ Control flow type=Name(id='Exception', ctx=Load()), body=[ Expr( - value=Constant(value=Ellipsis))])], - orelse=[], - finalbody=[])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.11 @@ -1353,10 +1323,7 @@ Control flow ExceptHandler( type=Name(id='TypeError', ctx=Load()), body=[ - Pass()])], - orelse=[], - finalbody=[])], - type_ignores=[]) + Pass()])])]) .. class:: With(items, body, type_comment) @@ -1398,9 +1365,7 @@ Control flow func=Name(id='something', ctx=Load()), args=[ Name(id='b', ctx=Load()), - Name(id='d', ctx=Load())], - keywords=[]))])], - type_ignores=[]) + Name(id='d', ctx=Load())]))])]) Pattern matching @@ -1457,14 +1422,10 @@ Pattern matching value=Constant(value=Ellipsis))]), match_case( pattern=MatchClass( - cls=Name(id='tuple', ctx=Load()), - patterns=[], - kwd_attrs=[], - kwd_patterns=[]), + cls=Name(id='tuple', ctx=Load())), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1492,8 +1453,7 @@ Pattern matching value=Constant(value='Relevant')), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1519,8 +1479,7 @@ Pattern matching pattern=MatchSingleton(value=None), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1552,8 +1511,7 @@ Pattern matching value=Constant(value=2))]), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1594,8 +1552,7 @@ Pattern matching MatchStar()]), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1639,11 +1596,10 @@ Pattern matching Expr( value=Constant(value=Ellipsis))]), match_case( - pattern=MatchMapping(keys=[], patterns=[], rest='rest'), + pattern=MatchMapping(rest='rest'), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1685,16 +1641,13 @@ Pattern matching MatchValue( value=Constant(value=0)), MatchValue( - value=Constant(value=0))], - kwd_attrs=[], - kwd_patterns=[]), + value=Constant(value=0))]), body=[ Expr( value=Constant(value=Ellipsis))]), match_case( pattern=MatchClass( cls=Name(id='Point3D', ctx=Load()), - patterns=[], kwd_attrs=[ 'x', 'y', @@ -1708,8 +1661,7 @@ Pattern matching value=Constant(value=0))]), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1751,8 +1703,7 @@ Pattern matching pattern=MatchAs(), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1785,8 +1736,7 @@ Pattern matching MatchAs(name='y')]), body=[ Expr( - value=Constant(value=Ellipsis))])])], - type_ignores=[]) + value=Constant(value=Ellipsis))])])]) .. versionadded:: 3.10 @@ -1818,8 +1768,7 @@ aliases. value=Subscript( value=Name(id='list', ctx=Load()), slice=Name(id='T', ctx=Load()), - ctx=Load()))], - type_ignores=[]) + ctx=Load()))]) .. versionadded:: 3.12 @@ -1843,8 +1792,7 @@ aliases. Name(id='P', ctx=Load()), Name(id='int', ctx=Load())], ctx=Load()), - ctx=Load()))], - type_ignores=[]) + ctx=Load()))]) .. versionadded:: 3.12 @@ -1869,8 +1817,7 @@ aliases. value=Name(id='Ts', ctx=Load()), ctx=Load())], ctx=Load()), - ctx=Load()))], - type_ignores=[]) + ctx=Load()))]) .. versionadded:: 3.12 @@ -1910,15 +1857,10 @@ Function and class definitions Expr( value=Lambda( args=arguments( - posonlyargs=[], args=[ arg(arg='x'), - arg(arg='y')], - kwonlyargs=[], - kw_defaults=[], - defaults=[]), - body=Constant(value=Ellipsis)))], - type_ignores=[]) + arg(arg='y')]), + body=Constant(value=Ellipsis)))]) .. class:: arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults) @@ -1957,7 +1899,6 @@ Function and class definitions FunctionDef( name='f', args=arguments( - posonlyargs=[], args=[ arg( arg='a', @@ -1980,9 +1921,7 @@ Function and class definitions decorator_list=[ Name(id='decorator1', ctx=Load()), Name(id='decorator2', ctx=Load())], - returns=Constant(value='return annotation'), - type_params=[])], - type_ignores=[]) + returns=Constant(value='return annotation'))]) .. class:: Return(value) @@ -1995,8 +1934,7 @@ Function and class definitions Module( body=[ Return( - value=Constant(value=4))], - type_ignores=[]) + value=Constant(value=4))]) .. class:: Yield(value) @@ -2012,16 +1950,14 @@ Function and class definitions body=[ Expr( value=Yield( - value=Name(id='x', ctx=Load())))], - type_ignores=[]) + value=Name(id='x', ctx=Load())))]) >>> print(ast.dump(ast.parse('yield from x'), indent=4)) Module( body=[ Expr( value=YieldFrom( - value=Name(id='x', ctx=Load())))], - type_ignores=[]) + value=Name(id='x', ctx=Load())))]) .. class:: Global(names) @@ -2038,8 +1974,7 @@ Function and class definitions names=[ 'x', 'y', - 'z'])], - type_ignores=[]) + 'z'])]) >>> print(ast.dump(ast.parse('nonlocal x,y,z'), indent=4)) Module( @@ -2048,8 +1983,7 @@ Function and class definitions names=[ 'x', 'y', - 'z'])], - type_ignores=[]) + 'z'])]) .. class:: ClassDef(name, bases, keywords, body, decorator_list, type_params) @@ -2089,9 +2023,7 @@ Function and class definitions Pass()], decorator_list=[ Name(id='decorator1', ctx=Load()), - Name(id='decorator2', ctx=Load())], - type_params=[])], - type_ignores=[]) + Name(id='decorator2', ctx=Load())])]) .. versionchanged:: 3.12 Added ``type_params``. @@ -2123,22 +2055,12 @@ Async and await body=[ AsyncFunctionDef( name='f', - args=arguments( - posonlyargs=[], - args=[], - kwonlyargs=[], - kw_defaults=[], - defaults=[]), + args=arguments(), body=[ Expr( value=Await( value=Call( - func=Name(id='other_func', ctx=Load()), - args=[], - keywords=[])))], - decorator_list=[], - type_params=[])], - type_ignores=[]) + func=Name(id='other_func', ctx=Load()))))])]) .. class:: AsyncFor(target, iter, body, orelse, type_comment) @@ -2425,7 +2347,7 @@ and classes for traversing abstract syntax trees: node = YourTransformer().visit(node) -.. function:: dump(node, annotate_fields=True, include_attributes=False, *, indent=None) +.. function:: dump(node, annotate_fields=True, include_attributes=False, *, indent=None, show_empty=False) Return a formatted dump of the tree in *node*. This is mainly useful for debugging purposes. If *annotate_fields* is true (by default), @@ -2442,9 +2364,42 @@ and classes for traversing abstract syntax trees: indents that many spaces per level. If *indent* is a string (such as ``"\t"``), that string is used to indent each level. + If *show_empty* is ``False`` (the default), empty lists and fields that are ``None`` + will be omitted from the output. + .. versionchanged:: 3.9 Added the *indent* option. + .. versionchanged:: 3.13 + Added the *show_empty* option. + + .. doctest:: + + >>> print(ast.dump(ast.parse("""\ + ... async def f(): + ... await other_func() + ... """), indent=4, show_empty=True)) + Module( + body=[ + AsyncFunctionDef( + name='f', + args=arguments( + posonlyargs=[], + args=[], + kwonlyargs=[], + kw_defaults=[], + defaults=[]), + body=[ + Expr( + value=Await( + value=Call( + func=Name(id='other_func', ctx=Load()), + args=[], + keywords=[])))], + decorator_list=[], + type_params=[])], + type_ignores=[]) + .. _ast-compiler-flags: diff --git a/Lib/ast.py b/Lib/ast.py index b8c4ce6f919e6b..9f386051659e76 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -114,7 +114,11 @@ def _convert(node): return _convert(node_or_string) -def dump(node, annotate_fields=True, include_attributes=False, *, indent=None): +def dump( + node, annotate_fields=True, include_attributes=False, + *, + indent=None, show_empty=False, +): """ Return a formatted dump of the tree in node. This is mainly useful for debugging purposes. If annotate_fields is true (by default), @@ -125,6 +129,8 @@ def dump(node, annotate_fields=True, include_attributes=False, *, indent=None): include_attributes can be set to true. If indent is a non-negative integer or string, then the tree will be pretty-printed with that indent level. None (the default) selects the single line representation. + If show_empty is False, then empty lists and fields that are None + will be omitted from the output for better readability. """ def _format(node, level=0): if indent is not None: @@ -137,6 +143,7 @@ def _format(node, level=0): if isinstance(node, AST): cls = type(node) args = [] + args_buffer = [] allsimple = True keywords = annotate_fields for name in node._fields: @@ -148,6 +155,18 @@ def _format(node, level=0): if value is None and getattr(cls, name, ...) is None: keywords = True continue + if ( + not show_empty + and (value is None or value == []) + # Special cases: + # `Constant(value=None)` and `MatchSingleton(value=None)` + and not isinstance(node, (Constant, MatchSingleton)) + ): + args_buffer.append(repr(value)) + continue + elif not keywords: + args.extend(args_buffer) + args_buffer = [] value, simple = _format(value, level) allsimple = allsimple and simple if keywords: diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 5b47cdaafb092e..44bcb9bae1cfde 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -1227,21 +1227,20 @@ def test_dump(self): node = ast.parse('spam(eggs, "and cheese")') self.assertEqual(ast.dump(node), "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), " - "args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')], " - "keywords=[]))], type_ignores=[])" + "args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')]))])" ) self.assertEqual(ast.dump(node, annotate_fields=False), "Module([Expr(Call(Name('spam', Load()), [Name('eggs', Load()), " - "Constant('and cheese')], []))], [])" + "Constant('and cheese')]))])" ) self.assertEqual(ast.dump(node, include_attributes=True), "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), " "lineno=1, col_offset=0, end_lineno=1, end_col_offset=4), " "args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=5, " "end_lineno=1, end_col_offset=9), Constant(value='and cheese', " - "lineno=1, col_offset=11, end_lineno=1, end_col_offset=23)], keywords=[], " + "lineno=1, col_offset=11, end_lineno=1, end_col_offset=23)], " "lineno=1, col_offset=0, end_lineno=1, end_col_offset=24), " - "lineno=1, col_offset=0, end_lineno=1, end_col_offset=24)], type_ignores=[])" + "lineno=1, col_offset=0, end_lineno=1, end_col_offset=24)])" ) def test_dump_indent(self): @@ -1254,9 +1253,7 @@ def test_dump_indent(self): func=Name(id='spam', ctx=Load()), args=[ Name(id='eggs', ctx=Load()), - Constant(value='and cheese')], - keywords=[]))], - type_ignores=[])""") + Constant(value='and cheese')]))])""") self.assertEqual(ast.dump(node, annotate_fields=False, indent='\t'), """\ Module( \t[ @@ -1265,9 +1262,7 @@ def test_dump_indent(self): \t\t\t\tName('spam', Load()), \t\t\t\t[ \t\t\t\t\tName('eggs', Load()), -\t\t\t\t\tConstant('and cheese')], -\t\t\t\t[]))], -\t[])""") +\t\t\t\t\tConstant('and cheese')]))])""") self.assertEqual(ast.dump(node, include_attributes=True, indent=3), """\ Module( body=[ @@ -1294,7 +1289,6 @@ def test_dump_indent(self): col_offset=11, end_lineno=1, end_col_offset=23)], - keywords=[], lineno=1, col_offset=0, end_lineno=1, @@ -1302,8 +1296,7 @@ def test_dump_indent(self): lineno=1, col_offset=0, end_lineno=1, - end_col_offset=24)], - type_ignores=[])""") + end_col_offset=24)])""") def test_dump_incomplete(self): node = ast.Raise(lineno=3, col_offset=4) @@ -1333,6 +1326,119 @@ def test_dump_incomplete(self): self.assertEqual(ast.dump(node, annotate_fields=False), "Raise(cause=Name('e', Load()))" ) + # Arguments: + node = ast.arguments(args=[ast.arg("x")]) + self.assertEqual(ast.dump(node, annotate_fields=False), + "arguments([], [arg('x')])", + ) + node = ast.arguments(posonlyargs=[ast.arg("x")]) + self.assertEqual(ast.dump(node, annotate_fields=False), + "arguments([arg('x')])", + ) + node = ast.arguments(posonlyargs=[ast.arg("x")], kwonlyargs=[ast.arg('y')]) + self.assertEqual(ast.dump(node, annotate_fields=False), + "arguments([arg('x')], kwonlyargs=[arg('y')])", + ) + node = ast.arguments(args=[ast.arg("x")], kwonlyargs=[ast.arg('y')]) + self.assertEqual(ast.dump(node, annotate_fields=False), + "arguments([], [arg('x')], kwonlyargs=[arg('y')])", + ) + node = ast.arguments() + self.assertEqual(ast.dump(node, annotate_fields=False), + "arguments()", + ) + # Classes: + node = ast.ClassDef( + 'T', + [], + [ast.keyword('a', ast.Constant(None))], + [], + [ast.Name('dataclass')], + ) + self.assertEqual(ast.dump(node), + "ClassDef(name='T', keywords=[keyword(arg='a', value=Constant(value=None))], decorator_list=[Name(id='dataclass')])", + ) + self.assertEqual(ast.dump(node, annotate_fields=False), + "ClassDef('T', [], [keyword('a', Constant(None))], [], [Name('dataclass')])", + ) + + def test_dump_show_empty(self): + def check_node(node, empty, full, **kwargs): + with self.subTest(show_empty=False): + self.assertEqual( + ast.dump(node, show_empty=False, **kwargs), + empty, + ) + with self.subTest(show_empty=True): + self.assertEqual( + ast.dump(node, show_empty=True, **kwargs), + full, + ) + + def check_text(code, empty, full, **kwargs): + check_node(ast.parse(code), empty, full, **kwargs) + + check_node( + ast.arguments(), + empty="arguments()", + full="arguments(posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[])", + ) + + check_node( + # Corner case: there are no real `Name` instances with `id=''`: + ast.Name(id='', ctx=ast.Load()), + empty="Name(id='', ctx=Load())", + full="Name(id='', ctx=Load())", + ) + + check_node( + ast.MatchSingleton(value=None), + empty="MatchSingleton(value=None)", + full="MatchSingleton(value=None)", + ) + + check_node( + ast.Constant(value=None), + empty="Constant(value=None)", + full="Constant(value=None)", + ) + + check_node( + ast.Constant(value=''), + empty="Constant(value='')", + full="Constant(value='')", + ) + + check_text( + "def a(b: int = 0, *, c): ...", + empty="Module(body=[FunctionDef(name='a', args=arguments(args=[arg(arg='b', annotation=Name(id='int', ctx=Load()))], kwonlyargs=[arg(arg='c')], kw_defaults=[None], defaults=[Constant(value=0)]), body=[Expr(value=Constant(value=Ellipsis))])])", + full="Module(body=[FunctionDef(name='a', args=arguments(posonlyargs=[], args=[arg(arg='b', annotation=Name(id='int', ctx=Load()))], kwonlyargs=[arg(arg='c')], kw_defaults=[None], defaults=[Constant(value=0)]), body=[Expr(value=Constant(value=Ellipsis))], decorator_list=[], type_params=[])], type_ignores=[])", + ) + + check_text( + "def a(b: int = 0, *, c): ...", + empty="Module(body=[FunctionDef(name='a', args=arguments(args=[arg(arg='b', annotation=Name(id='int', ctx=Load(), lineno=1, col_offset=9, end_lineno=1, end_col_offset=12), lineno=1, col_offset=6, end_lineno=1, end_col_offset=12)], kwonlyargs=[arg(arg='c', lineno=1, col_offset=21, end_lineno=1, end_col_offset=22)], kw_defaults=[None], defaults=[Constant(value=0, lineno=1, col_offset=15, end_lineno=1, end_col_offset=16)]), body=[Expr(value=Constant(value=Ellipsis, lineno=1, col_offset=25, end_lineno=1, end_col_offset=28), lineno=1, col_offset=25, end_lineno=1, end_col_offset=28)], lineno=1, col_offset=0, end_lineno=1, end_col_offset=28)])", + full="Module(body=[FunctionDef(name='a', args=arguments(posonlyargs=[], args=[arg(arg='b', annotation=Name(id='int', ctx=Load(), lineno=1, col_offset=9, end_lineno=1, end_col_offset=12), lineno=1, col_offset=6, end_lineno=1, end_col_offset=12)], kwonlyargs=[arg(arg='c', lineno=1, col_offset=21, end_lineno=1, end_col_offset=22)], kw_defaults=[None], defaults=[Constant(value=0, lineno=1, col_offset=15, end_lineno=1, end_col_offset=16)]), body=[Expr(value=Constant(value=Ellipsis, lineno=1, col_offset=25, end_lineno=1, end_col_offset=28), lineno=1, col_offset=25, end_lineno=1, end_col_offset=28)], decorator_list=[], type_params=[], lineno=1, col_offset=0, end_lineno=1, end_col_offset=28)], type_ignores=[])", + include_attributes=True, + ) + + check_text( + 'spam(eggs, "and cheese")', + empty="Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')]))])", + full="Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')], keywords=[]))], type_ignores=[])", + ) + + check_text( + 'spam(eggs, text="and cheese")', + empty="Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='eggs', ctx=Load())], keywords=[keyword(arg='text', value=Constant(value='and cheese'))]))])", + full="Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='eggs', ctx=Load())], keywords=[keyword(arg='text', value=Constant(value='and cheese'))]))], type_ignores=[])", + ) + + check_text( + "import _ast as ast; from module import sub", + empty="Module(body=[Import(names=[alias(name='_ast', asname='ast')]), ImportFrom(module='module', names=[alias(name='sub')], level=0)])", + full="Module(body=[Import(names=[alias(name='_ast', asname='ast')]), ImportFrom(module='module', names=[alias(name='sub')], level=0)], type_ignores=[])", + ) def test_copy_location(self): src = ast.parse('1 + 1', mode='eval') @@ -1361,14 +1467,13 @@ def test_fix_missing_locations(self): "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), " "lineno=1, col_offset=0, end_lineno=1, end_col_offset=5), " "args=[Constant(value='spam', lineno=1, col_offset=6, end_lineno=1, " - "end_col_offset=12)], keywords=[], lineno=1, col_offset=0, end_lineno=1, " + "end_col_offset=12)], lineno=1, col_offset=0, end_lineno=1, " "end_col_offset=13), lineno=1, col_offset=0, end_lineno=1, " "end_col_offset=13), Expr(value=Call(func=Name(id='spam', ctx=Load(), " "lineno=1, col_offset=0, end_lineno=1, end_col_offset=0), " "args=[Constant(value='eggs', lineno=1, col_offset=0, end_lineno=1, " - "end_col_offset=0)], keywords=[], lineno=1, col_offset=0, end_lineno=1, " - "end_col_offset=0), lineno=1, col_offset=0, end_lineno=1, end_col_offset=0)], " - "type_ignores=[])" + "end_col_offset=0)], lineno=1, col_offset=0, end_lineno=1, " + "end_col_offset=0), lineno=1, col_offset=0, end_lineno=1, end_col_offset=0)])" ) def test_increment_lineno(self): diff --git a/Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst b/Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst new file mode 100644 index 00000000000000..bebb67e585eea6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst @@ -0,0 +1,3 @@ +Don't show empty fields (value ``None`` or ``[]``) +in :func:`ast.dump` by default. Add ``show_empty=False`` +parameter to optionally show them. From 0aa0fc3d3ca144f979c684552a56a18ed8f558e4 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:46:17 +0100 Subject: [PATCH 028/217] gh-117901: Add option for compiler's codegen to save nested instruction sequences for introspection (#118007) --- .../internal/pycore_instruction_sequence.h | 1 + Lib/test/test_compiler_codegen.py | 96 ++++++++++++++++++- ...-04-17-22-53-52.gh-issue-117901.SsEcVJ.rst | 1 + Python/compile.c | 18 ++++ 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-17-22-53-52.gh-issue-117901.SsEcVJ.rst diff --git a/Include/internal/pycore_instruction_sequence.h b/Include/internal/pycore_instruction_sequence.h index ecba0d9d8e996e..d6a79616db71fa 100644 --- a/Include/internal/pycore_instruction_sequence.h +++ b/Include/internal/pycore_instruction_sequence.h @@ -61,6 +61,7 @@ _PyJumpTargetLabel _PyInstructionSequence_NewLabel(_PyInstructionSequence *seq); int _PyInstructionSequence_ApplyLabelMap(_PyInstructionSequence *seq); int _PyInstructionSequence_InsertInstruction(_PyInstructionSequence *seq, int pos, int opcode, int oparg, _Py_SourceLocation loc); +int _PyInstructionSequence_AddNested(_PyInstructionSequence *seq, _PyInstructionSequence *nested); void PyInstructionSequence_Fini(_PyInstructionSequence *seq); extern PyTypeObject _PyInstructionSequence_Type; diff --git a/Lib/test/test_compiler_codegen.py b/Lib/test/test_compiler_codegen.py index 166294a40c1cb7..1088b4aa9e624d 100644 --- a/Lib/test/test_compiler_codegen.py +++ b/Lib/test/test_compiler_codegen.py @@ -1,4 +1,5 @@ +import textwrap from test.support.bytecode_helper import CodegenTestCase # Tests for the code-generation stage of the compiler. @@ -6,11 +7,19 @@ class IsolatedCodeGenTests(CodegenTestCase): + def assertInstructionsMatch_recursive(self, insts, expected_insts): + expected_nested = [i for i in expected_insts if isinstance(i, list)] + expected_insts = [i for i in expected_insts if not isinstance(i, list)] + self.assertInstructionsMatch(insts, expected_insts) + self.assertEqual(len(insts.get_nested()), len(expected_nested)) + for n_insts, n_expected in zip(insts.get_nested(), expected_nested): + self.assertInstructionsMatch_recursive(n_insts, n_expected) + def codegen_test(self, snippet, expected_insts): import ast a = ast.parse(snippet, "my_file.py", "exec") insts = self.generate_code(a) - self.assertInstructionsMatch(insts, expected_insts) + self.assertInstructionsMatch_recursive(insts, expected_insts) def test_if_expression(self): snippet = "42 if True else 24" @@ -55,6 +64,91 @@ def test_for_loop(self): ] self.codegen_test(snippet, expected) + def test_function(self): + snippet = textwrap.dedent(""" + def f(x): + return x + 42 + """) + expected = [ + # Function definition + ('RESUME', 0), + ('LOAD_CONST', 0), + ('MAKE_FUNCTION', None), + ('STORE_NAME', 0), + ('LOAD_CONST', 1), + ('RETURN_VALUE', None), + [ + # Function body + ('RESUME', 0), + ('LOAD_FAST', 0), + ('LOAD_CONST', 1), + ('BINARY_OP', 0), + ('RETURN_VALUE', None), + ('LOAD_CONST', 0), + ('RETURN_VALUE', None), + ] + ] + self.codegen_test(snippet, expected) + + def test_nested_functions(self): + snippet = textwrap.dedent(""" + def f(): + def h(): + return 12 + def g(): + x = 1 + y = 2 + z = 3 + u = 4 + return 42 + """) + expected = [ + # Function definition + ('RESUME', 0), + ('LOAD_CONST', 0), + ('MAKE_FUNCTION', None), + ('STORE_NAME', 0), + ('LOAD_CONST', 1), + ('RETURN_VALUE', None), + [ + # Function body + ('RESUME', 0), + ('LOAD_CONST', 1), + ('MAKE_FUNCTION', None), + ('STORE_FAST', 0), + ('LOAD_CONST', 2), + ('MAKE_FUNCTION', None), + ('STORE_FAST', 1), + ('LOAD_CONST', 0), + ('RETURN_VALUE', None), + [ + ('RESUME', 0), + ('NOP', None), + ('LOAD_CONST', 1), + ('RETURN_VALUE', None), + ('LOAD_CONST', 0), + ('RETURN_VALUE', None), + ], + [ + ('RESUME', 0), + ('LOAD_CONST', 1), + ('STORE_FAST', 0), + ('LOAD_CONST', 2), + ('STORE_FAST', 1), + ('LOAD_CONST', 3), + ('STORE_FAST', 2), + ('LOAD_CONST', 4), + ('STORE_FAST', 3), + ('NOP', None), + ('LOAD_CONST', 5), + ('RETURN_VALUE', None), + ('LOAD_CONST', 0), + ('RETURN_VALUE', None), + ], + ], + ] + self.codegen_test(snippet, expected) + def test_syntax_error__return_not_in_function(self): snippet = "return 42" with self.assertRaisesRegex(SyntaxError, "'return' outside function"): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-17-22-53-52.gh-issue-117901.SsEcVJ.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-17-22-53-52.gh-issue-117901.SsEcVJ.rst new file mode 100644 index 00000000000000..1e412690deecd7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-17-22-53-52.gh-issue-117901.SsEcVJ.rst @@ -0,0 +1 @@ +Add option for compiler's codegen to save nested instruction sequences for introspection. diff --git a/Python/compile.c b/Python/compile.c index 3d856b7e4ddd97..ca5551a8e64ab0 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -285,6 +285,10 @@ struct compiler { struct compiler_unit *u; /* compiler state for current block */ PyObject *c_stack; /* Python list holding compiler_unit ptrs */ PyArena *c_arena; /* pointer to memory allocation arena */ + + bool c_save_nested_seqs; /* if true, construct recursive instruction sequences + * (including instructions for nested code objects) + */ }; #define INSTR_SEQUENCE(C) ((C)->u->u_instr_sequence) @@ -402,6 +406,7 @@ compiler_setup(struct compiler *c, mod_ty mod, PyObject *filename, c->c_flags = *flags; c->c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level : optimize; c->c_nestlevel = 0; + c->c_save_nested_seqs = false; if (!_PyAST_Optimize(mod, arena, c->c_optimize, merged)) { return ERROR; @@ -1290,6 +1295,11 @@ compiler_exit_scope(struct compiler *c) // Don't call PySequence_DelItem() with an exception raised PyObject *exc = PyErr_GetRaisedException(); + instr_sequence *nested_seq = NULL; + if (c->c_save_nested_seqs) { + nested_seq = c->u->u_instr_sequence; + Py_INCREF(nested_seq); + } c->c_nestlevel--; compiler_unit_free(c->u); /* Restore c->u to the parent unit. */ @@ -1303,10 +1313,17 @@ compiler_exit_scope(struct compiler *c) PyErr_FormatUnraisable("Exception ignored on removing " "the last compiler stack item"); } + if (nested_seq != NULL) { + if (_PyInstructionSequence_AddNested(c->u->u_instr_sequence, nested_seq) < 0) { + PyErr_FormatUnraisable("Exception ignored on appending " + "nested instruction sequence"); + } + } } else { c->u = NULL; } + Py_XDECREF(nested_seq); PyErr_SetRaisedException(exc); } @@ -7734,6 +7751,7 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags, _PyArena_Free(arena); return NULL; } + c->c_save_nested_seqs = true; metadata = PyDict_New(); if (metadata == NULL) { From f6e5cc66bef74346b4a6490f9d0c623e7a736e03 Mon Sep 17 00:00:00 2001 From: rindeal Date: Wed, 24 Apr 2024 11:13:35 +0000 Subject: [PATCH 029/217] no-issue: devcontainer: update to Fedora 40 (gh-118161) --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index fa30aee478cf33..6f8fe005621c88 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/fedora:37 +FROM docker.io/library/fedora:40 ENV CC=clang From 975081b11e052c9f8deb42c5876104651736302e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:27:40 +0300 Subject: [PATCH 030/217] gh-117225: Add color to doctest output (#117583) Co-authored-by: Alex Waygood --- Lib/doctest.py | 49 ++++++++++++++---- Lib/test/support/__init__.py | 2 +- Lib/test/test_doctest/test_doctest.py | 51 +++++++++++++++++-- Lib/traceback.py | 4 ++ ...-04-06-18-41-36.gh-issue-117225.tJh1Hw.rst | 1 + 5 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-06-18-41-36.gh-issue-117225.tJh1Hw.rst diff --git a/Lib/doctest.py b/Lib/doctest.py index 4e362cbb9c9d6b..a3b42fdfb12254 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -104,6 +104,7 @@ def _test(): import unittest from io import StringIO, IncrementalNewlineDecoder from collections import namedtuple +from traceback import _ANSIColors, _can_colorize class TestResults(namedtuple('TestResults', 'failed attempted')): @@ -1179,6 +1180,9 @@ class DocTestRunner: The `run` method is used to process a single DocTest case. It returns a TestResults instance. + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> tests = DocTestFinder().find(_TestClass) >>> runner = DocTestRunner(verbose=False) >>> tests.sort(key = lambda test: test.name) @@ -1229,6 +1233,8 @@ class DocTestRunner: can be also customized by subclassing DocTestRunner, and overriding the methods `report_start`, `report_success`, `report_unexpected_exception`, and `report_failure`. + + >>> traceback._COLORIZE = save_colorize """ # This divider string is used to separate failure messages, and to # separate sections of the summary. @@ -1307,7 +1313,10 @@ def report_unexpected_exception(self, out, test, example, exc_info): 'Exception raised:\n' + _indent(_exception_traceback(exc_info))) def _failure_header(self, test, example): - out = [self.DIVIDER] + red, reset = ( + (_ANSIColors.RED, _ANSIColors.RESET) if _can_colorize() else ("", "") + ) + out = [f"{red}{self.DIVIDER}{reset}"] if test.filename: if test.lineno is not None and example.lineno is not None: lineno = test.lineno + example.lineno + 1 @@ -1592,6 +1601,21 @@ def summarize(self, verbose=None): else: failed.append((name, (failures, tries, skips))) + if _can_colorize(): + bold_green = _ANSIColors.BOLD_GREEN + bold_red = _ANSIColors.BOLD_RED + green = _ANSIColors.GREEN + red = _ANSIColors.RED + reset = _ANSIColors.RESET + yellow = _ANSIColors.YELLOW + else: + bold_green = "" + bold_red = "" + green = "" + red = "" + reset = "" + yellow = "" + if verbose: if notests: print(f"{_n_items(notests)} had no tests:") @@ -1600,13 +1624,13 @@ def summarize(self, verbose=None): print(f" {name}") if passed: - print(f"{_n_items(passed)} passed all tests:") + print(f"{green}{_n_items(passed)} passed all tests:{reset}") for name, count in sorted(passed): s = "" if count == 1 else "s" - print(f" {count:3d} test{s} in {name}") + print(f" {green}{count:3d} test{s} in {name}{reset}") if failed: - print(self.DIVIDER) + print(f"{red}{self.DIVIDER}{reset}") print(f"{_n_items(failed)} had failures:") for name, (failures, tries, skips) in sorted(failed): print(f" {failures:3d} of {tries:3d} in {name}") @@ -1615,18 +1639,21 @@ def summarize(self, verbose=None): s = "" if total_tries == 1 else "s" print(f"{total_tries} test{s} in {_n_items(self._stats)}.") - and_f = f" and {total_failures} failed" if total_failures else "" - print(f"{total_tries - total_failures} passed{and_f}.") + and_f = ( + f" and {red}{total_failures} failed{reset}" + if total_failures else "" + ) + print(f"{green}{total_tries - total_failures} passed{reset}{and_f}.") if total_failures: s = "" if total_failures == 1 else "s" - msg = f"***Test Failed*** {total_failures} failure{s}" + msg = f"{bold_red}***Test Failed*** {total_failures} failure{s}{reset}" if total_skips: s = "" if total_skips == 1 else "s" - msg = f"{msg} and {total_skips} skipped test{s}" + msg = f"{msg} and {yellow}{total_skips} skipped test{s}{reset}" print(f"{msg}.") elif verbose: - print("Test passed.") + print(f"{bold_green}Test passed.{reset}") return TestResults(total_failures, total_tries, skipped=total_skips) @@ -1644,7 +1671,7 @@ def merge(self, other): d[name] = (failures, tries, skips) -def _n_items(items: list) -> str: +def _n_items(items: list | dict) -> str: """ Helper to pluralise the number of items in a list. """ @@ -1655,7 +1682,7 @@ def _n_items(items: list) -> str: class OutputChecker: """ - A class used to check the whether the actual output from a doctest + A class used to check whether the actual output from a doctest example matches the expected output. `OutputChecker` defines two methods: `check_output`, which compares a given pair of outputs, and returns true if they match; and `output_difference`, which diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index be3f93ab2e5fd1..6eb0f84b02ea22 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -26,7 +26,7 @@ "Error", "TestFailed", "TestDidNotRun", "ResourceDenied", # io "record_original_stdout", "get_original_stdout", "captured_stdout", - "captured_stdin", "captured_stderr", + "captured_stdin", "captured_stderr", "captured_output", # unittest "is_resource_enabled", "requires", "requires_freebsd_version", "requires_gil_enabled", "requires_linux_version", "requires_mac_ver", diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index cba4b16d544a20..0f1e584e22a888 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -16,6 +16,7 @@ import tempfile import types import contextlib +import traceback def doctest_skip_if(condition): @@ -470,7 +471,7 @@ def basics(): r""" >>> tests = finder.find(sample_func) >>> print(tests) # doctest: +ELLIPSIS - [] + [] The exact name depends on how test_doctest was invoked, so allow for leading path components. @@ -892,6 +893,9 @@ def basics(): r""" DocTestRunner is used to run DocTest test cases, and to accumulate statistics. Here's a simple DocTest case we can use: + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> def f(x): ... ''' ... >>> x = 12 @@ -946,6 +950,8 @@ def basics(): r""" 6 ok TestResults(failed=1, attempted=3) + + >>> traceback._COLORIZE = save_colorize """ def verbose_flag(): r""" The `verbose` flag makes the test runner generate more detailed @@ -1021,6 +1027,9 @@ def exceptions(): r""" lines between the first line and the type/value may be omitted or replaced with any other string: + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> def f(x): ... ''' ... >>> x = 12 @@ -1251,6 +1260,8 @@ def exceptions(): r""" ... ZeroDivisionError: integer division or modulo by zero TestResults(failed=1, attempted=1) + + >>> traceback._COLORIZE = save_colorize """ def displayhook(): r""" Test that changing sys.displayhook doesn't matter for doctest. @@ -1292,6 +1303,9 @@ def optionflags(): r""" The DONT_ACCEPT_TRUE_FOR_1 flag disables matches between True/False and 1/0: + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> def f(x): ... '>>> True\n1\n' @@ -1711,6 +1725,7 @@ def optionflags(): r""" Clean up. >>> del doctest.OPTIONFLAGS_BY_NAME[unlikely] + >>> traceback._COLORIZE = save_colorize """ @@ -1721,6 +1736,9 @@ def option_directives(): r""" single example. To turn an option on for an example, follow that example with a comment of the form ``# doctest: +OPTION``: + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> def f(x): r''' ... >>> print(list(range(10))) # should fail: no ellipsis ... [0, 1, ..., 9] @@ -1928,6 +1946,8 @@ def option_directives(): r""" >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) Traceback (most recent call last): ValueError: line 0 of the doctest for s has an option directive on a line with no example: '# doctest: +ELLIPSIS' + + >>> traceback._COLORIZE = save_colorize """ def test_testsource(): r""" @@ -2011,6 +2031,9 @@ def test_pdb_set_trace(): with a version that restores stdout. This is necessary for you to see debugger output. + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> doc = ''' ... >>> x = 42 ... >>> raise Exception('clé') @@ -2065,7 +2088,7 @@ def test_pdb_set_trace(): ... finally: ... sys.stdin = real_stdin --Return-- - > (3)calls_set_trace()->None + > (3)calls_set_trace()->None -> import pdb; pdb.set_trace() (Pdb) print(y) 2 @@ -2133,6 +2156,8 @@ def test_pdb_set_trace(): Got: 9 TestResults(failed=1, attempted=3) + + >>> traceback._COLORIZE = save_colorize """ def test_pdb_set_trace_nested(): @@ -2667,7 +2692,10 @@ def test_testfile(): r""" called with the name of a file, which is taken to be relative to the calling module. The return value is (#failures, #tests). -We don't want `-v` in sys.argv for these tests. +We don't want color or `-v` in sys.argv for these tests. + + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False >>> save_argv = sys.argv >>> if '-v' in sys.argv: @@ -2835,6 +2863,7 @@ def test_testfile(): r""" TestResults(failed=0, attempted=2) >>> doctest.master = None # Reset master. >>> sys.argv = save_argv + >>> traceback._COLORIZE = save_colorize """ class TestImporter(importlib.abc.MetaPathFinder, importlib.abc.ResourceLoader): @@ -2972,6 +3001,9 @@ def test_testmod(): r""" def test_unicode(): """ Check doctest with a non-ascii filename: + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> doc = ''' ... >>> raise Exception('clé') ... ''' @@ -2997,8 +3029,11 @@ def test_unicode(): """ raise Exception('clé') Exception: clé TestResults(failed=1, attempted=1) + + >>> traceback._COLORIZE = save_colorize """ + @doctest_skip_if(not support.has_subprocess_support) def test_CLI(): r""" The doctest module can be used to run doctests against an arbitrary file. @@ -3290,6 +3325,9 @@ def test_run_doctestsuite_multiple_times(): def test_exception_with_note(note): """ + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> test_exception_with_note('Note') Traceback (most recent call last): ... @@ -3339,6 +3377,8 @@ def test_exception_with_note(note): ValueError: message note TestResults(failed=1, attempted=...) + + >>> traceback._COLORIZE = save_colorize """ exc = ValueError('Text') exc.add_note(note) @@ -3419,6 +3459,9 @@ def test_syntax_error_subclass_from_stdlib(): def test_syntax_error_with_incorrect_expected_note(): """ + >>> save_colorize = traceback._COLORIZE + >>> traceback._COLORIZE = False + >>> def f(x): ... r''' ... >>> exc = SyntaxError("error", ("x.py", 23, None, "bad syntax")) @@ -3447,6 +3490,8 @@ def test_syntax_error_with_incorrect_expected_note(): note1 note2 TestResults(failed=1, attempted=...) + + >>> traceback._COLORIZE = save_colorize """ diff --git a/Lib/traceback.py b/Lib/traceback.py index d27c7a726d2bb6..054def57c21482 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -448,8 +448,12 @@ class _ANSIColors: BOLD_RED = '\x1b[1;31m' MAGENTA = '\x1b[35m' BOLD_MAGENTA = '\x1b[1;35m' + GREEN = "\x1b[32m" + BOLD_GREEN = "\x1b[1;32m" GREY = '\x1b[90m' RESET = '\x1b[0m' + YELLOW = "\x1b[33m" + class StackSummary(list): """A list of FrameSummary objects, representing a stack of frames.""" diff --git a/Misc/NEWS.d/next/Library/2024-04-06-18-41-36.gh-issue-117225.tJh1Hw.rst b/Misc/NEWS.d/next/Library/2024-04-06-18-41-36.gh-issue-117225.tJh1Hw.rst new file mode 100644 index 00000000000000..6a0da1c3bc9388 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-06-18-41-36.gh-issue-117225.tJh1Hw.rst @@ -0,0 +1 @@ +Add colour to doctest output. Patch by Hugo van Kemenade. From 7d369d471cf2b067c4d795d70b75201c48b46f5b Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 24 Apr 2024 13:20:19 +0100 Subject: [PATCH 031/217] GH-117536: GH-117894: fix athrow().throw(...) unawaited warning (GH-117851) --- Lib/test/test_asyncgen.py | 83 ++++++++++++++++--- ...-04-13-16-55-53.gh-issue-117536.xkVbfv.rst | 1 + ...-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst | 1 + Objects/genobject.c | 9 +- 4 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-13-16-55-53.gh-issue-117536.xkVbfv.rst create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 39605dca3886c8..a1e9e1b89c6a2c 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -399,9 +399,8 @@ async def gen(): with self.assertWarns(DeprecationWarning): x = gen().athrow(GeneratorExit, GeneratorExit(), None) - with self.assertWarnsRegex(RuntimeWarning, - f"coroutine method 'athrow' of '{gen.__qualname__}' " - f"was never awaited"): + with self.assertRaises(GeneratorExit): + x.send(None) del x gc_collect() @@ -1572,11 +1571,6 @@ async def main(): self.assertIsInstance(message['exception'], ZeroDivisionError) self.assertIn('unhandled exception during asyncio.run() shutdown', message['message']) - with self.assertWarnsRegex(RuntimeWarning, - f"coroutine method 'aclose' of '{async_iterate.__qualname__}' " - f"was never awaited"): - del message, messages - gc_collect() def test_async_gen_expression_01(self): async def arange(n): @@ -1630,10 +1624,6 @@ async def main(): asyncio.run(main()) self.assertEqual([], messages) - with self.assertWarnsRegex(RuntimeWarning, - f"coroutine method 'aclose' of '{async_iterate.__qualname__}' " - f"was never awaited"): - gc_collect() def test_async_gen_await_same_anext_coro_twice(self): async def async_iterate(): @@ -1671,6 +1661,62 @@ async def run(): self.loop.run_until_complete(run()) + def test_async_gen_throw_same_aclose_coro_twice(self): + async def async_iterate(): + yield 1 + yield 2 + + it = async_iterate() + nxt = it.aclose() + with self.assertRaises(StopIteration): + nxt.throw(GeneratorExit) + + with self.assertRaisesRegex( + RuntimeError, + r"cannot reuse already awaited aclose\(\)/athrow\(\)" + ): + nxt.throw(GeneratorExit) + + def test_async_gen_throw_custom_same_aclose_coro_twice(self): + async def async_iterate(): + yield 1 + yield 2 + + it = async_iterate() + + class MyException(Exception): + pass + + nxt = it.aclose() + with self.assertRaises(MyException): + nxt.throw(MyException) + + with self.assertRaisesRegex( + RuntimeError, + r"cannot reuse already awaited aclose\(\)/athrow\(\)" + ): + nxt.throw(MyException) + + def test_async_gen_throw_custom_same_athrow_coro_twice(self): + async def async_iterate(): + yield 1 + yield 2 + + it = async_iterate() + + class MyException(Exception): + pass + + nxt = it.athrow(MyException) + with self.assertRaises(MyException): + nxt.throw(MyException) + + with self.assertRaisesRegex( + RuntimeError, + r"cannot reuse already awaited aclose\(\)/athrow\(\)" + ): + nxt.throw(MyException) + def test_async_gen_aclose_twice_with_different_coros(self): # Regression test for https://bugs.python.org/issue39606 async def async_iterate(): @@ -1752,6 +1798,19 @@ async def gen(): g.aclose() gc_collect() + def test_aclose_throw(self): + async def gen(): + return + yield + + class MyException(Exception): + pass + + g = gen() + with self.assertRaises(MyException): + g.aclose().throw(MyException) + del g + gc_collect() if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-13-16-55-53.gh-issue-117536.xkVbfv.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-13-16-55-53.gh-issue-117536.xkVbfv.rst new file mode 100644 index 00000000000000..2492fd163cb549 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-13-16-55-53.gh-issue-117536.xkVbfv.rst @@ -0,0 +1 @@ +Fix a :exc:`RuntimeWarning` when calling ``agen.aclose().throw(Exception)``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst new file mode 100644 index 00000000000000..bd32500a54ee21 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst @@ -0,0 +1 @@ +Prevent ``agen.aclose()`` objects being re-used after ``.throw()``. diff --git a/Objects/genobject.c b/Objects/genobject.c index 8d1dbb72ba9ec2..5d7da49cfca166 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -2208,7 +2208,11 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na retval = gen_throw((PyGenObject*)o->agt_gen, args, nargs); if (o->agt_args) { - return async_gen_unwrap_value(o->agt_gen, retval); + retval = async_gen_unwrap_value(o->agt_gen, retval); + if (retval == NULL) { + o->agt_state = AWAITABLE_STATE_CLOSED; + } + return retval; } else { /* aclose() mode */ if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) { @@ -2218,6 +2222,9 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na PyErr_SetString(PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG); return NULL; } + if (retval == NULL) { + o->agt_state = AWAITABLE_STATE_CLOSED; + } if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) || PyErr_ExceptionMatches(PyExc_GeneratorExit)) { From 77cd0428b698a743844179f7babead43b2794d77 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 24 Apr 2024 14:37:55 +0100 Subject: [PATCH 032/217] GH-118095: Convert DEOPT_IFs on likely side exits to EXIT_IFs (GH-118106) Covert DEOPT_IFs on likely side exits to EXIT_IFs --- Include/internal/pycore_opcode_metadata.h | 10 +++++----- Include/internal/pycore_uop_metadata.h | 14 +++++++------- Python/bytecodes.c | 22 +++++++++++----------- Python/optimizer.c | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index e75de9d12e0c55..5636debbf4a7f2 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -984,7 +984,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [CACHE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_ALLOC_AND_ENTER_INIT] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [CALL_BUILTIN_CLASS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_BUILTIN_FAST] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1000,7 +1000,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_NOARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_O] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [CALL_PY_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [CALL_PY_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [CALL_PY_WITH_DEFAULTS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [CALL_STR_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_TUPLE_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1036,9 +1036,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [FORMAT_WITH_SPEC] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, - [FOR_ITER_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_DEOPT_FLAG }, - [FOR_ITER_RANGE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG }, - [FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_DEOPT_FLAG }, + [FOR_ITER_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG }, + [FOR_ITER_RANGE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG }, [GET_AITER] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [GET_ANEXT] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [GET_AWAITABLE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 4d15be6317d615..2da4c4d4e21e93 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -171,14 +171,14 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GET_ITER] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_YIELD_FROM_ITER] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_FOR_ITER_TIER_TWO] = HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, - [_ITER_CHECK_LIST] = HAS_DEOPT_FLAG, - [_GUARD_NOT_EXHAUSTED_LIST] = HAS_DEOPT_FLAG, + [_ITER_CHECK_LIST] = HAS_EXIT_FLAG, + [_GUARD_NOT_EXHAUSTED_LIST] = HAS_EXIT_FLAG, [_ITER_NEXT_LIST] = 0, - [_ITER_CHECK_TUPLE] = HAS_DEOPT_FLAG, - [_GUARD_NOT_EXHAUSTED_TUPLE] = HAS_DEOPT_FLAG, + [_ITER_CHECK_TUPLE] = HAS_EXIT_FLAG, + [_GUARD_NOT_EXHAUSTED_TUPLE] = HAS_EXIT_FLAG, [_ITER_NEXT_TUPLE] = 0, - [_ITER_CHECK_RANGE] = HAS_DEOPT_FLAG, - [_GUARD_NOT_EXHAUSTED_RANGE] = HAS_DEOPT_FLAG, + [_ITER_CHECK_RANGE] = HAS_EXIT_FLAG, + [_GUARD_NOT_EXHAUSTED_RANGE] = HAS_EXIT_FLAG, [_ITER_NEXT_RANGE] = HAS_ERROR_FLAG, [_WITH_EXCEPT_START] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_PUSH_EXC_INFO] = 0, @@ -194,7 +194,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG, [_CHECK_PEP_523] = HAS_DEOPT_FLAG, - [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, + [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_INIT_CALL_PY_EXACT_ARGS_0] = HAS_PURE_FLAG, [_INIT_CALL_PY_EXACT_ARGS_1] = HAS_PURE_FLAG, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 4541eb635da015..1f908a99107293 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2628,7 +2628,7 @@ dummy_func( } op(_ITER_CHECK_LIST, (iter -- iter)) { - DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type); + EXIT_IF(Py_TYPE(iter) != &PyListIter_Type); } replaced op(_ITER_JUMP_LIST, (iter -- iter)) { @@ -2657,8 +2657,8 @@ dummy_func( _PyListIterObject *it = (_PyListIterObject *)iter; assert(Py_TYPE(iter) == &PyListIter_Type); PyListObject *seq = it->it_seq; - DEOPT_IF(seq == NULL); - DEOPT_IF((size_t)it->it_index >= (size_t)PyList_GET_SIZE(seq)); + EXIT_IF(seq == NULL); + EXIT_IF((size_t)it->it_index >= (size_t)PyList_GET_SIZE(seq)); } op(_ITER_NEXT_LIST, (iter -- iter, next)) { @@ -2677,7 +2677,7 @@ dummy_func( _ITER_NEXT_LIST; op(_ITER_CHECK_TUPLE, (iter -- iter)) { - DEOPT_IF(Py_TYPE(iter) != &PyTupleIter_Type); + EXIT_IF(Py_TYPE(iter) != &PyTupleIter_Type); } replaced op(_ITER_JUMP_TUPLE, (iter -- iter)) { @@ -2703,8 +2703,8 @@ dummy_func( _PyTupleIterObject *it = (_PyTupleIterObject *)iter; assert(Py_TYPE(iter) == &PyTupleIter_Type); PyTupleObject *seq = it->it_seq; - DEOPT_IF(seq == NULL); - DEOPT_IF(it->it_index >= PyTuple_GET_SIZE(seq)); + EXIT_IF(seq == NULL); + EXIT_IF(it->it_index >= PyTuple_GET_SIZE(seq)); } op(_ITER_NEXT_TUPLE, (iter -- iter, next)) { @@ -2724,7 +2724,7 @@ dummy_func( op(_ITER_CHECK_RANGE, (iter -- iter)) { _PyRangeIterObject *r = (_PyRangeIterObject *)iter; - DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type); + EXIT_IF(Py_TYPE(r) != &PyRangeIter_Type); } replaced op(_ITER_JUMP_RANGE, (iter -- iter)) { @@ -2744,7 +2744,7 @@ dummy_func( op(_GUARD_NOT_EXHAUSTED_RANGE, (iter -- iter)) { _PyRangeIterObject *r = (_PyRangeIterObject *)iter; assert(Py_TYPE(r) == &PyRangeIter_Type); - DEOPT_IF(r->len <= 0); + EXIT_IF(r->len <= 0); } op(_ITER_NEXT_RANGE, (iter -- iter, next)) { @@ -3145,11 +3145,11 @@ dummy_func( } op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { - DEOPT_IF(!PyFunction_Check(callable)); + EXIT_IF(!PyFunction_Check(callable)); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version); + EXIT_IF(func->func_version != func_version); PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL)); + EXIT_IF(code->co_argcount != oparg + (self_or_null != NULL)); } op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { diff --git a/Python/optimizer.c b/Python/optimizer.c index 0017965c32f290..6a8b4f3c9504ad 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -696,7 +696,7 @@ translate_bytecode_to_trace( if (expansion->nuops > 0) { // Reserve space for nuops (+ _SET_IP + _EXIT_TRACE) int nuops = expansion->nuops; - RESERVE(nuops); + RESERVE(nuops + 1); /* One extra for exit */ if (expansion->uops[nuops-1].uop == _POP_FRAME) { // Check for trace stack underflow now: // We can't bail e.g. in the middle of From 83235f7791fbe6ee2618192f2341de9cd22d0511 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 24 Apr 2024 14:41:30 +0100 Subject: [PATCH 033/217] GH-115419: Move setting the instruction pointer to error exit stubs (GH-118088) --- Lib/test/test_capi/test_opt.py | 2 +- Python/bytecodes.c | 3 ++- Python/executor_cases.c.h | 2 ++ Python/optimizer.c | 1 + Python/optimizer_analysis.c | 3 --- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index ae23eadb8aafa0..c004f463770019 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -231,7 +231,7 @@ def testfunc(x): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_SET_IP", uops) + self.assertIn("_JUMP_TO_TOP", uops) self.assertIn("_LOAD_FAST_0", uops) def test_extended_arg(self): diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 1f908a99107293..c31617d35b02f5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4226,7 +4226,8 @@ dummy_func( EXIT_TO_TRACE(); } - tier2 op(_ERROR_POP_N, (unused[oparg] --)) { + tier2 op(_ERROR_POP_N, (target/2, unused[oparg] --)) { + frame->instr_ptr = ((_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive) + target; SYNC_SP(); GOTO_UNWIND(); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 43b022107a9ae6..7403d6fdaf0e2b 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4210,6 +4210,8 @@ case _ERROR_POP_N: { oparg = CURRENT_OPARG(); + uint32_t target = (uint32_t)CURRENT_OPERAND(); + frame->instr_ptr = ((_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive) + target; stack_pointer += -oparg; GOTO_UNWIND(); break; diff --git a/Python/optimizer.c b/Python/optimizer.c index 6a8b4f3c9504ad..b17c2998e2504b 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -978,6 +978,7 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) current_error_target = target; make_exit(&buffer[next_spare], _ERROR_POP_N, 0); buffer[next_spare].oparg = popped; + buffer[next_spare].operand = target; next_spare++; } buffer[i].error_target = current_error; diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 76de6e50f1f786..a76edd62c94c13 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -554,9 +554,6 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size) needs_ip = true; may_have_escaped = true; } - if (_PyUop_Flags[opcode] & HAS_ERROR_FLAG) { - needs_ip = true; - } if (needs_ip && last_set_ip >= 0) { if (buffer[last_set_ip].opcode == _CHECK_VALIDITY) { buffer[last_set_ip].opcode = _CHECK_VALIDITY_AND_SET_IP; From 8227883d1f1bbb6560e5f175d7ee49f013c094bd Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 24 Apr 2024 15:55:02 +0100 Subject: [PATCH 034/217] gh-118013: Use weakrefs for the cache key in `inspect._shadowed_dict` (#118202) --- Doc/whatsnew/3.12.rst | 7 +++--- Lib/inspect.py | 25 ++++++++++++++++--- Lib/test/libregrtest/utils.py | 2 +- Lib/test/test_inspect/test_inspect.py | 24 ++++++++++++++++++ ...-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst | 9 +++++++ 5 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index ce3d9ec6a29de8..8757311a484257 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -734,8 +734,7 @@ inspect * The performance of :func:`inspect.getattr_static` has been considerably improved. Most calls to the function should be at least 2x faster than they - were in Python 3.11, and some may be 6x faster or more. (Contributed by Alex - Waygood in :gh:`103193`.) + were in Python 3.11. (Contributed by Alex Waygood in :gh:`103193`.) itertools --------- @@ -1006,8 +1005,8 @@ typing :func:`runtime-checkable protocols ` has changed significantly. Most ``isinstance()`` checks against protocols with only a few members should be at least 2x faster than in 3.11, and some may be 20x - faster or more. However, ``isinstance()`` checks against protocols with fourteen - or more members may be slower than in Python 3.11. (Contributed by Alex + faster or more. However, ``isinstance()`` checks against protocols with many + members may be slower than in Python 3.11. (Contributed by Alex Waygood in :gh:`74690` and :gh:`103193`.) * All :data:`typing.TypedDict` and :data:`typing.NamedTuple` classes now have the diff --git a/Lib/inspect.py b/Lib/inspect.py index 422c09a92ad141..3c346b27b1f06d 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -160,6 +160,7 @@ from keyword import iskeyword from operator import attrgetter from collections import namedtuple, OrderedDict +from weakref import ref as make_weakref # Create constants for the compiler flags in Include/code.h # We try to get them from dis to avoid duplication @@ -1832,9 +1833,16 @@ def _check_class(klass, attr): return entry.__dict__[attr] return _sentinel + @functools.lru_cache() -def _shadowed_dict_from_mro_tuple(mro): - for entry in mro: +def _shadowed_dict_from_weakref_mro_tuple(*weakref_mro): + for weakref_entry in weakref_mro: + # Normally we'd have to check whether the result of weakref_entry() + # is None here, in case the object the weakref is pointing to has died. + # In this specific case, however, we know that the only caller of this + # function is `_shadowed_dict()`, and that therefore this weakref is + # guaranteed to point to an object that is still alive. + entry = weakref_entry() dunder_dict = _get_dunder_dict_of_class(entry) if '__dict__' in dunder_dict: class_dict = dunder_dict['__dict__'] @@ -1844,8 +1852,19 @@ def _shadowed_dict_from_mro_tuple(mro): return class_dict return _sentinel + def _shadowed_dict(klass): - return _shadowed_dict_from_mro_tuple(_static_getmro(klass)) + # gh-118013: the inner function here is decorated with lru_cache for + # performance reasons, *but* make sure not to pass strong references + # to the items in the mro. Doing so can lead to unexpected memory + # consumption in cases where classes are dynamically created and + # destroyed, and the dynamically created classes happen to be the only + # objects that hold strong references to other objects that take up a + # significant amount of memory. + return _shadowed_dict_from_weakref_mro_tuple( + *[make_weakref(entry) for entry in _static_getmro(klass)] + ) + def getattr_static(obj, attr, default=_sentinel): """Retrieve attributes without triggering dynamic lookup via the diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 791f996127ea58..8253d330b95b81 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -275,7 +275,7 @@ def clear_caches(): except KeyError: pass else: - inspect._shadowed_dict_from_mro_tuple.cache_clear() + inspect._shadowed_dict_from_weakref_mro_tuple.cache_clear() inspect._filesbymodname.clear() inspect.modulesbyfile.clear() diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index b2265e44e0c79b..169d1edb706fc3 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4,6 +4,7 @@ import copy import datetime import functools +import gc import importlib import inspect import io @@ -25,6 +26,7 @@ import unittest import unittest.mock import warnings +import weakref try: @@ -2302,6 +2304,13 @@ def __dict__(self): self.assertEqual(inspect.getattr_static(foo, 'a'), 3) self.assertFalse(test.called) + class Bar(Foo): pass + + bar = Bar() + bar.a = 5 + self.assertEqual(inspect.getattr_static(bar, 'a'), 3) + self.assertFalse(test.called) + def test_mutated_mro(self): test = self test.called = False @@ -2406,6 +2415,21 @@ def __getattribute__(self, attr): self.assertFalse(test.called) + def test_cache_does_not_cause_classes_to_persist(self): + # regression test for gh-118013: + # check that the internal _shadowed_dict cache does not cause + # dynamically created classes to have extended lifetimes even + # when no other strong references to those classes remain. + # Since these classes can themselves hold strong references to + # other objects, this can cause unexpected memory consumption. + class Foo: pass + Foo.instance = Foo() + weakref_to_class = weakref.ref(Foo) + inspect.getattr_static(Foo.instance, 'whatever', 'irrelevant') + del Foo + gc.collect() + self.assertIsNone(weakref_to_class()) + class TestGetGeneratorState(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst b/Misc/NEWS.d/next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst new file mode 100644 index 00000000000000..8eb68ebe99ba15 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst @@ -0,0 +1,9 @@ +Fix regression introduced in gh-103193 that meant that calling +:func:`inspect.getattr_static` on an instance would cause a strong reference +to that instance's class to persist in an internal cache in the +:mod:`inspect` module. This caused unexpected memory consumption if the +class was dynamically created, the class held strong references to other +objects which took up a significant amount of memory, and the cache +contained the sole strong reference to the class. The fix for the regression +leads to a slowdown in :func:`getattr_static`, but the function should still +be signficantly faster than it was in Python 3.11. Patch by Alex Waygood. From af3c1d817d3f8369f8003965d967332a3a721a25 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 24 Apr 2024 09:55:48 -0600 Subject: [PATCH 035/217] gh-117953: Cleanups For fix_up_extension() in import.c (gh-118192) These are cleanups I've pulled out of gh-118116. Mostly, this change moves code around to align with some future changes and to improve clarity a little. There is one very small change in behavior: we now add the module to the per-interpreter caches after updating the global state, rather than before. --- Include/internal/pycore_import.h | 2 + Python/import.c | 249 +++++++++++++++++++------------ Python/importdl.c | 5 + Python/pylifecycle.c | 2 +- Python/sysmodule.c | 2 +- 5 files changed, 163 insertions(+), 97 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 08af53258cde97..8d7f0543f8d315 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -24,10 +24,12 @@ extern int _PyImport_ReleaseLock(PyInterpreterState *interp); // This is used exclusively for the sys and builtins modules: extern int _PyImport_FixupBuiltin( + PyThreadState *tstate, PyObject *mod, const char *name, /* UTF-8 encoded string */ PyObject *modules ); +// We could probably drop this: extern int _PyImport_FixupExtensionObject(PyObject*, PyObject *, PyObject *, PyObject *); diff --git a/Python/import.c b/Python/import.c index 8cdc04f03dd201..30d8082607ab37 100644 --- a/Python/import.c +++ b/Python/import.c @@ -200,39 +200,54 @@ _PyImport_ClearModules(PyInterpreterState *interp) Py_SETREF(MODULES(interp), NULL); } +static inline PyObject * +get_modules_dict(PyThreadState *tstate, bool fatal) +{ + /* Technically, it would make sense to incref the dict, + * since sys.modules could be swapped out and decref'ed to 0 + * before the caller is done using it. However, that is highly + * unlikely, especially since we can rely on a global lock + * (i.e. the GIL) for thread-safety. */ + PyObject *modules = MODULES(tstate->interp); + if (modules == NULL) { + if (fatal) { + Py_FatalError("interpreter has no modules dictionary"); + } + _PyErr_SetString(tstate, PyExc_RuntimeError, + "unable to get sys.modules"); + return NULL; + } + return modules; +} + PyObject * PyImport_GetModuleDict(void) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (MODULES(interp) == NULL) { - Py_FatalError("interpreter has no modules dictionary"); - } - return MODULES(interp); + PyThreadState *tstate = _PyThreadState_GET(); + return get_modules_dict(tstate, true); } int _PyImport_SetModule(PyObject *name, PyObject *m) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *modules = MODULES(interp); + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *modules = get_modules_dict(tstate, true); return PyObject_SetItem(modules, name, m); } int _PyImport_SetModuleString(const char *name, PyObject *m) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *modules = MODULES(interp); + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *modules = get_modules_dict(tstate, true); return PyMapping_SetItemString(modules, name, m); } static PyObject * import_get_module(PyThreadState *tstate, PyObject *name) { - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, false); if (modules == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "unable to get sys.modules"); return NULL; } @@ -297,10 +312,8 @@ PyImport_GetModule(PyObject *name) static PyObject * import_add_module(PyThreadState *tstate, PyObject *name) { - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, false); if (modules == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "no import module dictionary"); return NULL; } @@ -397,7 +410,7 @@ remove_module(PyThreadState *tstate, PyObject *name) { PyObject *exc = _PyErr_GetRaisedException(tstate); - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, true); if (PyDict_CheckExact(modules)) { // Error is reported to the caller (void)PyDict_Pop(modules, name, NULL); @@ -618,77 +631,91 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) ...for single-phase init modules, where m_size == -1: (6). first time (not found in _PyRuntime.imports.extensions): - 1. _imp_create_dynamic_impl() -> import_find_extension() - 2. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() - 3. _PyImport_LoadDynamicModuleWithSpec(): load - 4. _PyImport_LoadDynamicModuleWithSpec(): call - 5. -> PyModule_Create() -> PyModule_Create2() -> PyModule_CreateInitialized() - 6. PyModule_CreateInitialized() -> PyModule_New() - 7. PyModule_CreateInitialized(): allocate mod->md_state - 8. PyModule_CreateInitialized() -> PyModule_AddFunctions() - 9. PyModule_CreateInitialized() -> PyModule_SetDocString() - 10. PyModule_CreateInitialized(): set mod->md_def - 11. : initialize the module - 12. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() - 13. _PyImport_LoadDynamicModuleWithSpec(): set def->m_base.m_init - 14. _PyImport_LoadDynamicModuleWithSpec(): set __file__ - 15. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_FixupExtensionObject() - 16. _PyImport_FixupExtensionObject(): add it to interp->imports.modules_by_index - 17. _PyImport_FixupExtensionObject(): copy __dict__ into def->m_base.m_copy - 18. _PyImport_FixupExtensionObject(): add it to _PyRuntime.imports.extensions + A. _imp_create_dynamic_impl() -> import_find_extension() + B. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() + C. _PyImport_LoadDynamicModuleWithSpec(): load + D. _PyImport_LoadDynamicModuleWithSpec(): call + E. -> PyModule_Create() -> PyModule_Create2() + -> PyModule_CreateInitialized() + F. PyModule_CreateInitialized() -> PyModule_New() + G. PyModule_CreateInitialized(): allocate mod->md_state + H. PyModule_CreateInitialized() -> PyModule_AddFunctions() + I. PyModule_CreateInitialized() -> PyModule_SetDocString() + J. PyModule_CreateInitialized(): set mod->md_def + K. : initialize the module, etc. + L. _PyImport_LoadDynamicModuleWithSpec() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + M. _PyImport_LoadDynamicModuleWithSpec(): set def->m_base.m_init + N. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_FixupExtensionObject() + O. _PyImport_FixupExtensionObject() -> update_global_state_for_extension() + P. update_global_state_for_extension(): + copy __dict__ into def->m_base.m_copy + Q. update_global_state_for_extension(): + add it to _PyRuntime.imports.extensions + R. _PyImport_FixupExtensionObject() -> finish_singlephase_extension() + S. finish_singlephase_extension(): + add it to interp->imports.modules_by_index + T. finish_singlephase_extension(): add it to sys.modules + U. _imp_create_dynamic_impl(): set __file__ + + Step (P) is skipped for core modules (sys/builtins). (6). subsequent times (found in _PyRuntime.imports.extensions): - 1. _imp_create_dynamic_impl() -> import_find_extension() - 2. import_find_extension() -> import_add_module() - 3. if name in sys.modules: use that module - 4. else: + A. _imp_create_dynamic_impl() -> import_find_extension() + B. import_find_extension() -> import_add_module() + C. if name in sys.modules: use that module + D. else: 1. import_add_module() -> PyModule_NewObject() 2. import_add_module(): set it on sys.modules - 5. import_find_extension(): copy the "m_copy" dict into __dict__ - 6. _imp_create_dynamic_impl() -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + E. import_find_extension(): copy the "m_copy" dict into __dict__ + F. _imp_create_dynamic_impl() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() (10). (every time): - 1. noop + A. noop ...for single-phase init modules, where m_size >= 0: (6). not main interpreter and never loaded there - every time (not found in _PyRuntime.imports.extensions): - 1-16. (same as for m_size == -1) + A-N. (same as for m_size == -1) + O-Q. (skipped) + R-U. (same as for m_size == -1) (6). main interpreter - first time (not found in _PyRuntime.imports.extensions): - 1-16. (same as for m_size == -1) - 17. _PyImport_FixupExtensionObject(): add it to _PyRuntime.imports.extensions + A-O. (same as for m_size == -1) + P. (skipped) + Q-U. (same as for m_size == -1) (6). previously loaded in main interpreter (found in _PyRuntime.imports.extensions): - 1. _imp_create_dynamic_impl() -> import_find_extension() - 2. import_find_extension(): call def->m_base.m_init - 3. import_find_extension(): add the module to sys.modules + A. _imp_create_dynamic_impl() -> import_find_extension() + B. import_find_extension(): call def->m_base.m_init + C. import_find_extension(): add the module to sys.modules (10). every time: - 1. noop + A. noop ...for multi-phase init modules: (6). every time: - 1. _imp_create_dynamic_impl() -> import_find_extension() (not found) - 2. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() - 3. _PyImport_LoadDynamicModuleWithSpec(): load module init func - 4. _PyImport_LoadDynamicModuleWithSpec(): call module init func - 5. _PyImport_LoadDynamicModuleWithSpec() -> PyModule_FromDefAndSpec() - 6. PyModule_FromDefAndSpec(): gather/check moduledef slots - 7. if there's a Py_mod_create slot: + A. _imp_create_dynamic_impl() -> import_find_extension() (not found) + B. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() + C. _PyImport_LoadDynamicModuleWithSpec(): load module init func + D. _PyImport_LoadDynamicModuleWithSpec(): call module init func + E. _PyImport_LoadDynamicModuleWithSpec() -> PyModule_FromDefAndSpec() + F. PyModule_FromDefAndSpec(): gather/check moduledef slots + G. if there's a Py_mod_create slot: 1. PyModule_FromDefAndSpec(): call its function - 8. else: + H. else: 1. PyModule_FromDefAndSpec() -> PyModule_NewObject() - 9: PyModule_FromDefAndSpec(): set mod->md_def - 10. PyModule_FromDefAndSpec() -> _add_methods_to_object() - 11. PyModule_FromDefAndSpec() -> PyModule_SetDocString() + I: PyModule_FromDefAndSpec(): set mod->md_def + J. PyModule_FromDefAndSpec() -> _add_methods_to_object() + K. PyModule_FromDefAndSpec() -> PyModule_SetDocString() (10). every time: - 1. _imp_exec_dynamic_impl() -> exec_builtin_or_dynamic() - 2. if mod->md_state == NULL (including if m_size == 0): + A. _imp_exec_dynamic_impl() -> exec_builtin_or_dynamic() + B. if mod->md_state == NULL (including if m_size == 0): 1. exec_builtin_or_dynamic() -> PyModule_ExecDef() 2. PyModule_ExecDef(): allocate mod->md_state 3. if there's a Py_mod_exec slot: @@ -894,7 +921,7 @@ extensions_lock_release(void) (module name, module name) (for built-in modules) or by (filename, module name) (for dynamically loaded modules), containing these modules. A copy of the module's dictionary is stored by calling - _PyImport_FixupExtensionObject() immediately after the module initialization + fix_up_extension() immediately after the module initialization function succeeds. A copy can be retrieved from there by calling import_find_extension(). @@ -1158,24 +1185,14 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) return 0; } + static int -fix_up_extension(PyObject *mod, PyObject *name, PyObject *path) +update_global_state_for_extension(PyThreadState *tstate, + PyObject *mod, PyModuleDef *def, + PyObject *name, PyObject *path) { - if (mod == NULL || !PyModule_Check(mod)) { - PyErr_BadInternalCall(); - return -1; - } - - struct PyModuleDef *def = PyModule_GetDef(mod); - if (!def) { - PyErr_BadInternalCall(); - return -1; - } - - PyThreadState *tstate = _PyThreadState_GET(); - if (_modules_by_index_set(tstate->interp, def, mod) < 0) { - return -1; - } + assert(mod != NULL && PyModule_Check(mod)); + assert(def == _PyModule_GetDef(mod)); // bpo-44050: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. @@ -1202,6 +1219,10 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *path) // XXX Why special-case the main interpreter? if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) { +#ifndef NDEBUG + PyModuleDef *cached = _extensions_cache_get(path, name); + assert(cached == NULL || cached == def); +#endif if (_extensions_cache_set(path, name, def) < 0) { return -1; } @@ -1210,15 +1231,50 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *path) return 0; } +/* For multi-phase init modules, the module is finished + * by PyModule_FromDefAndSpec(). */ +static int +finish_singlephase_extension(PyThreadState *tstate, + PyObject *mod, PyModuleDef *def, + PyObject *name, PyObject *modules) +{ + assert(mod != NULL && PyModule_Check(mod)); + assert(def == PyModule_GetDef(mod)); + + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { + return -1; + } + + if (modules != NULL) { + if (PyObject_SetItem(modules, name, mod) < 0) { + return -1; + } + } + + return 0; +} + int _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, PyObject *filename, PyObject *modules) { - if (PyObject_SetItem(modules, name, mod) < 0) { + if (mod == NULL || !PyModule_Check(mod)) { + PyErr_BadInternalCall(); return -1; } - if (fix_up_extension(mod, name, filename) < 0) { - PyMapping_DelItem(modules, name); + PyModuleDef *def = PyModule_GetDef(mod); + if (def == NULL) { + PyErr_BadInternalCall(); + return -1; + } + + PyThreadState *tstate = _PyThreadState_GET(); + if (update_global_state_for_extension( + tstate, mod, def, name, filename) < 0) + { + return -1; + } + if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) { return -1; } return 0; @@ -1245,7 +1301,7 @@ import_find_extension(PyThreadState *tstate, PyObject *name, } PyObject *mod, *mdict; - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, true); if (def->m_size == -1) { PyObject *m_copy = def->m_base.m_copy; @@ -1333,7 +1389,8 @@ clear_singlephase_extension(PyInterpreterState *interp, /*******************/ int -_PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) +_PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, + PyObject *modules) { int res = -1; assert(mod != NULL && PyModule_Check(mod)); @@ -1350,11 +1407,12 @@ _PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) goto finally; } - if (PyObject_SetItem(modules, nameobj, mod) < 0) { + if (update_global_state_for_extension( + tstate, mod, def, nameobj, nameobj) < 0) + { goto finally; } - if (fix_up_extension(mod, nameobj, nameobj) < 0) { - PyMapping_DelItem(modules, nameobj); + if (finish_singlephase_extension(tstate, mod, def, nameobj, modules) < 0) { goto finally; } @@ -1391,7 +1449,6 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) return mod; } - PyObject *modules = MODULES(tstate->interp); struct _inittab *found = NULL; for (struct _inittab *p = INITTAB; p->name != NULL; p++) { if (_PyUnicode_EqualToASCIIString(name, p->name)) { @@ -1419,14 +1476,22 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); } else { - /* Remember pointer to module init function. */ + assert(PyModule_Check(mod)); PyModuleDef *def = PyModule_GetDef(mod); if (def == NULL) { return NULL; } + /* Remember pointer to module init function. */ def->m_base.m_init = p0; - if (_PyImport_FixupExtensionObject(mod, name, name, modules) < 0) { + + if (update_global_state_for_extension( + tstate, mod, def, name, name) < 0) + { + return NULL; + } + PyObject *modules = get_modules_dict(tstate, true); + if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) { return NULL; } return mod; @@ -3783,12 +3848,6 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } mod = _PyImport_LoadDynamicModuleWithSpec(spec, fp); - if (mod != NULL) { - /* Remember the filename as the __file__ attribute */ - if (PyModule_AddObjectRef(mod, "__file__", filename) < 0) { - PyErr_Clear(); /* Not important enough to report */ - } - } // XXX Shouldn't this happen in the error cases too. if (fp) { diff --git a/Python/importdl.c b/Python/importdl.c index 7cf30bea3a861a..e512161d3071f2 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -226,6 +226,11 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) } def->m_base.m_init = p0; + /* Remember the filename as the __file__ attribute */ + if (PyModule_AddObjectRef(m, "__file__", filename) < 0) { + PyErr_Clear(); /* Not important enough to report */ + } + PyObject *modules = PyImport_GetModuleDict(); if (_PyImport_FixupExtensionObject(m, name_unicode, filename, modules) < 0) goto error; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index cc1824634e7a7f..0f3ca4a6687514 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -777,7 +777,7 @@ pycore_init_builtins(PyThreadState *tstate) } PyObject *modules = _PyImport_GetModules(interp); - if (_PyImport_FixupBuiltin(bimod, "builtins", modules) < 0) { + if (_PyImport_FixupBuiltin(tstate, bimod, "builtins", modules) < 0) { goto error; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 05ee4051a20e18..7af363678e8e86 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3764,7 +3764,7 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) return status; } - if (_PyImport_FixupBuiltin(sysmod, "sys", modules) < 0) { + if (_PyImport_FixupBuiltin(tstate, sysmod, "sys", modules) < 0) { goto error; } From 03e3e317231d67557191ee067cb7f192f3d4d092 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 24 Apr 2024 10:18:24 -0600 Subject: [PATCH 036/217] gh-76785: Rename _xxsubinterpreters to _interpreters (gh-117791) See https://discuss.python.org/t/pep-734-multiple-interpreters-in-the-stdlib/41147/26. --- Include/internal/pycore_atexit.h | 2 +- Include/internal/pycore_pybuffer.h | 2 +- Include/internal/pycore_pystate.h | 4 +- Include/internal/pycore_pythread.h | 4 +- Lib/test/support/import_helper.py | 2 +- Lib/test/support/interpreters/__init__.py | 4 +- Lib/test/support/interpreters/channels.py | 4 +- Lib/test/support/interpreters/queues.py | 4 +- ...erpchannels.py => test__interpchannels.py} | 844 +++++++++--------- ...binterpreters.py => test__interpreters.py} | 22 +- Lib/test/test_capi/test_misc.py | 2 +- Lib/test/test_import/__init__.py | 2 +- Lib/test/test_importlib/test_util.py | 2 +- Lib/test/test_interpreters/test_api.py | 16 +- Lib/test/test_interpreters/test_channels.py | 4 +- Lib/test/test_interpreters/test_lifecycle.py | 2 +- Lib/test/test_interpreters/test_queues.py | 2 +- Lib/test/test_interpreters/test_stress.py | 2 +- Lib/test/test_interpreters/utils.py | 2 +- Makefile.pre.in | 6 +- ...4-04-11-18-11-37.gh-issue-76785.BWNkhC.rst | 6 + Modules/Setup | 6 +- Modules/Setup.stdlib.in | 7 +- ...annelsmodule.c => _interpchannelsmodule.c} | 2 +- ...rpqueuesmodule.c => _interpqueuesmodule.c} | 2 +- ...rpretersmodule.c => _interpretersmodule.c} | 2 +- PC/config.c | 12 +- PCbuild/pythoncore.vcxproj | 6 +- PCbuild/pythoncore.vcxproj.filters | 6 +- Python/stdlib_module_names.h | 3 + Tools/build/generate_stdlib_module_names.py | 3 - Tools/c-analyzer/cpython/ignored.tsv | 10 +- configure | 84 +- configure.ac | 12 +- 34 files changed, 550 insertions(+), 543 deletions(-) rename Lib/test/{test__xxinterpchannels.py => test__interpchannels.py} (68%) rename Lib/test/{test__xxsubinterpreters.py => test__interpreters.py} (98%) create mode 100644 Misc/NEWS.d/next/Library/2024-04-11-18-11-37.gh-issue-76785.BWNkhC.rst rename Modules/{_xxinterpchannelsmodule.c => _interpchannelsmodule.c} (99%) rename Modules/{_xxinterpqueuesmodule.c => _interpqueuesmodule.c} (99%) rename Modules/{_xxsubinterpretersmodule.c => _interpretersmodule.c} (99%) diff --git a/Include/internal/pycore_atexit.h b/Include/internal/pycore_atexit.h index 4dcda8f517c787..507a5c03cbc792 100644 --- a/Include/internal/pycore_atexit.h +++ b/Include/internal/pycore_atexit.h @@ -54,7 +54,7 @@ struct atexit_state { int callback_len; }; -// Export for '_xxinterpchannels' shared extension +// Export for '_interpchannels' shared extension PyAPI_FUNC(int) _Py_AtExit( PyInterpreterState *interp, atexit_datacallbackfunc func, diff --git a/Include/internal/pycore_pybuffer.h b/Include/internal/pycore_pybuffer.h index 3cbc290b2ea3ee..9439d2bd770587 100644 --- a/Include/internal/pycore_pybuffer.h +++ b/Include/internal/pycore_pybuffer.h @@ -9,7 +9,7 @@ extern "C" { #endif -// Exported for the _xxinterpchannels module. +// Exported for the _interpchannels module. PyAPI_FUNC(int) _PyBuffer_ReleaseInInterpreter( PyInterpreterState *interp, Py_buffer *view); PyAPI_FUNC(int) _PyBuffer_ReleaseInInterpreterAndRawFree( diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index eb5b5fee59009c..a668d78b969bd9 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -77,10 +77,10 @@ _Py_IsMainInterpreterFinalizing(PyInterpreterState *interp) interp == &_PyRuntime._main_interpreter); } -// Export for _xxsubinterpreters module. +// Export for _interpreters module. PyAPI_FUNC(PyObject *) _PyInterpreterState_GetIDObject(PyInterpreterState *); -// Export for _xxsubinterpreters module. +// Export for _interpreters module. PyAPI_FUNC(int) _PyInterpreterState_SetRunningMain(PyInterpreterState *); PyAPI_FUNC(void) _PyInterpreterState_SetNotRunningMain(PyInterpreterState *); PyAPI_FUNC(int) _PyInterpreterState_IsRunningMain(PyInterpreterState *); diff --git a/Include/internal/pycore_pythread.h b/Include/internal/pycore_pythread.h index f032cb97388657..3610c6254db6af 100644 --- a/Include/internal/pycore_pythread.h +++ b/Include/internal/pycore_pythread.h @@ -99,7 +99,7 @@ extern void _PyThread_AfterFork(struct _pythread_runtime_state *state); // unset: -1 seconds, in nanoseconds #define PyThread_UNSET_TIMEOUT ((PyTime_t)(-1 * 1000 * 1000 * 1000)) -// Exported for the _xxinterpchannels module. +// Exported for the _interpchannels module. PyAPI_FUNC(int) PyThread_ParseTimeoutArg( PyObject *arg, int blocking, @@ -111,7 +111,7 @@ PyAPI_FUNC(int) PyThread_ParseTimeoutArg( * are returned, depending on whether the lock can be acquired within the * timeout. */ -// Exported for the _xxinterpchannels module. +// Exported for the _interpchannels module. PyAPI_FUNC(PyLockStatus) PyThread_acquire_lock_timed_with_retries( PyThread_type_lock, PY_TIMEOUT_T microseconds); diff --git a/Lib/test/support/import_helper.py b/Lib/test/support/import_helper.py index 29c6f535b40342..edcd2b9a35bbd9 100644 --- a/Lib/test/support/import_helper.py +++ b/Lib/test/support/import_helper.py @@ -114,7 +114,7 @@ def multi_interp_extensions_check(enabled=True): This only applies to modules that haven't been imported yet. It overrides the PyInterpreterConfig.check_multi_interp_extensions setting (see support.run_in_subinterp_with_config() and - _xxsubinterpreters.create()). + _interpreters.create()). Also see importlib.utils.allowing_all_extensions(). """ diff --git a/Lib/test/support/interpreters/__init__.py b/Lib/test/support/interpreters/__init__.py index 0a5a9259479be4..e067f259364d2a 100644 --- a/Lib/test/support/interpreters/__init__.py +++ b/Lib/test/support/interpreters/__init__.py @@ -2,10 +2,10 @@ import threading import weakref -import _xxsubinterpreters as _interpreters +import _interpreters # aliases: -from _xxsubinterpreters import ( +from _interpreters import ( InterpreterError, InterpreterNotFoundError, NotShareableError, is_shareable, ) diff --git a/Lib/test/support/interpreters/channels.py b/Lib/test/support/interpreters/channels.py index f7f523b1fc5a77..fbae7e634cf34d 100644 --- a/Lib/test/support/interpreters/channels.py +++ b/Lib/test/support/interpreters/channels.py @@ -1,10 +1,10 @@ """Cross-interpreter Channels High Level Module.""" import time -import _xxinterpchannels as _channels +import _interpchannels as _channels # aliases: -from _xxinterpchannels import ( +from _interpchannels import ( ChannelError, ChannelNotFoundError, ChannelClosedError, ChannelEmptyError, ChannelNotEmptyError, ) diff --git a/Lib/test/support/interpreters/queues.py b/Lib/test/support/interpreters/queues.py index 5849a1cc15e447..1b9e7481f2e313 100644 --- a/Lib/test/support/interpreters/queues.py +++ b/Lib/test/support/interpreters/queues.py @@ -4,10 +4,10 @@ import queue import time import weakref -import _xxinterpqueues as _queues +import _interpqueues as _queues # aliases: -from _xxinterpqueues import ( +from _interpqueues import ( QueueError, QueueNotFoundError, ) diff --git a/Lib/test/test__xxinterpchannels.py b/Lib/test/test__interpchannels.py similarity index 68% rename from Lib/test/test__xxinterpchannels.py rename to Lib/test/test__interpchannels.py index 3db0cb7e6e1d49..b76c58917c0b9c 100644 --- a/Lib/test/test__xxinterpchannels.py +++ b/Lib/test/test__interpchannels.py @@ -8,14 +8,14 @@ from test.support import import_helper -from test.test__xxsubinterpreters import ( +from test.test__interpreters import ( _interpreters, _run_output, clean_up_interpreters, ) -channels = import_helper.import_module('_xxinterpchannels') +_channels = import_helper.import_module('_interpchannels') # Additional tests are found in Lib/test/test_interpreters/test_channels.py. @@ -29,8 +29,8 @@ def recv_wait(cid): while True: try: - return channels.recv(cid) - except channels.ChannelEmptyError: + return _channels.recv(cid) + except _channels.ChannelEmptyError: time.sleep(0.1) #@contextmanager @@ -101,7 +101,7 @@ def __new__(cls, name=None, id=None): def expect_channel_closed(): try: yield - except channels.ChannelClosedError: + except _channels.ChannelClosedError: pass else: assert False, 'channel not closed' @@ -188,7 +188,7 @@ def run_action(cid, action, end, state, *, hideclosed=True): try: result = _run_action(cid, action, end, state) - except channels.ChannelClosedError: + except _channels.ChannelClosedError: if not hideclosed and not expectfail: raise result = state.close() @@ -201,18 +201,18 @@ def run_action(cid, action, end, state, *, hideclosed=True): def _run_action(cid, action, end, state): if action == 'use': if end == 'send': - channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) return state.incr() elif end == 'recv': if not state.pending: try: - channels.recv(cid) - except channels.ChannelEmptyError: + _channels.recv(cid) + except _channels.ChannelEmptyError: return state else: raise Exception('expected ChannelEmptyError') else: - channels.recv(cid) + _channels.recv(cid) return state.decr() else: raise ValueError(end) @@ -220,7 +220,7 @@ def _run_action(cid, action, end, state): kwargs = {} if end in ('recv', 'send'): kwargs[end] = True - channels.close(cid, **kwargs) + _channels.close(cid, **kwargs) return state.close() elif action == 'force-close': kwargs = { @@ -228,17 +228,17 @@ def _run_action(cid, action, end, state): } if end in ('recv', 'send'): kwargs[end] = True - channels.close(cid, **kwargs) + _channels.close(cid, **kwargs) return state.close(force=True) else: raise ValueError(action) def clean_up_channels(): - for cid in channels.list_all(): + for cid in _channels.list_all(): try: - channels.destroy(cid) - except channels.ChannelNotFoundError: + _channels.destroy(cid) + except _channels.ChannelNotFoundError: pass # already destroyed @@ -255,25 +255,25 @@ def tearDown(self): class ChannelIDTests(TestBase): def test_default_kwargs(self): - cid = channels._channel_id(10, force=True) + cid = _channels._channel_id(10, force=True) self.assertEqual(int(cid), 10) self.assertEqual(cid.end, 'both') def test_with_kwargs(self): - cid = channels._channel_id(10, send=True, force=True) + cid = _channels._channel_id(10, send=True, force=True) self.assertEqual(cid.end, 'send') - cid = channels._channel_id(10, send=True, recv=False, force=True) + cid = _channels._channel_id(10, send=True, recv=False, force=True) self.assertEqual(cid.end, 'send') - cid = channels._channel_id(10, recv=True, force=True) + cid = _channels._channel_id(10, recv=True, force=True) self.assertEqual(cid.end, 'recv') - cid = channels._channel_id(10, recv=True, send=False, force=True) + cid = _channels._channel_id(10, recv=True, send=False, force=True) self.assertEqual(cid.end, 'recv') - cid = channels._channel_id(10, send=True, recv=True, force=True) + cid = _channels._channel_id(10, send=True, recv=True, force=True) self.assertEqual(cid.end, 'both') def test_coerce_id(self): @@ -281,47 +281,47 @@ class Int(str): def __index__(self): return 10 - cid = channels._channel_id(Int(), force=True) + cid = _channels._channel_id(Int(), force=True) self.assertEqual(int(cid), 10) def test_bad_id(self): - self.assertRaises(TypeError, channels._channel_id, object()) - self.assertRaises(TypeError, channels._channel_id, 10.0) - self.assertRaises(TypeError, channels._channel_id, '10') - self.assertRaises(TypeError, channels._channel_id, b'10') - self.assertRaises(ValueError, channels._channel_id, -1) - self.assertRaises(OverflowError, channels._channel_id, 2**64) + self.assertRaises(TypeError, _channels._channel_id, object()) + self.assertRaises(TypeError, _channels._channel_id, 10.0) + self.assertRaises(TypeError, _channels._channel_id, '10') + self.assertRaises(TypeError, _channels._channel_id, b'10') + self.assertRaises(ValueError, _channels._channel_id, -1) + self.assertRaises(OverflowError, _channels._channel_id, 2**64) def test_bad_kwargs(self): with self.assertRaises(ValueError): - channels._channel_id(10, send=False, recv=False) + _channels._channel_id(10, send=False, recv=False) def test_does_not_exist(self): - cid = channels.create() - with self.assertRaises(channels.ChannelNotFoundError): - channels._channel_id(int(cid) + 1) # unforced + cid = _channels.create() + with self.assertRaises(_channels.ChannelNotFoundError): + _channels._channel_id(int(cid) + 1) # unforced def test_str(self): - cid = channels._channel_id(10, force=True) + cid = _channels._channel_id(10, force=True) self.assertEqual(str(cid), '10') def test_repr(self): - cid = channels._channel_id(10, force=True) + cid = _channels._channel_id(10, force=True) self.assertEqual(repr(cid), 'ChannelID(10)') - cid = channels._channel_id(10, send=True, force=True) + cid = _channels._channel_id(10, send=True, force=True) self.assertEqual(repr(cid), 'ChannelID(10, send=True)') - cid = channels._channel_id(10, recv=True, force=True) + cid = _channels._channel_id(10, recv=True, force=True) self.assertEqual(repr(cid), 'ChannelID(10, recv=True)') - cid = channels._channel_id(10, send=True, recv=True, force=True) + cid = _channels._channel_id(10, send=True, recv=True, force=True) self.assertEqual(repr(cid), 'ChannelID(10)') def test_equality(self): - cid1 = channels.create() - cid2 = channels._channel_id(int(cid1)) - cid3 = channels.create() + cid1 = _channels.create() + cid2 = _channels._channel_id(int(cid1)) + cid3 = _channels.create() self.assertTrue(cid1 == cid1) self.assertTrue(cid1 == cid2) @@ -341,11 +341,11 @@ def test_equality(self): self.assertTrue(cid1 != cid3) def test_shareable(self): - chan = channels.create() + chan = _channels.create() - obj = channels.create() - channels.send(chan, obj, blocking=False) - got = channels.recv(chan) + obj = _channels.create() + _channels.send(chan, obj, blocking=False) + got = _channels.recv(chan) self.assertEqual(got, obj) self.assertIs(type(got), type(obj)) @@ -356,15 +356,15 @@ def test_shareable(self): class ChannelTests(TestBase): def test_create_cid(self): - cid = channels.create() - self.assertIsInstance(cid, channels.ChannelID) + cid = _channels.create() + self.assertIsInstance(cid, _channels.ChannelID) def test_sequential_ids(self): - before = channels.list_all() - id1 = channels.create() - id2 = channels.create() - id3 = channels.create() - after = channels.list_all() + before = _channels.list_all() + id1 = _channels.create() + id2 = _channels.create() + id3 = _channels.create() + after = _channels.list_all() self.assertEqual(id2, int(id1) + 1) self.assertEqual(id3, int(id2) + 1) @@ -373,7 +373,7 @@ def test_sequential_ids(self): def test_ids_global(self): id1 = _interpreters.create() out = _run_output(id1, dedent(""" - import _xxinterpchannels as _channels + import _interpchannels as _channels cid = _channels.create() print(cid) """)) @@ -381,7 +381,7 @@ def test_ids_global(self): id2 = _interpreters.create() out = _run_output(id2, dedent(""" - import _xxinterpchannels as _channels + import _interpchannels as _channels cid = _channels.create() print(cid) """)) @@ -392,31 +392,31 @@ def test_ids_global(self): def test_channel_list_interpreters_none(self): """Test listing interpreters for a channel with no associations.""" # Test for channel with no associated _interpreters. - cid = channels.create() - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + cid = _channels.create() + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(send_interps, []) self.assertEqual(recv_interps, []) def test_channel_list_interpreters_basic(self): """Test basic listing channel _interpreters.""" interp0, *_ = _interpreters.get_main() - cid = channels.create() - channels.send(cid, "send", blocking=False) + cid = _channels.create() + _channels.send(cid, "send", blocking=False) # Test for a channel that has one end associated to an interpreter. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(send_interps, [interp0]) self.assertEqual(recv_interps, []) interp1 = _interpreters.create() _run_output(interp1, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels obj = _channels.recv({cid}) """)) # Test for channel that has both ends associated to an interpreter. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(send_interps, [interp0]) self.assertEqual(recv_interps, [interp1]) @@ -426,23 +426,23 @@ def test_channel_list_interpreters_multiple(self): interp1 = _interpreters.create() interp2 = _interpreters.create() interp3 = _interpreters.create() - cid = channels.create() + cid = _channels.create() - channels.send(cid, "send", blocking=False) + _channels.send(cid, "send", blocking=False) _run_output(interp1, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.send({cid}, "send", blocking=False) """)) _run_output(interp2, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels obj = _channels.recv({cid}) """)) _run_output(interp3, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels obj = _channels.recv({cid}) """)) - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(set(send_interps), {interp0, interp1}) self.assertEqual(set(recv_interps), {interp2, interp3}) @@ -450,22 +450,22 @@ def test_channel_list_interpreters_destroyed(self): """Test listing channel interpreters with a destroyed interpreter.""" interp0, *_ = _interpreters.get_main() interp1 = _interpreters.create() - cid = channels.create() - channels.send(cid, "send", blocking=False) + cid = _channels.create() + _channels.send(cid, "send", blocking=False) _run_output(interp1, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels obj = _channels.recv({cid}) """)) # Should be one interpreter associated with each end. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(send_interps, [interp0]) self.assertEqual(recv_interps, [interp1]) _interpreters.destroy(interp1) # Destroyed interpreter should not be listed. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(send_interps, [interp0]) self.assertEqual(recv_interps, []) @@ -476,39 +476,39 @@ def test_channel_list_interpreters_released(self): interp0, *_ = _interpreters.get_main() interp1 = _interpreters.create() interp2 = _interpreters.create() - cid = channels.create() - channels.send(cid, "data", blocking=False) + cid = _channels.create() + _channels.send(cid, "data", blocking=False) _run_output(interp1, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels obj = _channels.recv({cid}) """)) - channels.send(cid, "data", blocking=False) + _channels.send(cid, "data", blocking=False) _run_output(interp2, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels obj = _channels.recv({cid}) """)) # Check the setup. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(len(send_interps), 1) self.assertEqual(len(recv_interps), 2) # Release the main interpreter from the send end. - channels.release(cid, send=True) + _channels.release(cid, send=True) # Send end should have no associated _interpreters. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(len(send_interps), 0) self.assertEqual(len(recv_interps), 2) # Release one of the subinterpreters from the receive end. _run_output(interp2, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.release({cid}) """)) # Receive end should have the released interpreter removed. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(len(send_interps), 0) self.assertEqual(recv_interps, [interp1]) @@ -516,61 +516,61 @@ def test_channel_list_interpreters_closed(self): """Test listing channel interpreters with a closed channel.""" interp0, *_ = _interpreters.get_main() interp1 = _interpreters.create() - cid = channels.create() + cid = _channels.create() # Put something in the channel so that it's not empty. - channels.send(cid, "send", blocking=False) + _channels.send(cid, "send", blocking=False) # Check initial state. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(len(send_interps), 1) self.assertEqual(len(recv_interps), 0) # Force close the channel. - channels.close(cid, force=True) + _channels.close(cid, force=True) # Both ends should raise an error. - with self.assertRaises(channels.ChannelClosedError): - channels.list_interpreters(cid, send=True) - with self.assertRaises(channels.ChannelClosedError): - channels.list_interpreters(cid, send=False) + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=False) def test_channel_list_interpreters_closed_send_end(self): """Test listing channel interpreters with a channel's send end closed.""" interp0, *_ = _interpreters.get_main() interp1 = _interpreters.create() - cid = channels.create() + cid = _channels.create() # Put something in the channel so that it's not empty. - channels.send(cid, "send", blocking=False) + _channels.send(cid, "send", blocking=False) # Check initial state. - send_interps = channels.list_interpreters(cid, send=True) - recv_interps = channels.list_interpreters(cid, send=False) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(len(send_interps), 1) self.assertEqual(len(recv_interps), 0) # Close the send end of the channel. - channels.close(cid, send=True) + _channels.close(cid, send=True) # Send end should raise an error. - with self.assertRaises(channels.ChannelClosedError): - channels.list_interpreters(cid, send=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=True) # Receive end should not be closed (since channel is not empty). - recv_interps = channels.list_interpreters(cid, send=False) + recv_interps = _channels.list_interpreters(cid, send=False) self.assertEqual(len(recv_interps), 0) # Close the receive end of the channel from a subinterpreter. _run_output(interp1, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.close({cid}, force=True) """)) return # Both ends should raise an error. - with self.assertRaises(channels.ChannelClosedError): - channels.list_interpreters(cid, send=True) - with self.assertRaises(channels.ChannelClosedError): - channels.list_interpreters(cid, send=False) + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=False) def test_allowed_types(self): - cid = channels.create() + cid = _channels.create() objects = [ None, 'spam', @@ -579,8 +579,8 @@ def test_allowed_types(self): ] for obj in objects: with self.subTest(obj): - channels.send(cid, obj, blocking=False) - got = channels.recv(cid) + _channels.send(cid, obj, blocking=False) + got = _channels.recv(cid) self.assertEqual(got, obj) self.assertIs(type(got), type(obj)) @@ -589,16 +589,16 @@ def test_allowed_types(self): # XXX What about between interpreters? def test_run_string_arg_unresolved(self): - cid = channels.create() + cid = _channels.create() interp = _interpreters.create() _interpreters.set___main___attrs(interp, dict(cid=cid.send)) out = _run_output(interp, dedent(""" - import _xxinterpchannels as _channels + import _interpchannels as _channels print(cid.end) _channels.send(cid, b'spam', blocking=False) """)) - obj = channels.recv(cid) + obj = _channels.recv(cid) self.assertEqual(obj, b'spam') self.assertEqual(out.strip(), 'send') @@ -608,17 +608,17 @@ def test_run_string_arg_unresolved(self): # Note: this test caused crashes on some buildbots (bpo-33615). @unittest.skip('disabled until high-level channels exist') def test_run_string_arg_resolved(self): - cid = channels.create() - cid = channels._channel_id(cid, _resolve=True) + cid = _channels.create() + cid = _channels._channel_id(cid, _resolve=True) interp = _interpreters.create() out = _run_output(interp, dedent(""" - import _xxinterpchannels as _channels + import _interpchannels as _channels print(chan.id.end) _channels.send(chan.id, b'spam', blocking=False) """), dict(chan=cid.send)) - obj = channels.recv(cid) + obj = _channels.recv(cid) self.assertEqual(obj, b'spam') self.assertEqual(out.strip(), 'send') @@ -627,10 +627,10 @@ def test_run_string_arg_resolved(self): # send/recv def test_send_recv_main(self): - cid = channels.create() + cid = _channels.create() orig = b'spam' - channels.send(cid, orig, blocking=False) - obj = channels.recv(cid) + _channels.send(cid, orig, blocking=False) + obj = _channels.recv(cid) self.assertEqual(obj, orig) self.assertIsNot(obj, orig) @@ -638,7 +638,7 @@ def test_send_recv_main(self): def test_send_recv_same_interpreter(self): id1 = _interpreters.create() out = _run_output(id1, dedent(""" - import _xxinterpchannels as _channels + import _interpchannels as _channels cid = _channels.create() orig = b'spam' _channels.send(cid, orig, blocking=False) @@ -648,33 +648,33 @@ def test_send_recv_same_interpreter(self): """)) def test_send_recv_different_interpreters(self): - cid = channels.create() + cid = _channels.create() id1 = _interpreters.create() out = _run_output(id1, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.send({cid}, b'spam', blocking=False) """)) - obj = channels.recv(cid) + obj = _channels.recv(cid) self.assertEqual(obj, b'spam') def test_send_recv_different_threads(self): - cid = channels.create() + cid = _channels.create() def f(): obj = recv_wait(cid) - channels.send(cid, obj) + _channels.send(cid, obj) t = threading.Thread(target=f) t.start() - channels.send(cid, b'spam') + _channels.send(cid, b'spam') obj = recv_wait(cid) t.join() self.assertEqual(obj, b'spam') def test_send_recv_different_interpreters_and_threads(self): - cid = channels.create() + cid = _channels.create() id1 = _interpreters.create() out = None @@ -682,7 +682,7 @@ def f(): nonlocal out out = _run_output(id1, dedent(f""" import time - import _xxinterpchannels as _channels + import _interpchannels as _channels while True: try: obj = _channels.recv({cid}) @@ -695,38 +695,38 @@ def f(): t = threading.Thread(target=f) t.start() - channels.send(cid, b'spam') + _channels.send(cid, b'spam') obj = recv_wait(cid) t.join() self.assertEqual(obj, b'eggs') def test_send_not_found(self): - with self.assertRaises(channels.ChannelNotFoundError): - channels.send(10, b'spam') + with self.assertRaises(_channels.ChannelNotFoundError): + _channels.send(10, b'spam') def test_recv_not_found(self): - with self.assertRaises(channels.ChannelNotFoundError): - channels.recv(10) + with self.assertRaises(_channels.ChannelNotFoundError): + _channels.recv(10) def test_recv_empty(self): - cid = channels.create() - with self.assertRaises(channels.ChannelEmptyError): - channels.recv(cid) + cid = _channels.create() + with self.assertRaises(_channels.ChannelEmptyError): + _channels.recv(cid) def test_recv_default(self): default = object() - cid = channels.create() - obj1 = channels.recv(cid, default) - channels.send(cid, None, blocking=False) - channels.send(cid, 1, blocking=False) - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'eggs', blocking=False) - obj2 = channels.recv(cid, default) - obj3 = channels.recv(cid, default) - obj4 = channels.recv(cid) - obj5 = channels.recv(cid, default) - obj6 = channels.recv(cid, default) + cid = _channels.create() + obj1 = _channels.recv(cid, default) + _channels.send(cid, None, blocking=False) + _channels.send(cid, 1, blocking=False) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'eggs', blocking=False) + obj2 = _channels.recv(cid, default) + obj3 = _channels.recv(cid, default) + obj4 = _channels.recv(cid) + obj5 = _channels.recv(cid, default) + obj6 = _channels.recv(cid, default) self.assertIs(obj1, default) self.assertIs(obj2, None) @@ -737,32 +737,32 @@ def test_recv_default(self): def test_recv_sending_interp_destroyed(self): with self.subTest('closed'): - cid1 = channels.create() + cid1 = _channels.create() interp = _interpreters.create() _interpreters.run_string(interp, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.send({cid1}, b'spam', blocking=False) """)) _interpreters.destroy(interp) with self.assertRaisesRegex(RuntimeError, f'channel {cid1} is closed'): - channels.recv(cid1) + _channels.recv(cid1) del cid1 with self.subTest('still open'): - cid2 = channels.create() + cid2 = _channels.create() interp = _interpreters.create() _interpreters.run_string(interp, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.send({cid2}, b'spam', blocking=False) """)) - channels.send(cid2, b'eggs', blocking=False) + _channels.send(cid2, b'eggs', blocking=False) _interpreters.destroy(interp) - channels.recv(cid2) + _channels.recv(cid2) with self.assertRaisesRegex(RuntimeError, f'channel {cid2} is empty'): - channels.recv(cid2) + _channels.recv(cid2) del cid2 #------------------- @@ -770,9 +770,9 @@ def test_recv_sending_interp_destroyed(self): def test_send_buffer(self): buf = bytearray(b'spamspamspam') - cid = channels.create() - channels.send_buffer(cid, buf, blocking=False) - obj = channels.recv(cid) + cid = _channels.create() + _channels.send_buffer(cid, buf, blocking=False) + obj = _channels.recv(cid) self.assertIsNot(obj, buf) self.assertIsInstance(obj, memoryview) @@ -790,18 +790,18 @@ def build_send_waiter(self, obj, *, buffer=False): # We want a long enough sleep that send() actually has to wait. if buffer: - send = channels.send_buffer + send = _channels.send_buffer else: - send = channels.send + send = _channels.send - cid = channels.create() + cid = _channels.create() try: started = time.monotonic() send(cid, obj, blocking=False) stopped = time.monotonic() - channels.recv(cid) + _channels.recv(cid) finally: - channels.destroy(cid) + _channels.destroy(cid) delay = stopped - started # seconds delay *= 3 @@ -813,14 +813,14 @@ def test_send_blocking_waiting(self): received = None obj = b'spam' wait = self.build_send_waiter(obj) - cid = channels.create() + cid = _channels.create() def f(): nonlocal received wait() received = recv_wait(cid) t = threading.Thread(target=f) t.start() - channels.send(cid, obj, blocking=True) + _channels.send(cid, obj, blocking=True) t.join() self.assertEqual(received, obj) @@ -829,14 +829,14 @@ def test_send_buffer_blocking_waiting(self): received = None obj = bytearray(b'spam') wait = self.build_send_waiter(obj, buffer=True) - cid = channels.create() + cid = _channels.create() def f(): nonlocal received wait() received = recv_wait(cid) t = threading.Thread(target=f) t.start() - channels.send_buffer(cid, obj, blocking=True) + _channels.send_buffer(cid, obj, blocking=True) t.join() self.assertEqual(received, obj) @@ -844,13 +844,13 @@ def f(): def test_send_blocking_no_wait(self): received = None obj = b'spam' - cid = channels.create() + cid = _channels.create() def f(): nonlocal received received = recv_wait(cid) t = threading.Thread(target=f) t.start() - channels.send(cid, obj, blocking=True) + _channels.send(cid, obj, blocking=True) t.join() self.assertEqual(received, obj) @@ -858,13 +858,13 @@ def f(): def test_send_buffer_blocking_no_wait(self): received = None obj = bytearray(b'spam') - cid = channels.create() + cid = _channels.create() def f(): nonlocal received received = recv_wait(cid) t = threading.Thread(target=f) t.start() - channels.send_buffer(cid, obj, blocking=True) + _channels.send_buffer(cid, obj, blocking=True) t.join() self.assertEqual(received, obj) @@ -873,25 +873,25 @@ def test_send_timeout(self): obj = b'spam' with self.subTest('non-blocking with timeout'): - cid = channels.create() + cid = _channels.create() with self.assertRaises(ValueError): - channels.send(cid, obj, blocking=False, timeout=0.1) + _channels.send(cid, obj, blocking=False, timeout=0.1) with self.subTest('timeout hit'): - cid = channels.create() + cid = _channels.create() with self.assertRaises(TimeoutError): - channels.send(cid, obj, blocking=True, timeout=0.1) - with self.assertRaises(channels.ChannelEmptyError): - received = channels.recv(cid) + _channels.send(cid, obj, blocking=True, timeout=0.1) + with self.assertRaises(_channels.ChannelEmptyError): + received = _channels.recv(cid) print(repr(received)) with self.subTest('timeout not hit'): - cid = channels.create() + cid = _channels.create() def f(): recv_wait(cid) t = threading.Thread(target=f) t.start() - channels.send(cid, obj, blocking=True, timeout=10) + _channels.send(cid, obj, blocking=True, timeout=10) t.join() def test_send_buffer_timeout(self): @@ -910,25 +910,25 @@ def test_send_buffer_timeout(self): obj = bytearray(b'spam') with self.subTest('non-blocking with timeout'): - cid = channels.create() + cid = _channels.create() with self.assertRaises(ValueError): - channels.send_buffer(cid, obj, blocking=False, timeout=0.1) + _channels.send_buffer(cid, obj, blocking=False, timeout=0.1) with self.subTest('timeout hit'): - cid = channels.create() + cid = _channels.create() with self.assertRaises(TimeoutError): - channels.send_buffer(cid, obj, blocking=True, timeout=0.1) - with self.assertRaises(channels.ChannelEmptyError): - received = channels.recv(cid) + _channels.send_buffer(cid, obj, blocking=True, timeout=0.1) + with self.assertRaises(_channels.ChannelEmptyError): + received = _channels.recv(cid) print(repr(received)) with self.subTest('timeout not hit'): - cid = channels.create() + cid = _channels.create() def f(): recv_wait(cid) t = threading.Thread(target=f) t.start() - channels.send_buffer(cid, obj, blocking=True, timeout=10) + _channels.send_buffer(cid, obj, blocking=True, timeout=10) t.join() def test_send_closed_while_waiting(self): @@ -936,25 +936,25 @@ def test_send_closed_while_waiting(self): wait = self.build_send_waiter(obj) with self.subTest('without timeout'): - cid = channels.create() + cid = _channels.create() def f(): wait() - channels.close(cid, force=True) + _channels.close(cid, force=True) t = threading.Thread(target=f) t.start() - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, obj, blocking=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, obj, blocking=True) t.join() with self.subTest('with timeout'): - cid = channels.create() + cid = _channels.create() def f(): wait() - channels.close(cid, force=True) + _channels.close(cid, force=True) t = threading.Thread(target=f) t.start() - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, obj, blocking=True, timeout=30) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, obj, blocking=True, timeout=30) t.join() def test_send_buffer_closed_while_waiting(self): @@ -974,54 +974,54 @@ def test_send_buffer_closed_while_waiting(self): wait = self.build_send_waiter(obj, buffer=True) with self.subTest('without timeout'): - cid = channels.create() + cid = _channels.create() def f(): wait() - channels.close(cid, force=True) + _channels.close(cid, force=True) t = threading.Thread(target=f) t.start() - with self.assertRaises(channels.ChannelClosedError): - channels.send_buffer(cid, obj, blocking=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send_buffer(cid, obj, blocking=True) t.join() with self.subTest('with timeout'): - cid = channels.create() + cid = _channels.create() def f(): wait() - channels.close(cid, force=True) + _channels.close(cid, force=True) t = threading.Thread(target=f) t.start() - with self.assertRaises(channels.ChannelClosedError): - channels.send_buffer(cid, obj, blocking=True, timeout=30) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send_buffer(cid, obj, blocking=True, timeout=30) t.join() #------------------- # close def test_close_single_user(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.recv(cid) - channels.close(cid) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.recv(cid) + _channels.close(cid) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_close_multiple_users(self): - cid = channels.create() + cid = _channels.create() id1 = _interpreters.create() id2 = _interpreters.create() _interpreters.run_string(id1, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.send({cid}, b'spam', blocking=False) """)) _interpreters.run_string(id2, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.recv({cid}) """)) - channels.close(cid) + _channels.close(cid) excsnap = _interpreters.run_string(id1, dedent(f""" _channels.send({cid}, b'spam') @@ -1034,13 +1034,13 @@ def test_close_multiple_users(self): self.assertEqual(excsnap.type.__name__, 'ChannelClosedError') def test_close_multiple_times(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.recv(cid) - channels.close(cid) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.recv(cid) + _channels.close(cid) - with self.assertRaises(channels.ChannelClosedError): - channels.close(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.close(cid) def test_close_empty(self): tests = [ @@ -1051,149 +1051,149 @@ def test_close_empty(self): ] for send, recv in tests: with self.subTest((send, recv)): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.recv(cid) - channels.close(cid, send=send, recv=recv) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.recv(cid) + _channels.close(cid, send=send, recv=recv) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_close_defaults_with_unused_items(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'ham', blocking=False) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) - with self.assertRaises(channels.ChannelNotEmptyError): - channels.close(cid) - channels.recv(cid) - channels.send(cid, b'eggs', blocking=False) + with self.assertRaises(_channels.ChannelNotEmptyError): + _channels.close(cid) + _channels.recv(cid) + _channels.send(cid, b'eggs', blocking=False) def test_close_recv_with_unused_items_unforced(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'ham', blocking=False) - - with self.assertRaises(channels.ChannelNotEmptyError): - channels.close(cid, recv=True) - channels.recv(cid) - channels.send(cid, b'eggs', blocking=False) - channels.recv(cid) - channels.recv(cid) - channels.close(cid, recv=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + + with self.assertRaises(_channels.ChannelNotEmptyError): + _channels.close(cid, recv=True) + _channels.recv(cid) + _channels.send(cid, b'eggs', blocking=False) + _channels.recv(cid) + _channels.recv(cid) + _channels.close(cid, recv=True) def test_close_send_with_unused_items_unforced(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'ham', blocking=False) - channels.close(cid, send=True) - - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - channels.recv(cid) - channels.recv(cid) - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.close(cid, send=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + _channels.recv(cid) + _channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_close_both_with_unused_items_unforced(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'ham', blocking=False) - - with self.assertRaises(channels.ChannelNotEmptyError): - channels.close(cid, recv=True, send=True) - channels.recv(cid) - channels.send(cid, b'eggs', blocking=False) - channels.recv(cid) - channels.recv(cid) - channels.close(cid, recv=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + + with self.assertRaises(_channels.ChannelNotEmptyError): + _channels.close(cid, recv=True, send=True) + _channels.recv(cid) + _channels.send(cid, b'eggs', blocking=False) + _channels.recv(cid) + _channels.recv(cid) + _channels.close(cid, recv=True) def test_close_recv_with_unused_items_forced(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'ham', blocking=False) - channels.close(cid, recv=True, force=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.close(cid, recv=True, force=True) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_close_send_with_unused_items_forced(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'ham', blocking=False) - channels.close(cid, send=True, force=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.close(cid, send=True, force=True) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_close_both_with_unused_items_forced(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'ham', blocking=False) - channels.close(cid, send=True, recv=True, force=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.close(cid, send=True, recv=True, force=True) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_close_never_used(self): - cid = channels.create() - channels.close(cid) + cid = _channels.create() + _channels.close(cid) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'spam') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'spam') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_close_by_unassociated_interp(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) interp = _interpreters.create() _interpreters.run_string(interp, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.close({cid}, force=True) """)) - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) - with self.assertRaises(channels.ChannelClosedError): - channels.close(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.close(cid) def test_close_used_multiple_times_by_single_user(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'spam', blocking=False) - channels.recv(cid) - channels.close(cid, force=True) - - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) + _channels.recv(cid) + _channels.close(cid, force=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_channel_list_interpreters_invalid_channel(self): - cid = channels.create() + cid = _channels.create() # Test for invalid channel ID. - with self.assertRaises(channels.ChannelNotFoundError): - channels.list_interpreters(1000, send=True) + with self.assertRaises(_channels.ChannelNotFoundError): + _channels.list_interpreters(1000, send=True) - channels.close(cid) + _channels.close(cid) # Test for a channel that has been closed. - with self.assertRaises(channels.ChannelClosedError): - channels.list_interpreters(cid, send=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=True) def test_channel_list_interpreters_invalid_args(self): # Tests for invalid arguments passed to the API. - cid = channels.create() + cid = _channels.create() with self.assertRaises(TypeError): - channels.list_interpreters(cid) + _channels.list_interpreters(cid) class ChannelReleaseTests(TestBase): @@ -1240,26 +1240,26 @@ class ChannelReleaseTests(TestBase): """ def test_single_user(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.recv(cid) - channels.release(cid, send=True, recv=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.recv(cid) + _channels.release(cid, send=True, recv=True) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_multiple_users(self): - cid = channels.create() + cid = _channels.create() id1 = _interpreters.create() id2 = _interpreters.create() _interpreters.run_string(id1, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.send({cid}, b'spam', blocking=False) """)) out = _run_output(id2, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels obj = _channels.recv({cid}) _channels.release({cid}) print(repr(obj)) @@ -1271,94 +1271,94 @@ def test_multiple_users(self): self.assertEqual(out.strip(), "b'spam'") def test_no_kwargs(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.recv(cid) - channels.release(cid) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.recv(cid) + _channels.release(cid) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_multiple_times(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.recv(cid) - channels.release(cid, send=True, recv=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.recv(cid) + _channels.release(cid, send=True, recv=True) - with self.assertRaises(channels.ChannelClosedError): - channels.release(cid, send=True, recv=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.release(cid, send=True, recv=True) def test_with_unused_items(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'ham', blocking=False) - channels.release(cid, send=True, recv=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.release(cid, send=True, recv=True) - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_never_used(self): - cid = channels.create() - channels.release(cid) + cid = _channels.create() + _channels.release(cid) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'spam') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'spam') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_by_unassociated_interp(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) interp = _interpreters.create() _interpreters.run_string(interp, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels _channels.release({cid}) """)) - obj = channels.recv(cid) - channels.release(cid) + obj = _channels.recv(cid) + _channels.release(cid) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') self.assertEqual(obj, b'spam') def test_close_if_unassociated(self): # XXX Something's not right with this test... - cid = channels.create() + cid = _channels.create() interp = _interpreters.create() _interpreters.run_string(interp, dedent(f""" - import _xxinterpchannels as _channels + import _interpchannels as _channels obj = _channels.send({cid}, b'spam', blocking=False) _channels.release({cid}) """)) - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) def test_partially(self): # XXX Is partial close too weird/confusing? - cid = channels.create() - channels.send(cid, None, blocking=False) - channels.recv(cid) - channels.send(cid, b'spam', blocking=False) - channels.release(cid, send=True) - obj = channels.recv(cid) + cid = _channels.create() + _channels.send(cid, None, blocking=False) + _channels.recv(cid) + _channels.send(cid, b'spam', blocking=False) + _channels.release(cid, send=True) + obj = _channels.recv(cid) self.assertEqual(obj, b'spam') def test_used_multiple_times_by_single_user(self): - cid = channels.create() - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'spam', blocking=False) - channels.send(cid, b'spam', blocking=False) - channels.recv(cid) - channels.release(cid, send=True, recv=True) + cid = _channels.create() + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) + _channels.recv(cid) + _channels.release(cid, send=True, recv=True) - with self.assertRaises(channels.ChannelClosedError): - channels.send(cid, b'eggs') - with self.assertRaises(channels.ChannelClosedError): - channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) class ChannelCloseFixture(namedtuple('ChannelCloseFixture', @@ -1428,18 +1428,18 @@ def clean_up(self): def _new_channel(self, creator): if creator.name == 'main': - return channels.create() + return _channels.create() else: - ch = channels.create() + ch = _channels.create() run_interp(creator.id, f""" - import _xxsubinterpreters + import _interpreters cid = _xxsubchannels.create() # We purposefully send back an int to avoid tying the # channel to the other interpreter. _xxsubchannels.send({ch}, int(cid), blocking=False) - del _xxsubinterpreters + del _interpreters """) - self._cid = channels.recv(ch) + self._cid = _channels.recv(ch) return self._cid def _get_interpreter(self, interp): @@ -1464,13 +1464,13 @@ def _prep_interpreter(self, interp): if interp.name == 'main': return run_interp(interp.id, f""" - import _xxinterpchannels as channels - import test.test__xxinterpchannels as helpers + import _interpchannels as channels + import test.test__interpchannels as helpers ChannelState = helpers.ChannelState try: cid except NameError: - cid = channels._channel_id({self.cid}) + cid = _channels._channel_id({self.cid}) """) @@ -1657,7 +1657,7 @@ def run_action(self, fix, action, *, hideclosed=True): ) fix.record_action(action, result) else: - _cid = channels.create() + _cid = _channels.create() run_interp(interp.id, f""" result = helpers.run_action( {fix.cid}, @@ -1666,12 +1666,12 @@ def run_action(self, fix, action, *, hideclosed=True): {repr(fix.state)}, hideclosed={hideclosed}, ) - channels.send({_cid}, result.pending.to_bytes(1, 'little'), blocking=False) - channels.send({_cid}, b'X' if result.closed else b'', blocking=False) + _channels.send({_cid}, result.pending.to_bytes(1, 'little'), blocking=False) + _channels.send({_cid}, b'X' if result.closed else b'', blocking=False) """) result = ChannelState( - pending=int.from_bytes(channels.recv(_cid), 'little'), - closed=bool(channels.recv(_cid)), + pending=int.from_bytes(_channels.recv(_cid), 'little'), + closed=bool(_channels.recv(_cid)), ) fix.record_action(action, result) @@ -1694,42 +1694,42 @@ def _close(self, fix, *, force): if not fix.expect_closed_error(): self.run_action(fix, close, hideclosed=False) else: - with self.assertRaises(channels.ChannelClosedError): + with self.assertRaises(_channels.ChannelClosedError): self.run_action(fix, close, hideclosed=False) def _assert_closed_in_interp(self, fix, interp=None): if interp is None or interp.name == 'main': - with self.assertRaises(channels.ChannelClosedError): - channels.recv(fix.cid) - with self.assertRaises(channels.ChannelClosedError): - channels.send(fix.cid, b'spam') - with self.assertRaises(channels.ChannelClosedError): - channels.close(fix.cid) - with self.assertRaises(channels.ChannelClosedError): - channels.close(fix.cid, force=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(fix.cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(fix.cid, b'spam') + with self.assertRaises(_channels.ChannelClosedError): + _channels.close(fix.cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.close(fix.cid, force=True) else: run_interp(interp.id, """ with helpers.expect_channel_closed(): - channels.recv(cid) + _channels.recv(cid) """) run_interp(interp.id, """ with helpers.expect_channel_closed(): - channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) """) run_interp(interp.id, """ with helpers.expect_channel_closed(): - channels.close(cid) + _channels.close(cid) """) run_interp(interp.id, """ with helpers.expect_channel_closed(): - channels.close(cid, force=True) + _channels.close(cid, force=True) """) def _assert_closed(self, fix): self.assertTrue(fix.state.closed) for _ in range(fix.state.pending): - channels.recv(fix.cid) + _channels.recv(fix.cid) self._assert_closed_in_interp(fix) for interp in ('same', 'other'): diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__interpreters.py similarity index 98% rename from Lib/test/test__xxsubinterpreters.py rename to Lib/test/test__interpreters.py index c8c964f642f1cf..beeb280894ea99 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__interpreters.py @@ -13,9 +13,9 @@ from test.support import script_helper -_interpreters = import_helper.import_module('_xxsubinterpreters') +_interpreters = import_helper.import_module('_interpreters') _testinternalcapi = import_helper.import_module('_testinternalcapi') -from _xxsubinterpreters import InterpreterNotFoundError +from _interpreters import InterpreterNotFoundError ################################## @@ -231,7 +231,7 @@ class ModuleTests(TestBase): def test_import_in_interpreter(self): _run_output( _interpreters.create(), - 'import _xxsubinterpreters as _interpreters', + 'import _interpreters', ) @@ -273,7 +273,7 @@ def test_subinterpreter(self): main, *_ = _interpreters.get_main() interp = _interpreters.create() out = _run_output(interp, dedent(""" - import _xxsubinterpreters as _interpreters + import _interpreters cur, *_ = _interpreters.get_current() print(cur) assert isinstance(cur, int) @@ -296,7 +296,7 @@ def test_from_subinterpreter(self): [expected] = [id for id, *_ in _interpreters.list_all()] interp = _interpreters.create() out = _run_output(interp, dedent(""" - import _xxsubinterpreters as _interpreters + import _interpreters main, *_ = _interpreters.get_main() print(main) assert isinstance(main, int) @@ -323,7 +323,7 @@ def test_subinterpreter(self): def test_from_subinterpreter(self): interp = _interpreters.create() out = _run_output(interp, dedent(f""" - import _xxsubinterpreters as _interpreters + import _interpreters if _interpreters.is_running({interp}): print(True) else: @@ -385,7 +385,7 @@ def test_in_subinterpreter(self): main, = [id for id, *_ in _interpreters.list_all()] id1 = _interpreters.create() out = _run_output(id1, dedent(""" - import _xxsubinterpreters as _interpreters + import _interpreters id = _interpreters.create() print(id) assert isinstance(id, int) @@ -402,7 +402,7 @@ def test_in_threaded_subinterpreter(self): def f(): nonlocal id2 out = _run_output(id1, dedent(""" - import _xxsubinterpreters as _interpreters + import _interpreters id = _interpreters.create() print(id) """)) @@ -505,7 +505,7 @@ def test_from_current(self): main, = [id for id, *_ in _interpreters.list_all()] id = _interpreters.create() script = dedent(f""" - import _xxsubinterpreters as _interpreters + import _interpreters try: _interpreters.destroy({id}) except _interpreters.InterpreterError: @@ -521,7 +521,7 @@ def test_from_sibling(self): id1 = _interpreters.create() id2 = _interpreters.create() script = dedent(f""" - import _xxsubinterpreters as _interpreters + import _interpreters _interpreters.destroy({id2}) """) _interpreters.run_string(id1, script) @@ -862,7 +862,7 @@ def test_still_running_at_exit(self): script = dedent(""" from textwrap import dedent import threading - import _xxsubinterpreters as _interpreters + import _interpreters id = _interpreters.create() def f(): _interpreters.run_string(id, dedent(''' diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 9c24ec8fd05b12..0701eafb7c36e0 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -42,7 +42,7 @@ except ImportError: _testsinglephase = None try: - import _xxsubinterpreters as _interpreters + import _interpreters except ModuleNotFoundError: _interpreters = None diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 947a7b19056bdb..40c3023a827067 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -50,7 +50,7 @@ except ImportError: _testmultiphase = None try: - import _xxsubinterpreters as _interpreters + import _interpreters except ModuleNotFoundError: _interpreters = None try: diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index f0583c5fd0196f..668042782bdc5f 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -27,7 +27,7 @@ except ImportError: _testmultiphase = None try: - import _xxsubinterpreters as _interpreters + import _interpreters except ModuleNotFoundError: _interpreters = None diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py index 2bd8bee4063920..0039fa46496c53 100644 --- a/Lib/test/test_interpreters/test_api.py +++ b/Lib/test/test_interpreters/test_api.py @@ -9,7 +9,7 @@ from test import support from test.support import import_helper # Raise SkipTest if subinterpreters not supported. -_interpreters = import_helper.import_module('_xxsubinterpreters') +_interpreters = import_helper.import_module('_interpreters') from test.support import Py_GIL_DISABLED from test.support import interpreters from test.support.interpreters import ( @@ -385,7 +385,7 @@ def test_finished(self): def test_from_subinterpreter(self): interp = interpreters.create() out = _run_output(interp, dedent(f""" - import _xxsubinterpreters as _interpreters + import _interpreters if _interpreters.is_running({interp.id}): print(True) else: @@ -876,7 +876,7 @@ def test_created_with_capi(self): with self.assertRaisesRegex(InterpreterError, 'unrecognized'): interp.exec('raise Exception("it worked!")') - # test_xxsubinterpreters covers the remaining + # test__interpreters covers the remaining # Interpreter.exec() behavior. @@ -1290,7 +1290,7 @@ def test_get_current(self): self.assertEqual(whence, _interpreters.WHENCE_RUNTIME) script = f""" - import {_interpreters.__name__} as _interpreters + import _interpreters interpid, whence = _interpreters.get_current() print((interpid, whence)) """ @@ -1333,7 +1333,7 @@ def test_list_all(self): with self.subTest('via interp from _interpreters'): text = self.run_and_capture(interpid2, f""" - import {_interpreters.__name__} as _interpreters + import _interpreters print( _interpreters.list_all()) """) @@ -1352,7 +1352,7 @@ def test_list_all(self): (interpid5, _interpreters.WHENCE_STDLIB), ] text = self.run_temp_from_capi(f""" - import {_interpreters.__name__} as _interpreters + import _interpreters _interpreters.create() print( _interpreters.list_all()) @@ -1507,7 +1507,7 @@ def test_whence(self): with self.subTest('from C-API, running'): text = self.run_temp_from_capi(dedent(f""" - import {_interpreters.__name__} as _interpreters + import _interpreters interpid, *_ = _interpreters.get_current() print(_interpreters.whence(interpid)) """), @@ -1518,7 +1518,7 @@ def test_whence(self): with self.subTest('from legacy C-API, running'): ... text = self.run_temp_from_capi(dedent(f""" - import {_interpreters.__name__} as _interpreters + import _interpreters interpid, *_ = _interpreters.get_current() print(_interpreters.whence(interpid)) """), diff --git a/Lib/test/test_interpreters/test_channels.py b/Lib/test/test_interpreters/test_channels.py index 7e0b82884c33d3..68cc45d1a5e09f 100644 --- a/Lib/test/test_interpreters/test_channels.py +++ b/Lib/test/test_interpreters/test_channels.py @@ -7,7 +7,7 @@ from test.support import import_helper # Raise SkipTest if subinterpreters not supported. -_channels = import_helper.import_module('_xxinterpchannels') +_channels = import_helper.import_module('_interpchannels') from test.support import interpreters from test.support.interpreters import channels from .utils import _run_output, TestBase @@ -22,7 +22,7 @@ class LowLevelTests(TestBase): # encountered by the high-level module, thus they # mostly shouldn't matter as much. - # Additional tests are found in Lib/test/test__xxinterpchannels.py. + # Additional tests are found in Lib/test/test__interpchannels.py. # XXX Those should be either moved to LowLevelTests or eliminated # in favor of high-level tests in this file. diff --git a/Lib/test/test_interpreters/test_lifecycle.py b/Lib/test/test_interpreters/test_lifecycle.py index becf003e2e5f20..ac24f6568acd95 100644 --- a/Lib/test/test_interpreters/test_lifecycle.py +++ b/Lib/test/test_interpreters/test_lifecycle.py @@ -10,7 +10,7 @@ from test.support import import_helper from test.support import os_helper # Raise SkipTest if subinterpreters not supported. -import_helper.import_module('_xxsubinterpreters') +import_helper.import_module('_interpreters') from .utils import TestBase diff --git a/Lib/test/test_interpreters/test_queues.py b/Lib/test/test_interpreters/test_queues.py index 8ab9ebb354712a..a3d44c402e0ea2 100644 --- a/Lib/test/test_interpreters/test_queues.py +++ b/Lib/test/test_interpreters/test_queues.py @@ -7,7 +7,7 @@ from test.support import import_helper, Py_DEBUG # Raise SkipTest if subinterpreters not supported. -_queues = import_helper.import_module('_xxinterpqueues') +_queues = import_helper.import_module('_interpqueues') from test.support import interpreters from test.support.interpreters import queues from .utils import _run_output, TestBase as _TestBase diff --git a/Lib/test/test_interpreters/test_stress.py b/Lib/test/test_interpreters/test_stress.py index 3cc570b3bf7128..e400535b2a0e4e 100644 --- a/Lib/test/test_interpreters/test_stress.py +++ b/Lib/test/test_interpreters/test_stress.py @@ -5,7 +5,7 @@ from test.support import import_helper from test.support import threading_helper # Raise SkipTest if subinterpreters not supported. -import_helper.import_module('_xxsubinterpreters') +import_helper.import_module('_interpreters') from test.support import interpreters from .utils import TestBase diff --git a/Lib/test/test_interpreters/utils.py b/Lib/test/test_interpreters/utils.py index 8e475816f04de4..312e6fff0ceb17 100644 --- a/Lib/test/test_interpreters/utils.py +++ b/Lib/test/test_interpreters/utils.py @@ -21,7 +21,7 @@ # We would use test.support.import_helper.import_module(), # but the indirect import of test.support.os_helper causes refleaks. try: - import _xxsubinterpreters as _interpreters + import _interpreters except ImportError as exc: raise unittest.SkipTest(str(exc)) from test.support import interpreters diff --git a/Makefile.pre.in b/Makefile.pre.in index c7cf44de2dbe79..0e52e10602cf85 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1686,11 +1686,11 @@ Modules/pwdmodule.o: $(srcdir)/Modules/pwdmodule.c $(srcdir)/Modules/posixmodule Modules/signalmodule.o: $(srcdir)/Modules/signalmodule.c $(srcdir)/Modules/posixmodule.h -Modules/_xxsubinterpretersmodule.o: $(srcdir)/Modules/_xxsubinterpretersmodule.c $(srcdir)/Modules/_interpreters_common.h +Modules/_interpretersmodule.o: $(srcdir)/Modules/_interpretersmodule.c $(srcdir)/Modules/_interpreters_common.h -Modules/_xxinterpqueuesmodule.o: $(srcdir)/Modules/_xxinterpqueuesmodule.c $(srcdir)/Modules/_interpreters_common.h +Modules/_interpqueuesmodule.o: $(srcdir)/Modules/_interpqueuesmodule.c $(srcdir)/Modules/_interpreters_common.h -Modules/_xxinterpchannelsmodule.o: $(srcdir)/Modules/_xxinterpchannelsmodule.c $(srcdir)/Modules/_interpreters_common.h +Modules/_interpchannelsmodule.o: $(srcdir)/Modules/_interpchannelsmodule.c $(srcdir)/Modules/_interpreters_common.h Python/crossinterp.o: $(srcdir)/Python/crossinterp.c $(srcdir)/Python/crossinterp_data_lookup.h $(srcdir)/Python/crossinterp_exceptions.h diff --git a/Misc/NEWS.d/next/Library/2024-04-11-18-11-37.gh-issue-76785.BWNkhC.rst b/Misc/NEWS.d/next/Library/2024-04-11-18-11-37.gh-issue-76785.BWNkhC.rst new file mode 100644 index 00000000000000..f3e4c57b8f19cb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-11-18-11-37.gh-issue-76785.BWNkhC.rst @@ -0,0 +1,6 @@ +We've exposed the low-level :mod:`!_interpreters` module for the sake of the +PyPI implementation of :pep:`734`. It was sometimes available as the +:mod:`!_xxsubinterpreters` module and was formerly used only for testing. For +the most part, it should be considered an internal module, like :mod:`!_thread` +and :mod:`!_imp`. See +https://discuss.python.org/t/pep-734-multiple-interpreters-in-the-stdlib/41147/26. diff --git a/Modules/Setup b/Modules/Setup index cd1cf24c25d406..e4acf6bc7de8ea 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -137,6 +137,9 @@ PYTHONPATH=$(COREPYTHONPATH) #_datetime _datetimemodule.c #_decimal _decimal/_decimal.c #_heapq _heapqmodule.c +#_interpchannels _interpchannelsmodule.c +#_interpqueues _interpqueuesmodule.c +#_interpreters _interpretersmodule.c #_json _json.c #_lsprof _lsprof.c rotatingtree.c #_multiprocessing -I$(srcdir)/Modules/_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c @@ -271,9 +274,6 @@ PYTHONPATH=$(COREPYTHONPATH) # Testing -#_xxsubinterpreters _xxsubinterpretersmodule.c -#_xxinterpchannels _xxinterpchannelsmodule.c -#_xxinterpqueues _xxinterpqueuesmodule.c #_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c #_testbuffer _testbuffer.c #_testinternalcapi _testinternalcapi.c diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 0b0c1eef0cd872..61037f592f82f1 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -43,9 +43,10 @@ @MODULE__STRUCT_TRUE@_struct _struct.c # build supports subinterpreters -@MODULE__XXSUBINTERPRETERS_TRUE@_xxsubinterpreters _xxsubinterpretersmodule.c -@MODULE__XXINTERPCHANNELS_TRUE@_xxinterpchannels _xxinterpchannelsmodule.c -@MODULE__XXINTERPQUEUES_TRUE@_xxinterpqueues _xxinterpqueuesmodule.c +@MODULE__INTERPRETERS_TRUE@_interpreters _interpretersmodule.c +@MODULE__INTERPCHANNELS_TRUE@_interpchannels _interpchannelsmodule.c +@MODULE__INTERPQUEUES_TRUE@_interpqueues _interpqueuesmodule.c + @MODULE__ZONEINFO_TRUE@_zoneinfo _zoneinfo.c # needs libm diff --git a/Modules/_xxinterpchannelsmodule.c b/Modules/_interpchannelsmodule.c similarity index 99% rename from Modules/_xxinterpchannelsmodule.c rename to Modules/_interpchannelsmodule.c index bea0a6cf93fa02..43c96584790fa0 100644 --- a/Modules/_xxinterpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -84,7 +84,7 @@ channel's queue, which are safely managed via the _PyCrossInterpreterData_*() API.. The module does not create any objects that are shared globally. */ -#define MODULE_NAME _xxinterpchannels +#define MODULE_NAME _interpchannels #define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME) #define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME) diff --git a/Modules/_xxinterpqueuesmodule.c b/Modules/_interpqueuesmodule.c similarity index 99% rename from Modules/_xxinterpqueuesmodule.c rename to Modules/_interpqueuesmodule.c index 96f6eeeea94b5e..46801bd416495a 100644 --- a/Modules/_xxinterpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -13,7 +13,7 @@ #undef REGISTERS_HEAP_TYPES -#define MODULE_NAME _xxinterpqueues +#define MODULE_NAME _interpqueues #define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME) #define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_interpretersmodule.c similarity index 99% rename from Modules/_xxsubinterpretersmodule.c rename to Modules/_interpretersmodule.c index 8fcd4fc4154882..8fea56977ef3fe 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -23,7 +23,7 @@ #include "_interpreters_common.h" -#define MODULE_NAME _xxsubinterpreters +#define MODULE_NAME _interpreters #define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME) #define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME) diff --git a/PC/config.c b/PC/config.c index 5eff2f5b2310bb..b744f711b0d636 100644 --- a/PC/config.c +++ b/PC/config.c @@ -35,9 +35,9 @@ extern PyObject* PyInit__codecs(void); extern PyObject* PyInit__weakref(void); /* XXX: These two should really be extracted to standalone extensions. */ extern PyObject* PyInit_xxsubtype(void); -extern PyObject* PyInit__xxsubinterpreters(void); -extern PyObject* PyInit__xxinterpchannels(void); -extern PyObject* PyInit__xxinterpqueues(void); +extern PyObject* PyInit__interpreters(void); +extern PyObject* PyInit__interpchannels(void); +extern PyObject* PyInit__interpqueues(void); extern PyObject* PyInit__random(void); extern PyObject* PyInit_itertools(void); extern PyObject* PyInit__collections(void); @@ -139,9 +139,9 @@ struct _inittab _PyImport_Inittab[] = { {"_json", PyInit__json}, {"xxsubtype", PyInit_xxsubtype}, - {"_xxsubinterpreters", PyInit__xxsubinterpreters}, - {"_xxinterpchannels", PyInit__xxinterpchannels}, - {"_xxinterpqueues", PyInit__xxinterpqueues}, + {"_interpreters", PyInit__interpreters}, + {"_interpchannels", PyInit__interpchannels}, + {"_interpqueues", PyInit__interpqueues}, #ifdef _Py_HAVE_ZLIB {"zlib", PyInit_zlib}, #endif diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 3a019a5fe550db..25d52945c1c330 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -465,9 +465,9 @@ - - - + + + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index e43970410bd378..4b1f9aa6538562 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -1547,13 +1547,13 @@ Parser - + Modules - + Modules - + Modules diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index ac9d91b5e12885..08a66f447e2258 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -37,6 +37,9 @@ static const char* _Py_stdlib_module_names[] = { "_hashlib", "_heapq", "_imp", +"_interpchannels", +"_interpqueues", +"_interpreters", "_io", "_ios_support", "_json", diff --git a/Tools/build/generate_stdlib_module_names.py b/Tools/build/generate_stdlib_module_names.py index 69dc74e7f25ec9..f9fd29509f3225 100644 --- a/Tools/build/generate_stdlib_module_names.py +++ b/Tools/build/generate_stdlib_module_names.py @@ -36,9 +36,6 @@ '_testmultiphase', '_testsinglephase', '_testexternalinspection', - '_xxsubinterpreters', - '_xxinterpchannels', - '_xxinterpqueues', '_xxtestfuzz', 'idlelib.idle_test', 'test', diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index e0ae39036c128d..87b695de23e25e 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -164,8 +164,8 @@ Python/pylifecycle.c _Py_FatalErrorFormat reentrant - Python/pylifecycle.c fatal_error reentrant - # explicitly protected, internal-only -Modules/_xxinterpchannelsmodule.c - _globals - -Modules/_xxinterpqueuesmodule.c - _globals - +Modules/_interpchannelsmodule.c - _globals - +Modules/_interpqueuesmodule.c - _globals - # set once during module init Modules/_decimal/_decimal.c - minalloc_is_set - @@ -246,11 +246,11 @@ Modules/_struct.c - bigendian_table - Modules/_struct.c - lilendian_table - Modules/_struct.c - native_table - Modules/_tkinter.c - state_key - -Modules/_xxinterpchannelsmodule.c - _channelid_end_recv - -Modules/_xxinterpchannelsmodule.c - _channelid_end_send - +Modules/_interpchannelsmodule.c - _channelid_end_recv - +Modules/_interpchannelsmodule.c - _channelid_end_send - Modules/_zoneinfo.c - DAYS_BEFORE_MONTH - Modules/_zoneinfo.c - DAYS_IN_MONTH - -Modules/_xxsubinterpretersmodule.c - no_exception - +Modules/_interpretersmodule.c - no_exception - Modules/arraymodule.c - descriptors - Modules/arraymodule.c - emptybuf - Modules/cjkcodecs/_codecs_cn.c - _mapping_list - diff --git a/configure b/configure index 94ee1ca9cd0b8d..78f86d83077eaa 100755 --- a/configure +++ b/configure @@ -775,12 +775,12 @@ MODULE__MULTIPROCESSING_FALSE MODULE__MULTIPROCESSING_TRUE MODULE__ZONEINFO_FALSE MODULE__ZONEINFO_TRUE -MODULE__XXINTERPQUEUES_FALSE -MODULE__XXINTERPQUEUES_TRUE -MODULE__XXINTERPCHANNELS_FALSE -MODULE__XXINTERPCHANNELS_TRUE -MODULE__XXSUBINTERPRETERS_FALSE -MODULE__XXSUBINTERPRETERS_TRUE +MODULE__INTERPQUEUES_FALSE +MODULE__INTERPQUEUES_TRUE +MODULE__INTERPCHANNELS_FALSE +MODULE__INTERPCHANNELS_TRUE +MODULE__INTERPRETERS_FALSE +MODULE__INTERPRETERS_TRUE MODULE__TYPING_FALSE MODULE__TYPING_TRUE MODULE__STRUCT_FALSE @@ -28659,9 +28659,9 @@ case $ac_sys_system in #( py_cv_module__posixsubprocess=n/a py_cv_module__scproxy=n/a py_cv_module__tkinter=n/a - py_cv_module__xxsubinterpreters=n/a - py_cv_module__xxinterpchannels=n/a - py_cv_module__xxinterpqueues=n/a + py_cv_module__interpreters=n/a + py_cv_module__interpchannels=n/a + py_cv_module__interpqueues=n/a py_cv_module_grp=n/a py_cv_module_pwd=n/a py_cv_module_resource=n/a @@ -29126,20 +29126,20 @@ then : fi - if test "$py_cv_module__xxsubinterpreters" != "n/a" + if test "$py_cv_module__interpreters" != "n/a" then : - py_cv_module__xxsubinterpreters=yes + py_cv_module__interpreters=yes fi - if test "$py_cv_module__xxsubinterpreters" = yes; then - MODULE__XXSUBINTERPRETERS_TRUE= - MODULE__XXSUBINTERPRETERS_FALSE='#' + if test "$py_cv_module__interpreters" = yes; then + MODULE__INTERPRETERS_TRUE= + MODULE__INTERPRETERS_FALSE='#' else - MODULE__XXSUBINTERPRETERS_TRUE='#' - MODULE__XXSUBINTERPRETERS_FALSE= + MODULE__INTERPRETERS_TRUE='#' + MODULE__INTERPRETERS_FALSE= fi - as_fn_append MODULE_BLOCK "MODULE__XXSUBINTERPRETERS_STATE=$py_cv_module__xxsubinterpreters$as_nl" - if test "x$py_cv_module__xxsubinterpreters" = xyes + as_fn_append MODULE_BLOCK "MODULE__INTERPRETERS_STATE=$py_cv_module__interpreters$as_nl" + if test "x$py_cv_module__interpreters" = xyes then : @@ -29148,20 +29148,20 @@ then : fi - if test "$py_cv_module__xxinterpchannels" != "n/a" + if test "$py_cv_module__interpchannels" != "n/a" then : - py_cv_module__xxinterpchannels=yes + py_cv_module__interpchannels=yes fi - if test "$py_cv_module__xxinterpchannels" = yes; then - MODULE__XXINTERPCHANNELS_TRUE= - MODULE__XXINTERPCHANNELS_FALSE='#' + if test "$py_cv_module__interpchannels" = yes; then + MODULE__INTERPCHANNELS_TRUE= + MODULE__INTERPCHANNELS_FALSE='#' else - MODULE__XXINTERPCHANNELS_TRUE='#' - MODULE__XXINTERPCHANNELS_FALSE= + MODULE__INTERPCHANNELS_TRUE='#' + MODULE__INTERPCHANNELS_FALSE= fi - as_fn_append MODULE_BLOCK "MODULE__XXINTERPCHANNELS_STATE=$py_cv_module__xxinterpchannels$as_nl" - if test "x$py_cv_module__xxinterpchannels" = xyes + as_fn_append MODULE_BLOCK "MODULE__INTERPCHANNELS_STATE=$py_cv_module__interpchannels$as_nl" + if test "x$py_cv_module__interpchannels" = xyes then : @@ -29170,20 +29170,20 @@ then : fi - if test "$py_cv_module__xxinterpqueues" != "n/a" + if test "$py_cv_module__interpqueues" != "n/a" then : - py_cv_module__xxinterpqueues=yes + py_cv_module__interpqueues=yes fi - if test "$py_cv_module__xxinterpqueues" = yes; then - MODULE__XXINTERPQUEUES_TRUE= - MODULE__XXINTERPQUEUES_FALSE='#' + if test "$py_cv_module__interpqueues" = yes; then + MODULE__INTERPQUEUES_TRUE= + MODULE__INTERPQUEUES_FALSE='#' else - MODULE__XXINTERPQUEUES_TRUE='#' - MODULE__XXINTERPQUEUES_FALSE= + MODULE__INTERPQUEUES_TRUE='#' + MODULE__INTERPQUEUES_FALSE= fi - as_fn_append MODULE_BLOCK "MODULE__XXINTERPQUEUES_STATE=$py_cv_module__xxinterpqueues$as_nl" - if test "x$py_cv_module__xxinterpqueues" = xyes + as_fn_append MODULE_BLOCK "MODULE__INTERPQUEUES_STATE=$py_cv_module__interpqueues$as_nl" + if test "x$py_cv_module__interpqueues" = xyes then : @@ -31532,16 +31532,16 @@ if test -z "${MODULE__TYPING_TRUE}" && test -z "${MODULE__TYPING_FALSE}"; then as_fn_error $? "conditional \"MODULE__TYPING\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${MODULE__XXSUBINTERPRETERS_TRUE}" && test -z "${MODULE__XXSUBINTERPRETERS_FALSE}"; then - as_fn_error $? "conditional \"MODULE__XXSUBINTERPRETERS\" was never defined. +if test -z "${MODULE__INTERPRETERS_TRUE}" && test -z "${MODULE__INTERPRETERS_FALSE}"; then + as_fn_error $? "conditional \"MODULE__INTERPRETERS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${MODULE__XXINTERPCHANNELS_TRUE}" && test -z "${MODULE__XXINTERPCHANNELS_FALSE}"; then - as_fn_error $? "conditional \"MODULE__XXINTERPCHANNELS\" was never defined. +if test -z "${MODULE__INTERPCHANNELS_TRUE}" && test -z "${MODULE__INTERPCHANNELS_FALSE}"; then + as_fn_error $? "conditional \"MODULE__INTERPCHANNELS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${MODULE__XXINTERPQUEUES_TRUE}" && test -z "${MODULE__XXINTERPQUEUES_FALSE}"; then - as_fn_error $? "conditional \"MODULE__XXINTERPQUEUES\" was never defined. +if test -z "${MODULE__INTERPQUEUES_TRUE}" && test -z "${MODULE__INTERPQUEUES_FALSE}"; then + as_fn_error $? "conditional \"MODULE__INTERPQUEUES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MODULE__ZONEINFO_TRUE}" && test -z "${MODULE__ZONEINFO_FALSE}"; then diff --git a/configure.ac b/configure.ac index 7877ef45c2e500..719b8d3a9573b9 100644 --- a/configure.ac +++ b/configure.ac @@ -7433,9 +7433,9 @@ AS_CASE([$ac_sys_system], [_posixsubprocess], [_scproxy], [_tkinter], - [_xxsubinterpreters], - [_xxinterpchannels], - [_xxinterpqueues], + [_interpreters], + [_interpchannels], + [_interpqueues], [grp], [pwd], [resource], @@ -7558,9 +7558,9 @@ PY_STDLIB_MOD_SIMPLE([_random]) PY_STDLIB_MOD_SIMPLE([select]) PY_STDLIB_MOD_SIMPLE([_struct]) PY_STDLIB_MOD_SIMPLE([_typing]) -PY_STDLIB_MOD_SIMPLE([_xxsubinterpreters]) -PY_STDLIB_MOD_SIMPLE([_xxinterpchannels]) -PY_STDLIB_MOD_SIMPLE([_xxinterpqueues]) +PY_STDLIB_MOD_SIMPLE([_interpreters]) +PY_STDLIB_MOD_SIMPLE([_interpchannels]) +PY_STDLIB_MOD_SIMPLE([_interpqueues]) PY_STDLIB_MOD_SIMPLE([_zoneinfo]) dnl multiprocessing modules From 1acd2497983f1a78dffd6e5b3e0f5dd0920a550f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 24 Apr 2024 10:28:35 -0600 Subject: [PATCH 037/217] gh-117953: Let update_global_state_for_extension() Caller Decide If Singlephase or Not (gh-118193) This change makes other upcoming changes simpler. --- Python/import.c | 113 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/Python/import.c b/Python/import.c index 30d8082607ab37..739f506fe03100 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1185,19 +1185,51 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) return 0; } +#ifndef NDEBUG +static bool +is_singlephase(PyModuleDef *def) +{ + if (def == NULL) { + /* It must be a module created by reload_singlephase_extension() + * from m_copy. Ideally we'd do away with this case. */ + return true; + } + else if (def->m_slots == NULL) { + return true; + } + else { + return false; + } +} +#endif + + +struct singlephase_global_update { + PyObject *m_dict; +}; static int update_global_state_for_extension(PyThreadState *tstate, - PyObject *mod, PyModuleDef *def, - PyObject *name, PyObject *path) + PyObject *path, PyObject *name, + PyModuleDef *def, + struct singlephase_global_update *singlephase) { - assert(mod != NULL && PyModule_Check(mod)); - assert(def == _PyModule_GetDef(mod)); - - // bpo-44050: Extensions and def->m_base.m_copy can be updated - // when the extension module doesn't support sub-interpreters. - if (def->m_size == -1) { - if (!is_core_module(tstate->interp, name, path)) { + /* Copy the module's __dict__, if applicable. */ + if (singlephase == NULL) { + assert(def->m_base.m_copy == NULL); + } + else { + assert(def->m_base.m_init != NULL + || is_core_module(tstate->interp, name, path)); + if (singlephase->m_dict == NULL) { + assert(def->m_base.m_copy == NULL); + } + else { + assert(PyDict_Check(singlephase->m_dict)); + // gh-88216: Extensions and def->m_base.m_copy can be updated + // when the extension module doesn't support sub-interpreters. + assert(def->m_size == -1); + assert(!is_core_module(tstate->interp, name, path)); assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0); assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0); if (def->m_base.m_copy) { @@ -1206,17 +1238,16 @@ update_global_state_for_extension(PyThreadState *tstate, XXX this should really not happen. */ Py_CLEAR(def->m_base.m_copy); } - PyObject *dict = PyModule_GetDict(mod); - if (dict == NULL) { - return -1; - } - def->m_base.m_copy = PyDict_Copy(dict); + def->m_base.m_copy = PyDict_Copy(singlephase->m_dict); if (def->m_base.m_copy == NULL) { + // XXX Ignore this error? Doing so would effectively + // mark the module as not loadable. */ return -1; } } } + /* Add the module's def to the global cache. */ // XXX Why special-case the main interpreter? if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) { #ifndef NDEBUG @@ -1258,6 +1289,8 @@ int _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, PyObject *filename, PyObject *modules) { + PyThreadState *tstate = _PyThreadState_GET(); + if (mod == NULL || !PyModule_Check(mod)) { PyErr_BadInternalCall(); return -1; @@ -1268,15 +1301,28 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, return -1; } - PyThreadState *tstate = _PyThreadState_GET(); + /* Only single-phase init extension modules can reach here. */ + assert(is_singlephase(def)); + assert(!is_core_module(tstate->interp, name, filename)); + assert(!is_core_module(tstate->interp, name, name)); + + struct singlephase_global_update singlephase = {0}; + // gh-88216: Extensions and def->m_base.m_copy can be updated + // when the extension module doesn't support sub-interpreters. + if (def->m_size == -1) { + singlephase.m_dict = PyModule_GetDict(mod); + assert(singlephase.m_dict != NULL); + } if (update_global_state_for_extension( - tstate, mod, def, name, filename) < 0) + tstate, filename, name, def, &singlephase) < 0) { return -1; } + if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) { return -1; } + return 0; } @@ -1407,11 +1453,25 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, goto finally; } + /* We only use _PyImport_FixupBuiltin() for the core builtin modules + * (sys and builtins). These modules are single-phase init with no + * module state, but we also don't populate def->m_base.m_copy + * for them. */ + assert(is_core_module(tstate->interp, nameobj, nameobj)); + assert(is_singlephase(def)); + assert(def->m_size == -1); + assert(def->m_base.m_copy == NULL); + + struct singlephase_global_update singlephase = { + /* We don't want def->m_base.m_copy populated. */ + .m_dict=NULL, + }; if (update_global_state_for_extension( - tstate, mod, def, nameobj, nameobj) < 0) + tstate, nameobj, nameobj, def, &singlephase) < 0) { goto finally; } + if (finish_singlephase_extension(tstate, mod, def, nameobj, modules) < 0) { goto finally; } @@ -1444,6 +1504,7 @@ is_builtin(PyObject *name) static PyObject* create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) { + PyModuleDef *def = NULL; PyObject *mod = import_find_extension(tstate, name, name); if (mod || _PyErr_Occurred(tstate)) { return mod; @@ -1473,20 +1534,32 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) } if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { - return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); + def = (PyModuleDef*)mod; + assert(!is_singlephase(def)); + return PyModule_FromDefAndSpec(def, spec); } else { assert(PyModule_Check(mod)); - PyModuleDef *def = PyModule_GetDef(mod); + def = PyModule_GetDef(mod); if (def == NULL) { return NULL; } + assert(is_singlephase(def)); /* Remember pointer to module init function. */ def->m_base.m_init = p0; + struct singlephase_global_update singlephase = {0}; + // gh-88216: Extensions and def->m_base.m_copy can be updated + // when the extension module doesn't support sub-interpreters. + if (def->m_size == -1 + && !is_core_module(tstate->interp, name, name)) + { + singlephase.m_dict = PyModule_GetDict(mod); + assert(singlephase.m_dict != NULL); + } if (update_global_state_for_extension( - tstate, mod, def, name, name) < 0) + tstate, name, name, def, &singlephase) < 0) { return NULL; } From 9b280ab0ab97902d55ea3bde66b2e23f8b23959f Mon Sep 17 00:00:00 2001 From: David Rubin <87927264+Rexicon226@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:16:06 -0700 Subject: [PATCH 038/217] gh-116988: Remove duplicates of `annotated_rhs` in the Grammar (#117004) --- Grammar/python.gram | 18 +- Parser/parser.c | 3763 ++++++++++------------ Tools/peg_generator/pegen/c_generator.py | 2 +- 3 files changed, 1626 insertions(+), 2157 deletions(-) diff --git a/Grammar/python.gram b/Grammar/python.gram index 11438e57da527b..3943d7fec5db03 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -915,7 +915,7 @@ fstring_middle[expr_ty]: | fstring_replacement_field | t=FSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) } fstring_replacement_field[expr_ty]: - | '{' a=(yield_expr | star_expressions) debug_expr='='? conversion=[fstring_conversion] format=[fstring_full_format_spec] rbrace='}' { + | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[fstring_full_format_spec] rbrace='}' { _PyPegen_formatted_value(p, a, debug_expr, conversion, format, rbrace, EXTRA) } | invalid_replacement_field fstring_conversion[ResultTokenWithMetadata*]: @@ -1201,7 +1201,7 @@ invalid_assignment: | (star_targets '=')* a=star_expressions '=' { RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) } | (star_targets '=')* a=yield_expr '=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "assignment to yield expression not possible") } - | a=star_expressions augassign (yield_expr | star_expressions) { + | a=star_expressions augassign annotated_rhs { RAISE_SYNTAX_ERROR_KNOWN_LOCATION( a, "'%s' is an illegal expression for augmented assignment", @@ -1407,17 +1407,17 @@ invalid_replacement_field: | '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '!'") } | '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before ':'") } | '{' a='}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '}'") } - | '{' !(yield_expr | star_expressions) { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'")} - | '{' (yield_expr | star_expressions) !('=' | '!' | ':' | '}') { + | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'")} + | '{' annotated_rhs !('=' | '!' | ':' | '}') { PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '=', or '!', or ':', or '}'") } - | '{' (yield_expr | star_expressions) '=' !('!' | ':' | '}') { + | '{' annotated_rhs '=' !('!' | ':' | '}') { PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '!', or ':', or '}'") } - | '{' (yield_expr | star_expressions) '='? invalid_conversion_character - | '{' (yield_expr | star_expressions) '='? ['!' NAME] !(':' | '}') { + | '{' annotated_rhs '='? invalid_conversion_character + | '{' annotated_rhs '='? ['!' NAME] !(':' | '}') { PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting ':' or '}'") } - | '{' (yield_expr | star_expressions) '='? ['!' NAME] ':' fstring_format_spec* !'}' { + | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' { PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}', or format specs") } - | '{' (yield_expr | star_expressions) '='? ['!' NAME] !'}' { + | '{' annotated_rhs '='? ['!' NAME] !'}' { PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}'") } invalid_conversion_character: diff --git a/Parser/parser.c b/Parser/parser.c index b6683bfd1f1bc0..0715e9775a8f06 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -442,48 +442,48 @@ static char *soft_keywords[] = { #define _loop1_109_type 1355 #define _loop0_110_type 1356 #define _loop1_111_type 1357 -#define _tmp_112_type 1358 +#define _loop0_112_type 1358 #define _loop0_113_type 1359 -#define _loop0_114_type 1360 -#define _loop1_115_type 1361 -#define _tmp_116_type 1362 -#define _loop0_118_type 1363 -#define _gather_117_type 1364 -#define _loop1_119_type 1365 +#define _loop1_114_type 1360 +#define _tmp_115_type 1361 +#define _loop0_117_type 1362 +#define _gather_116_type 1363 +#define _loop1_118_type 1364 +#define _loop0_119_type 1365 #define _loop0_120_type 1366 -#define _loop0_121_type 1367 +#define _tmp_121_type 1367 #define _tmp_122_type 1368 -#define _tmp_123_type 1369 -#define _loop0_125_type 1370 -#define _gather_124_type 1371 -#define _tmp_126_type 1372 -#define _loop0_128_type 1373 -#define _gather_127_type 1374 -#define _loop0_130_type 1375 -#define _gather_129_type 1376 -#define _loop0_132_type 1377 -#define _gather_131_type 1378 -#define _loop0_134_type 1379 -#define _gather_133_type 1380 -#define _loop0_135_type 1381 -#define _loop0_137_type 1382 -#define _gather_136_type 1383 -#define _loop1_138_type 1384 -#define _tmp_139_type 1385 -#define _loop0_141_type 1386 -#define _gather_140_type 1387 -#define _loop0_143_type 1388 -#define _gather_142_type 1389 -#define _loop0_145_type 1390 -#define _gather_144_type 1391 -#define _loop0_147_type 1392 -#define _gather_146_type 1393 -#define _loop0_149_type 1394 -#define _gather_148_type 1395 +#define _loop0_124_type 1369 +#define _gather_123_type 1370 +#define _tmp_125_type 1371 +#define _loop0_127_type 1372 +#define _gather_126_type 1373 +#define _loop0_129_type 1374 +#define _gather_128_type 1375 +#define _loop0_131_type 1376 +#define _gather_130_type 1377 +#define _loop0_133_type 1378 +#define _gather_132_type 1379 +#define _loop0_134_type 1380 +#define _loop0_136_type 1381 +#define _gather_135_type 1382 +#define _loop1_137_type 1383 +#define _tmp_138_type 1384 +#define _loop0_140_type 1385 +#define _gather_139_type 1386 +#define _loop0_142_type 1387 +#define _gather_141_type 1388 +#define _loop0_144_type 1389 +#define _gather_143_type 1390 +#define _loop0_146_type 1391 +#define _gather_145_type 1392 +#define _loop0_148_type 1393 +#define _gather_147_type 1394 +#define _tmp_149_type 1395 #define _tmp_150_type 1396 -#define _tmp_151_type 1397 -#define _loop0_153_type 1398 -#define _gather_152_type 1399 +#define _loop0_152_type 1397 +#define _gather_151_type 1398 +#define _tmp_153_type 1399 #define _tmp_154_type 1400 #define _tmp_155_type 1401 #define _tmp_156_type 1402 @@ -493,49 +493,49 @@ static char *soft_keywords[] = { #define _tmp_160_type 1406 #define _tmp_161_type 1407 #define _tmp_162_type 1408 -#define _tmp_163_type 1409 +#define _loop0_163_type 1409 #define _loop0_164_type 1410 #define _loop0_165_type 1411 -#define _loop0_166_type 1412 +#define _tmp_166_type 1412 #define _tmp_167_type 1413 #define _tmp_168_type 1414 #define _tmp_169_type 1415 -#define _tmp_170_type 1416 -#define _tmp_171_type 1417 +#define _loop0_170_type 1416 +#define _loop0_171_type 1417 #define _loop0_172_type 1418 -#define _loop0_173_type 1419 -#define _loop0_174_type 1420 -#define _loop1_175_type 1421 +#define _loop1_173_type 1419 +#define _tmp_174_type 1420 +#define _loop0_175_type 1421 #define _tmp_176_type 1422 #define _loop0_177_type 1423 -#define _tmp_178_type 1424 -#define _loop0_179_type 1425 -#define _loop1_180_type 1426 +#define _loop1_178_type 1424 +#define _tmp_179_type 1425 +#define _tmp_180_type 1426 #define _tmp_181_type 1427 -#define _tmp_182_type 1428 +#define _loop0_182_type 1428 #define _tmp_183_type 1429 -#define _loop0_184_type 1430 -#define _tmp_185_type 1431 +#define _tmp_184_type 1430 +#define _loop1_185_type 1431 #define _tmp_186_type 1432 -#define _loop1_187_type 1433 -#define _tmp_188_type 1434 +#define _loop0_187_type 1433 +#define _loop0_188_type 1434 #define _loop0_189_type 1435 -#define _loop0_190_type 1436 -#define _loop0_191_type 1437 -#define _loop0_193_type 1438 -#define _gather_192_type 1439 +#define _loop0_191_type 1436 +#define _gather_190_type 1437 +#define _tmp_192_type 1438 +#define _loop0_193_type 1439 #define _tmp_194_type 1440 #define _loop0_195_type 1441 -#define _tmp_196_type 1442 -#define _loop0_197_type 1443 -#define _loop1_198_type 1444 -#define _loop1_199_type 1445 -#define _tmp_200_type 1446 +#define _loop1_196_type 1442 +#define _loop1_197_type 1443 +#define _tmp_198_type 1444 +#define _tmp_199_type 1445 +#define _loop0_200_type 1446 #define _tmp_201_type 1447 -#define _loop0_202_type 1448 +#define _tmp_202_type 1448 #define _tmp_203_type 1449 -#define _tmp_204_type 1450 -#define _tmp_205_type 1451 +#define _loop0_205_type 1450 +#define _gather_204_type 1451 #define _loop0_207_type 1452 #define _gather_206_type 1453 #define _loop0_209_type 1454 @@ -544,14 +544,14 @@ static char *soft_keywords[] = { #define _gather_210_type 1457 #define _loop0_213_type 1458 #define _gather_212_type 1459 -#define _loop0_215_type 1460 -#define _gather_214_type 1461 -#define _tmp_216_type 1462 -#define _loop0_217_type 1463 -#define _loop1_218_type 1464 -#define _tmp_219_type 1465 -#define _loop0_220_type 1466 -#define _loop1_221_type 1467 +#define _tmp_214_type 1460 +#define _loop0_215_type 1461 +#define _loop1_216_type 1462 +#define _tmp_217_type 1463 +#define _loop0_218_type 1464 +#define _loop1_219_type 1465 +#define _tmp_220_type 1466 +#define _tmp_221_type 1467 #define _tmp_222_type 1468 #define _tmp_223_type 1469 #define _tmp_224_type 1470 @@ -560,16 +560,16 @@ static char *soft_keywords[] = { #define _tmp_227_type 1473 #define _tmp_228_type 1474 #define _tmp_229_type 1475 -#define _tmp_230_type 1476 -#define _tmp_231_type 1477 -#define _loop0_233_type 1478 -#define _gather_232_type 1479 +#define _loop0_231_type 1476 +#define _gather_230_type 1477 +#define _tmp_232_type 1478 +#define _tmp_233_type 1479 #define _tmp_234_type 1480 #define _tmp_235_type 1481 #define _tmp_236_type 1482 #define _tmp_237_type 1483 #define _tmp_238_type 1484 -#define _tmp_239_type 1485 +#define _loop0_239_type 1485 #define _tmp_240_type 1486 #define _tmp_241_type 1487 #define _tmp_242_type 1488 @@ -577,7 +577,7 @@ static char *soft_keywords[] = { #define _tmp_244_type 1490 #define _tmp_245_type 1491 #define _tmp_246_type 1492 -#define _loop0_247_type 1493 +#define _tmp_247_type 1493 #define _tmp_248_type 1494 #define _tmp_249_type 1495 #define _tmp_250_type 1496 @@ -586,7 +586,7 @@ static char *soft_keywords[] = { #define _tmp_253_type 1499 #define _tmp_254_type 1500 #define _tmp_255_type 1501 -#define _tmp_256_type 1502 +#define _loop0_256_type 1502 #define _tmp_257_type 1503 #define _tmp_258_type 1504 #define _tmp_259_type 1505 @@ -595,7 +595,7 @@ static char *soft_keywords[] = { #define _tmp_262_type 1508 #define _tmp_263_type 1509 #define _tmp_264_type 1510 -#define _loop0_265_type 1511 +#define _tmp_265_type 1511 #define _tmp_266_type 1512 #define _tmp_267_type 1513 #define _tmp_268_type 1514 @@ -603,23 +603,14 @@ static char *soft_keywords[] = { #define _tmp_270_type 1516 #define _tmp_271_type 1517 #define _tmp_272_type 1518 -#define _tmp_273_type 1519 -#define _tmp_274_type 1520 +#define _loop0_274_type 1519 +#define _gather_273_type 1520 #define _tmp_275_type 1521 #define _tmp_276_type 1522 #define _tmp_277_type 1523 #define _tmp_278_type 1524 #define _tmp_279_type 1525 #define _tmp_280_type 1526 -#define _tmp_281_type 1527 -#define _loop0_283_type 1528 -#define _gather_282_type 1529 -#define _tmp_284_type 1530 -#define _tmp_285_type 1531 -#define _tmp_286_type 1532 -#define _tmp_287_type 1533 -#define _tmp_288_type 1534 -#define _tmp_289_type 1535 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -979,48 +970,48 @@ static asdl_seq *_loop0_108_rule(Parser *p); static asdl_seq *_loop1_109_rule(Parser *p); static asdl_seq *_loop0_110_rule(Parser *p); static asdl_seq *_loop1_111_rule(Parser *p); -static void *_tmp_112_rule(Parser *p); +static asdl_seq *_loop0_112_rule(Parser *p); static asdl_seq *_loop0_113_rule(Parser *p); -static asdl_seq *_loop0_114_rule(Parser *p); -static asdl_seq *_loop1_115_rule(Parser *p); -static void *_tmp_116_rule(Parser *p); -static asdl_seq *_loop0_118_rule(Parser *p); -static asdl_seq *_gather_117_rule(Parser *p); -static asdl_seq *_loop1_119_rule(Parser *p); +static asdl_seq *_loop1_114_rule(Parser *p); +static void *_tmp_115_rule(Parser *p); +static asdl_seq *_loop0_117_rule(Parser *p); +static asdl_seq *_gather_116_rule(Parser *p); +static asdl_seq *_loop1_118_rule(Parser *p); +static asdl_seq *_loop0_119_rule(Parser *p); static asdl_seq *_loop0_120_rule(Parser *p); -static asdl_seq *_loop0_121_rule(Parser *p); +static void *_tmp_121_rule(Parser *p); static void *_tmp_122_rule(Parser *p); -static void *_tmp_123_rule(Parser *p); -static asdl_seq *_loop0_125_rule(Parser *p); -static asdl_seq *_gather_124_rule(Parser *p); -static void *_tmp_126_rule(Parser *p); -static asdl_seq *_loop0_128_rule(Parser *p); -static asdl_seq *_gather_127_rule(Parser *p); -static asdl_seq *_loop0_130_rule(Parser *p); -static asdl_seq *_gather_129_rule(Parser *p); -static asdl_seq *_loop0_132_rule(Parser *p); -static asdl_seq *_gather_131_rule(Parser *p); +static asdl_seq *_loop0_124_rule(Parser *p); +static asdl_seq *_gather_123_rule(Parser *p); +static void *_tmp_125_rule(Parser *p); +static asdl_seq *_loop0_127_rule(Parser *p); +static asdl_seq *_gather_126_rule(Parser *p); +static asdl_seq *_loop0_129_rule(Parser *p); +static asdl_seq *_gather_128_rule(Parser *p); +static asdl_seq *_loop0_131_rule(Parser *p); +static asdl_seq *_gather_130_rule(Parser *p); +static asdl_seq *_loop0_133_rule(Parser *p); +static asdl_seq *_gather_132_rule(Parser *p); static asdl_seq *_loop0_134_rule(Parser *p); -static asdl_seq *_gather_133_rule(Parser *p); -static asdl_seq *_loop0_135_rule(Parser *p); -static asdl_seq *_loop0_137_rule(Parser *p); -static asdl_seq *_gather_136_rule(Parser *p); -static asdl_seq *_loop1_138_rule(Parser *p); -static void *_tmp_139_rule(Parser *p); -static asdl_seq *_loop0_141_rule(Parser *p); -static asdl_seq *_gather_140_rule(Parser *p); -static asdl_seq *_loop0_143_rule(Parser *p); -static asdl_seq *_gather_142_rule(Parser *p); -static asdl_seq *_loop0_145_rule(Parser *p); -static asdl_seq *_gather_144_rule(Parser *p); -static asdl_seq *_loop0_147_rule(Parser *p); -static asdl_seq *_gather_146_rule(Parser *p); -static asdl_seq *_loop0_149_rule(Parser *p); -static asdl_seq *_gather_148_rule(Parser *p); +static asdl_seq *_loop0_136_rule(Parser *p); +static asdl_seq *_gather_135_rule(Parser *p); +static asdl_seq *_loop1_137_rule(Parser *p); +static void *_tmp_138_rule(Parser *p); +static asdl_seq *_loop0_140_rule(Parser *p); +static asdl_seq *_gather_139_rule(Parser *p); +static asdl_seq *_loop0_142_rule(Parser *p); +static asdl_seq *_gather_141_rule(Parser *p); +static asdl_seq *_loop0_144_rule(Parser *p); +static asdl_seq *_gather_143_rule(Parser *p); +static asdl_seq *_loop0_146_rule(Parser *p); +static asdl_seq *_gather_145_rule(Parser *p); +static asdl_seq *_loop0_148_rule(Parser *p); +static asdl_seq *_gather_147_rule(Parser *p); +static void *_tmp_149_rule(Parser *p); static void *_tmp_150_rule(Parser *p); -static void *_tmp_151_rule(Parser *p); -static asdl_seq *_loop0_153_rule(Parser *p); -static asdl_seq *_gather_152_rule(Parser *p); +static asdl_seq *_loop0_152_rule(Parser *p); +static asdl_seq *_gather_151_rule(Parser *p); +static void *_tmp_153_rule(Parser *p); static void *_tmp_154_rule(Parser *p); static void *_tmp_155_rule(Parser *p); static void *_tmp_156_rule(Parser *p); @@ -1030,49 +1021,49 @@ static void *_tmp_159_rule(Parser *p); static void *_tmp_160_rule(Parser *p); static void *_tmp_161_rule(Parser *p); static void *_tmp_162_rule(Parser *p); -static void *_tmp_163_rule(Parser *p); +static asdl_seq *_loop0_163_rule(Parser *p); static asdl_seq *_loop0_164_rule(Parser *p); static asdl_seq *_loop0_165_rule(Parser *p); -static asdl_seq *_loop0_166_rule(Parser *p); +static void *_tmp_166_rule(Parser *p); static void *_tmp_167_rule(Parser *p); static void *_tmp_168_rule(Parser *p); static void *_tmp_169_rule(Parser *p); -static void *_tmp_170_rule(Parser *p); -static void *_tmp_171_rule(Parser *p); +static asdl_seq *_loop0_170_rule(Parser *p); +static asdl_seq *_loop0_171_rule(Parser *p); static asdl_seq *_loop0_172_rule(Parser *p); -static asdl_seq *_loop0_173_rule(Parser *p); -static asdl_seq *_loop0_174_rule(Parser *p); -static asdl_seq *_loop1_175_rule(Parser *p); +static asdl_seq *_loop1_173_rule(Parser *p); +static void *_tmp_174_rule(Parser *p); +static asdl_seq *_loop0_175_rule(Parser *p); static void *_tmp_176_rule(Parser *p); static asdl_seq *_loop0_177_rule(Parser *p); -static void *_tmp_178_rule(Parser *p); -static asdl_seq *_loop0_179_rule(Parser *p); -static asdl_seq *_loop1_180_rule(Parser *p); +static asdl_seq *_loop1_178_rule(Parser *p); +static void *_tmp_179_rule(Parser *p); +static void *_tmp_180_rule(Parser *p); static void *_tmp_181_rule(Parser *p); -static void *_tmp_182_rule(Parser *p); +static asdl_seq *_loop0_182_rule(Parser *p); static void *_tmp_183_rule(Parser *p); -static asdl_seq *_loop0_184_rule(Parser *p); -static void *_tmp_185_rule(Parser *p); +static void *_tmp_184_rule(Parser *p); +static asdl_seq *_loop1_185_rule(Parser *p); static void *_tmp_186_rule(Parser *p); -static asdl_seq *_loop1_187_rule(Parser *p); -static void *_tmp_188_rule(Parser *p); +static asdl_seq *_loop0_187_rule(Parser *p); +static asdl_seq *_loop0_188_rule(Parser *p); static asdl_seq *_loop0_189_rule(Parser *p); -static asdl_seq *_loop0_190_rule(Parser *p); static asdl_seq *_loop0_191_rule(Parser *p); +static asdl_seq *_gather_190_rule(Parser *p); +static void *_tmp_192_rule(Parser *p); static asdl_seq *_loop0_193_rule(Parser *p); -static asdl_seq *_gather_192_rule(Parser *p); static void *_tmp_194_rule(Parser *p); static asdl_seq *_loop0_195_rule(Parser *p); -static void *_tmp_196_rule(Parser *p); -static asdl_seq *_loop0_197_rule(Parser *p); -static asdl_seq *_loop1_198_rule(Parser *p); -static asdl_seq *_loop1_199_rule(Parser *p); -static void *_tmp_200_rule(Parser *p); +static asdl_seq *_loop1_196_rule(Parser *p); +static asdl_seq *_loop1_197_rule(Parser *p); +static void *_tmp_198_rule(Parser *p); +static void *_tmp_199_rule(Parser *p); +static asdl_seq *_loop0_200_rule(Parser *p); static void *_tmp_201_rule(Parser *p); -static asdl_seq *_loop0_202_rule(Parser *p); +static void *_tmp_202_rule(Parser *p); static void *_tmp_203_rule(Parser *p); -static void *_tmp_204_rule(Parser *p); -static void *_tmp_205_rule(Parser *p); +static asdl_seq *_loop0_205_rule(Parser *p); +static asdl_seq *_gather_204_rule(Parser *p); static asdl_seq *_loop0_207_rule(Parser *p); static asdl_seq *_gather_206_rule(Parser *p); static asdl_seq *_loop0_209_rule(Parser *p); @@ -1081,14 +1072,14 @@ static asdl_seq *_loop0_211_rule(Parser *p); static asdl_seq *_gather_210_rule(Parser *p); static asdl_seq *_loop0_213_rule(Parser *p); static asdl_seq *_gather_212_rule(Parser *p); +static void *_tmp_214_rule(Parser *p); static asdl_seq *_loop0_215_rule(Parser *p); -static asdl_seq *_gather_214_rule(Parser *p); -static void *_tmp_216_rule(Parser *p); -static asdl_seq *_loop0_217_rule(Parser *p); -static asdl_seq *_loop1_218_rule(Parser *p); -static void *_tmp_219_rule(Parser *p); -static asdl_seq *_loop0_220_rule(Parser *p); -static asdl_seq *_loop1_221_rule(Parser *p); +static asdl_seq *_loop1_216_rule(Parser *p); +static void *_tmp_217_rule(Parser *p); +static asdl_seq *_loop0_218_rule(Parser *p); +static asdl_seq *_loop1_219_rule(Parser *p); +static void *_tmp_220_rule(Parser *p); +static void *_tmp_221_rule(Parser *p); static void *_tmp_222_rule(Parser *p); static void *_tmp_223_rule(Parser *p); static void *_tmp_224_rule(Parser *p); @@ -1097,16 +1088,16 @@ static void *_tmp_226_rule(Parser *p); static void *_tmp_227_rule(Parser *p); static void *_tmp_228_rule(Parser *p); static void *_tmp_229_rule(Parser *p); -static void *_tmp_230_rule(Parser *p); -static void *_tmp_231_rule(Parser *p); -static asdl_seq *_loop0_233_rule(Parser *p); -static asdl_seq *_gather_232_rule(Parser *p); +static asdl_seq *_loop0_231_rule(Parser *p); +static asdl_seq *_gather_230_rule(Parser *p); +static void *_tmp_232_rule(Parser *p); +static void *_tmp_233_rule(Parser *p); static void *_tmp_234_rule(Parser *p); static void *_tmp_235_rule(Parser *p); static void *_tmp_236_rule(Parser *p); static void *_tmp_237_rule(Parser *p); static void *_tmp_238_rule(Parser *p); -static void *_tmp_239_rule(Parser *p); +static asdl_seq *_loop0_239_rule(Parser *p); static void *_tmp_240_rule(Parser *p); static void *_tmp_241_rule(Parser *p); static void *_tmp_242_rule(Parser *p); @@ -1114,7 +1105,7 @@ static void *_tmp_243_rule(Parser *p); static void *_tmp_244_rule(Parser *p); static void *_tmp_245_rule(Parser *p); static void *_tmp_246_rule(Parser *p); -static asdl_seq *_loop0_247_rule(Parser *p); +static void *_tmp_247_rule(Parser *p); static void *_tmp_248_rule(Parser *p); static void *_tmp_249_rule(Parser *p); static void *_tmp_250_rule(Parser *p); @@ -1123,7 +1114,7 @@ static void *_tmp_252_rule(Parser *p); static void *_tmp_253_rule(Parser *p); static void *_tmp_254_rule(Parser *p); static void *_tmp_255_rule(Parser *p); -static void *_tmp_256_rule(Parser *p); +static asdl_seq *_loop0_256_rule(Parser *p); static void *_tmp_257_rule(Parser *p); static void *_tmp_258_rule(Parser *p); static void *_tmp_259_rule(Parser *p); @@ -1132,7 +1123,7 @@ static void *_tmp_261_rule(Parser *p); static void *_tmp_262_rule(Parser *p); static void *_tmp_263_rule(Parser *p); static void *_tmp_264_rule(Parser *p); -static asdl_seq *_loop0_265_rule(Parser *p); +static void *_tmp_265_rule(Parser *p); static void *_tmp_266_rule(Parser *p); static void *_tmp_267_rule(Parser *p); static void *_tmp_268_rule(Parser *p); @@ -1140,23 +1131,14 @@ static void *_tmp_269_rule(Parser *p); static void *_tmp_270_rule(Parser *p); static void *_tmp_271_rule(Parser *p); static void *_tmp_272_rule(Parser *p); -static void *_tmp_273_rule(Parser *p); -static void *_tmp_274_rule(Parser *p); +static asdl_seq *_loop0_274_rule(Parser *p); +static asdl_seq *_gather_273_rule(Parser *p); static void *_tmp_275_rule(Parser *p); static void *_tmp_276_rule(Parser *p); static void *_tmp_277_rule(Parser *p); static void *_tmp_278_rule(Parser *p); static void *_tmp_279_rule(Parser *p); static void *_tmp_280_rule(Parser *p); -static void *_tmp_281_rule(Parser *p); -static asdl_seq *_loop0_283_rule(Parser *p); -static asdl_seq *_gather_282_rule(Parser *p); -static void *_tmp_284_rule(Parser *p); -static void *_tmp_285_rule(Parser *p); -static void *_tmp_286_rule(Parser *p); -static void *_tmp_287_rule(Parser *p); -static void *_tmp_288_rule(Parser *p); -static void *_tmp_289_rule(Parser *p); // file: statements? $ @@ -1820,7 +1802,7 @@ simple_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> simple_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&('import' | 'from') import_stmt")); stmt_ty import_stmt_var; if ( - _PyPegen_lookahead(1, _tmp_6_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_6_rule, p) && (import_stmt_var = import_stmt_rule(p)) // import_stmt ) @@ -2114,7 +2096,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&('def' | '@' | 'async') function_def")); stmt_ty function_def_var; if ( - _PyPegen_lookahead(1, _tmp_7_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_7_rule, p) && (function_def_var = function_def_rule(p)) // function_def ) @@ -2156,7 +2138,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&('class' | '@') class_def")); stmt_ty class_def_var; if ( - _PyPegen_lookahead(1, _tmp_8_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_8_rule, p) && (class_def_var = class_def_rule(p)) // class_def ) @@ -2177,7 +2159,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&('with' | 'async') with_stmt")); stmt_ty with_stmt_var; if ( - _PyPegen_lookahead(1, _tmp_9_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_9_rule, p) && (with_stmt_var = with_stmt_rule(p)) // with_stmt ) @@ -2198,7 +2180,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&('for' | 'async') for_stmt")); stmt_ty for_stmt_var; if ( - _PyPegen_lookahead(1, _tmp_10_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_10_rule, p) && (for_stmt_var = for_stmt_rule(p)) // for_stmt ) @@ -3229,7 +3211,7 @@ del_stmt_rule(Parser *p) && (a = del_targets_rule(p)) // del_targets && - _PyPegen_lookahead(1, _tmp_22_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_22_rule, p) ) { D(fprintf(stderr, "%*c+ del_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'del' del_targets &(';' | NEWLINE)")); @@ -6834,7 +6816,7 @@ with_item_rule(Parser *p) && (t = star_target_rule(p)) // star_target && - _PyPegen_lookahead(1, _tmp_59_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_59_rule, p) ) { D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); @@ -8239,7 +8221,7 @@ literal_pattern_rule(Parser *p) if ( (value = signed_number_rule(p)) // signed_number && - _PyPegen_lookahead(0, _tmp_67_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_67_rule, p) ) { D(fprintf(stderr, "%*c+ literal_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "signed_number !('+' | '-')")); @@ -8473,7 +8455,7 @@ literal_expr_rule(Parser *p) if ( (signed_number_var = signed_number_rule(p)) // signed_number && - _PyPegen_lookahead(0, _tmp_68_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_68_rule, p) ) { D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "signed_number !('+' | '-')")); @@ -9073,7 +9055,7 @@ pattern_capture_target_rule(Parser *p) && (name = _PyPegen_name_token(p)) // NAME && - _PyPegen_lookahead(0, _tmp_69_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_69_rule, p) ) { D(fprintf(stderr, "%*c+ pattern_capture_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!\"_\" NAME !('.' | '(' | '=')")); @@ -9188,7 +9170,7 @@ value_pattern_rule(Parser *p) if ( (attr = attr_rule(p)) // attr && - _PyPegen_lookahead(0, _tmp_70_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_70_rule, p) ) { D(fprintf(stderr, "%*c+ value_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "attr !('.' | '(' | '=')")); @@ -14769,7 +14751,7 @@ atom_rule(Parser *p) D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&(STRING | FSTRING_START) strings")); expr_ty strings_var; if ( - _PyPegen_lookahead(1, _tmp_93_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_93_rule, p) && (strings_var = strings_rule(p)) // strings ) @@ -15981,7 +15963,7 @@ fstring_middle_rule(Parser *p) } // fstring_replacement_field: -// | '{' (yield_expr | star_expressions) '='? fstring_conversion? fstring_full_format_spec? '}' +// | '{' annotated_rhs '='? fstring_conversion? fstring_full_format_spec? '}' // | invalid_replacement_field static expr_ty fstring_replacement_field_rule(Parser *p) @@ -16004,14 +15986,14 @@ fstring_replacement_field_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // '{' (yield_expr | star_expressions) '='? fstring_conversion? fstring_full_format_spec? '}' + { // '{' annotated_rhs '='? fstring_conversion? fstring_full_format_spec? '}' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? fstring_conversion? fstring_full_format_spec? '}'")); + D(fprintf(stderr, "%*c> fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? fstring_full_format_spec? '}'")); Token * _literal; - void *a; + expr_ty a; void *conversion; void *debug_expr; void *format; @@ -16019,7 +16001,7 @@ fstring_replacement_field_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (a = _tmp_112_rule(p)) // yield_expr | star_expressions + (a = annotated_rhs_rule(p)) // annotated_rhs && (debug_expr = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && @@ -16030,7 +16012,7 @@ fstring_replacement_field_rule(Parser *p) (rbrace = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? fstring_conversion? fstring_full_format_spec? '}'")); + D(fprintf(stderr, "%*c+ fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? fstring_full_format_spec? '}'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -16050,7 +16032,7 @@ fstring_replacement_field_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' (yield_expr | star_expressions) '='? fstring_conversion? fstring_full_format_spec? '}'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? fstring_full_format_spec? '}'")); } if (p->call_invalid_rules) { // invalid_replacement_field if (p->error_indicator) { @@ -16156,7 +16138,7 @@ fstring_full_format_spec_rule(Parser *p) if ( (colon = _PyPegen_expect_token(p, 11)) // token=':' && - (spec = _loop0_113_rule(p)) // fstring_format_spec* + (spec = _loop0_112_rule(p)) // fstring_format_spec* ) { D(fprintf(stderr, "%*c+ fstring_full_format_spec[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' fstring_format_spec*")); @@ -16274,7 +16256,7 @@ fstring_rule(Parser *p) if ( (a = _PyPegen_expect_token(p, FSTRING_START)) // token='FSTRING_START' && - (b = _loop0_114_rule(p)) // fstring_middle* + (b = _loop0_113_rule(p)) // fstring_middle* && (c = _PyPegen_expect_token(p, FSTRING_END)) // token='FSTRING_END' ) @@ -16375,7 +16357,7 @@ strings_rule(Parser *p) D(fprintf(stderr, "%*c> strings[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((fstring | string))+")); asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_loop1_115_rule(p)) // ((fstring | string))+ + (a = (asdl_expr_seq*)_loop1_114_rule(p)) // ((fstring | string))+ ) { D(fprintf(stderr, "%*c+ strings[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((fstring | string))+")); @@ -16508,7 +16490,7 @@ tuple_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (a = _tmp_116_rule(p), !p->error_indicator) // [star_named_expression ',' star_named_expressions?] + (a = _tmp_115_rule(p), !p->error_indicator) // [star_named_expression ',' star_named_expressions?] && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) @@ -16723,7 +16705,7 @@ double_starred_kvpairs_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_seq * a; if ( - (a = _gather_117_rule(p)) // ','.double_starred_kvpair+ + (a = _gather_116_rule(p)) // ','.double_starred_kvpair+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) @@ -16882,7 +16864,7 @@ for_if_clauses_rule(Parser *p) D(fprintf(stderr, "%*c> for_if_clauses[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause+")); asdl_comprehension_seq* a; if ( - (a = (asdl_comprehension_seq*)_loop1_119_rule(p)) // for_if_clause+ + (a = (asdl_comprehension_seq*)_loop1_118_rule(p)) // for_if_clause+ ) { D(fprintf(stderr, "%*c+ for_if_clauses[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "for_if_clause+")); @@ -16947,7 +16929,7 @@ for_if_clause_rule(Parser *p) && (b = disjunction_rule(p)) // disjunction && - (c = (asdl_expr_seq*)_loop0_120_rule(p)) // (('if' disjunction))* + (c = (asdl_expr_seq*)_loop0_119_rule(p)) // (('if' disjunction))* ) { D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async' 'for' star_targets 'in' ~ disjunction (('if' disjunction))*")); @@ -16990,7 +16972,7 @@ for_if_clause_rule(Parser *p) && (b = disjunction_rule(p)) // disjunction && - (c = (asdl_expr_seq*)_loop0_121_rule(p)) // (('if' disjunction))* + (c = (asdl_expr_seq*)_loop0_120_rule(p)) // (('if' disjunction))* ) { D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for' star_targets 'in' ~ disjunction (('if' disjunction))*")); @@ -17019,13 +17001,13 @@ for_if_clause_rule(Parser *p) Token * _keyword; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_122_var; + void *_tmp_121_var; if ( (_opt_var = _PyPegen_expect_token(p, 674), !p->error_indicator) // 'async'? && (_keyword = _PyPegen_expect_token(p, 672)) // token='for' && - (_tmp_122_var = _tmp_122_rule(p)) // bitwise_or ((',' bitwise_or))* ','? + (_tmp_121_var = _tmp_121_rule(p)) // bitwise_or ((',' bitwise_or))* ','? && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 673) // token='in' ) @@ -17283,7 +17265,7 @@ genexp_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (a = _tmp_123_rule(p)) // assignment_expression | expression !':=' + (a = _tmp_122_rule(p)) // assignment_expression | expression !':=' && (b = for_if_clauses_rule(p)) // for_if_clauses && @@ -17532,9 +17514,9 @@ args_rule(Parser *p) asdl_expr_seq* a; void *b; if ( - (a = (asdl_expr_seq*)_gather_124_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ + (a = (asdl_expr_seq*)_gather_123_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ && - (b = _tmp_126_rule(p), !p->error_indicator) // [',' kwargs] + (b = _tmp_125_rule(p), !p->error_indicator) // [',' kwargs] ) { D(fprintf(stderr, "%*c+ args[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ [',' kwargs]")); @@ -17624,11 +17606,11 @@ kwargs_rule(Parser *p) asdl_seq * a; asdl_seq * b; if ( - (a = _gather_127_rule(p)) // ','.kwarg_or_starred+ + (a = _gather_126_rule(p)) // ','.kwarg_or_starred+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (b = _gather_129_rule(p)) // ','.kwarg_or_double_starred+ + (b = _gather_128_rule(p)) // ','.kwarg_or_double_starred+ ) { D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+ ',' ','.kwarg_or_double_starred+")); @@ -17650,13 +17632,13 @@ kwargs_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> kwargs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+")); - asdl_seq * _gather_131_var; + asdl_seq * _gather_130_var; if ( - (_gather_131_var = _gather_131_rule(p)) // ','.kwarg_or_starred+ + (_gather_130_var = _gather_130_rule(p)) // ','.kwarg_or_starred+ ) { D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+")); - _res = _gather_131_var; + _res = _gather_130_var; goto done; } p->mark = _mark; @@ -17669,13 +17651,13 @@ kwargs_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> kwargs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_double_starred+")); - asdl_seq * _gather_133_var; + asdl_seq * _gather_132_var; if ( - (_gather_133_var = _gather_133_rule(p)) // ','.kwarg_or_double_starred+ + (_gather_132_var = _gather_132_rule(p)) // ','.kwarg_or_double_starred+ ) { D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_double_starred+")); - _res = _gather_133_var; + _res = _gather_132_var; goto done; } p->mark = _mark; @@ -18088,7 +18070,7 @@ star_targets_rule(Parser *p) if ( (a = star_target_rule(p)) // star_target && - (b = _loop0_135_rule(p)) // ((',' star_target))* + (b = _loop0_134_rule(p)) // ((',' star_target))* && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) @@ -18144,7 +18126,7 @@ star_targets_list_seq_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_gather_136_rule(p)) // ','.star_target+ + (a = (asdl_expr_seq*)_gather_135_rule(p)) // ','.star_target+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) @@ -18194,7 +18176,7 @@ star_targets_tuple_seq_rule(Parser *p) if ( (a = star_target_rule(p)) // star_target && - (b = _loop1_138_rule(p)) // ((',' star_target))+ + (b = _loop1_137_rule(p)) // ((',' star_target))+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) @@ -18282,7 +18264,7 @@ star_target_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (a = _tmp_139_rule(p)) // !'*' star_target + (a = _tmp_138_rule(p)) // !'*' star_target ) { D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (!'*' star_target)")); @@ -18378,7 +18360,7 @@ target_with_star_atom_rule(Parser *p) && (b = _PyPegen_name_token(p)) // NAME && - _PyPegen_lookahead(0, t_lookahead_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ target_with_star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '.' NAME !t_lookahead")); @@ -18422,7 +18404,7 @@ target_with_star_atom_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 10)) // token=']' && - _PyPegen_lookahead(0, t_lookahead_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ target_with_star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '[' slices ']' !t_lookahead")); @@ -18769,7 +18751,7 @@ single_subscript_attribute_target_rule(Parser *p) && (b = _PyPegen_name_token(p)) // NAME && - _PyPegen_lookahead(0, t_lookahead_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ single_subscript_attribute_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '.' NAME !t_lookahead")); @@ -18813,7 +18795,7 @@ single_subscript_attribute_target_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 10)) // token=']' && - _PyPegen_lookahead(0, t_lookahead_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ single_subscript_attribute_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '[' slices ']' !t_lookahead")); @@ -18923,7 +18905,7 @@ t_primary_raw(Parser *p) && (b = _PyPegen_name_token(p)) // NAME && - _PyPegen_lookahead(1, t_lookahead_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ t_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '.' NAME &t_lookahead")); @@ -18967,7 +18949,7 @@ t_primary_raw(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 10)) // token=']' && - _PyPegen_lookahead(1, t_lookahead_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ t_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '[' slices ']' &t_lookahead")); @@ -19005,7 +18987,7 @@ t_primary_raw(Parser *p) && (b = genexp_rule(p)) // genexp && - _PyPegen_lookahead(1, t_lookahead_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ t_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary genexp &t_lookahead")); @@ -19049,7 +19031,7 @@ t_primary_raw(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' && - _PyPegen_lookahead(1, t_lookahead_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ t_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '(' arguments? ')' &t_lookahead")); @@ -19084,7 +19066,7 @@ t_primary_raw(Parser *p) if ( (a = atom_rule(p)) // atom && - _PyPegen_lookahead(1, t_lookahead_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ t_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "atom &t_lookahead")); @@ -19205,7 +19187,7 @@ del_targets_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_gather_140_rule(p)) // ','.del_target+ + (a = (asdl_expr_seq*)_gather_139_rule(p)) // ','.del_target+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) @@ -19274,7 +19256,7 @@ del_target_rule(Parser *p) && (b = _PyPegen_name_token(p)) // NAME && - _PyPegen_lookahead(0, t_lookahead_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ del_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '.' NAME !t_lookahead")); @@ -19318,7 +19300,7 @@ del_target_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 10)) // token=']' && - _PyPegen_lookahead(0, t_lookahead_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) t_lookahead_rule, p) ) { D(fprintf(stderr, "%*c+ del_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '[' slices ']' !t_lookahead")); @@ -19563,7 +19545,7 @@ type_expressions_rule(Parser *p) expr_ty b; expr_ty c; if ( - (a = _gather_142_rule(p)) // ','.expression+ + (a = _gather_141_rule(p)) // ','.expression+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && @@ -19602,7 +19584,7 @@ type_expressions_rule(Parser *p) asdl_seq * a; expr_ty b; if ( - (a = _gather_144_rule(p)) // ','.expression+ + (a = _gather_143_rule(p)) // ','.expression+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && @@ -19635,7 +19617,7 @@ type_expressions_rule(Parser *p) asdl_seq * a; expr_ty b; if ( - (a = _gather_146_rule(p)) // ','.expression+ + (a = _gather_145_rule(p)) // ','.expression+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && @@ -19755,7 +19737,7 @@ type_expressions_rule(Parser *p) D(fprintf(stderr, "%*c> type_expressions[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.expression+")); asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_gather_148_rule(p)) // ','.expression+ + (a = (asdl_expr_seq*)_gather_147_rule(p)) // ','.expression+ ) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.expression+")); @@ -19806,7 +19788,7 @@ func_type_comment_rule(Parser *p) && (t = _PyPegen_expect_token(p, TYPE_COMMENT)) // token='TYPE_COMMENT' && - _PyPegen_lookahead(1, _tmp_150_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_149_rule, p) ) { D(fprintf(stderr, "%*c+ func_type_comment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE TYPE_COMMENT &(NEWLINE INDENT)")); @@ -19892,15 +19874,15 @@ invalid_arguments_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' ','.(starred_expression !'=')+")); - asdl_seq * _gather_152_var; - void *_tmp_151_var; + asdl_seq * _gather_151_var; + void *_tmp_150_var; Token * a; if ( - (_tmp_151_var = _tmp_151_rule(p)) // (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs + (_tmp_150_var = _tmp_150_rule(p)) // (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs && (a = _PyPegen_expect_token(p, 12)) // token=',' && - (_gather_152_var = _gather_152_rule(p)) // ','.(starred_expression !'=')+ + (_gather_151_var = _gather_151_rule(p)) // ','.(starred_expression !'=')+ ) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' ','.(starred_expression !'=')+")); @@ -19934,7 +19916,7 @@ invalid_arguments_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_opt_var = _tmp_154_rule(p), !p->error_indicator) // [args | expression for_if_clauses] + (_opt_var = _tmp_153_rule(p), !p->error_indicator) // [args | expression for_if_clauses] ) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); @@ -19994,13 +19976,13 @@ invalid_arguments_rule(Parser *p) expr_ty a; Token * b; if ( - (_opt_var = _tmp_155_rule(p), !p->error_indicator) // [(args ',')] + (_opt_var = _tmp_154_rule(p), !p->error_indicator) // [(args ',')] && (a = _PyPegen_name_token(p)) // NAME && (b = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(1, _tmp_156_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_155_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "[(args ',')] NAME '=' &(',' | ')')")); @@ -20138,7 +20120,7 @@ invalid_kwarg_rule(Parser *p) Token* a; Token * b; if ( - (a = (Token*)_tmp_157_rule(p)) // 'True' | 'False' | 'None' + (a = (Token*)_tmp_156_rule(p)) // 'True' | 'False' | 'None' && (b = _PyPegen_expect_token(p, 22)) // token='=' ) @@ -20198,7 +20180,7 @@ invalid_kwarg_rule(Parser *p) expr_ty a; Token * b; if ( - _PyPegen_lookahead(0, _tmp_158_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_157_rule, p) && (a = expression_rule(p)) // expression && @@ -20454,7 +20436,7 @@ invalid_expression_rule(Parser *p) expr_ty a; expr_ty b; if ( - _PyPegen_lookahead(0, _tmp_159_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_158_rule, p) && (a = disjunction_rule(p)) // disjunction && @@ -20490,7 +20472,7 @@ invalid_expression_rule(Parser *p) && (b = disjunction_rule(p)) // disjunction && - _PyPegen_lookahead(0, _tmp_160_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_159_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction !('else' | ':')")); @@ -20611,7 +20593,7 @@ invalid_named_expression_rule(Parser *p) && (b = bitwise_or_rule(p)) // bitwise_or && - _PyPegen_lookahead(0, _tmp_161_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_160_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '=' bitwise_or !('=' | ':=')")); @@ -20637,7 +20619,7 @@ invalid_named_expression_rule(Parser *p) Token * b; expr_ty bitwise_or_var; if ( - _PyPegen_lookahead(0, _tmp_162_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_161_rule, p) && (a = bitwise_or_rule(p)) // bitwise_or && @@ -20645,7 +20627,7 @@ invalid_named_expression_rule(Parser *p) && (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or && - _PyPegen_lookahead(0, _tmp_163_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_162_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!(list | tuple | genexp | 'True' | 'None' | 'False') bitwise_or '=' bitwise_or !('=' | ':=')")); @@ -20674,7 +20656,7 @@ invalid_named_expression_rule(Parser *p) // | expression ':' expression // | ((star_targets '='))* star_expressions '=' // | ((star_targets '='))* yield_expr '=' -// | star_expressions augassign (yield_expr | star_expressions) +// | star_expressions augassign annotated_rhs static void * invalid_assignment_rule(Parser *p) { @@ -20725,7 +20707,7 @@ invalid_assignment_rule(Parser *p) D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions* ':' expression")); Token * _literal; Token * _literal_1; - asdl_seq * _loop0_164_var; + asdl_seq * _loop0_163_var; expr_ty a; expr_ty expression_var; if ( @@ -20733,7 +20715,7 @@ invalid_assignment_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_loop0_164_var = _loop0_164_rule(p)) // star_named_expressions* + (_loop0_163_var = _loop0_163_rule(p)) // star_named_expressions* && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -20790,10 +20772,10 @@ invalid_assignment_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* star_expressions '='")); Token * _literal; - asdl_seq * _loop0_165_var; + asdl_seq * _loop0_164_var; expr_ty a; if ( - (_loop0_165_var = _loop0_165_rule(p)) // ((star_targets '='))* + (_loop0_164_var = _loop0_164_rule(p)) // ((star_targets '='))* && (a = star_expressions_rule(p)) // star_expressions && @@ -20820,10 +20802,10 @@ invalid_assignment_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* yield_expr '='")); Token * _literal; - asdl_seq * _loop0_166_var; + asdl_seq * _loop0_165_var; expr_ty a; if ( - (_loop0_166_var = _loop0_166_rule(p)) // ((star_targets '='))* + (_loop0_165_var = _loop0_165_rule(p)) // ((star_targets '='))* && (a = yield_expr_rule(p)) // yield_expr && @@ -20843,24 +20825,24 @@ invalid_assignment_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_assignment[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "((star_targets '='))* yield_expr '='")); } - { // star_expressions augassign (yield_expr | star_expressions) + { // star_expressions augassign annotated_rhs if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)")); - void *_tmp_167_var; + D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions augassign annotated_rhs")); expr_ty a; + expr_ty annotated_rhs_var; AugOperator* augassign_var; if ( (a = star_expressions_rule(p)) // star_expressions && (augassign_var = augassign_rule(p)) // augassign && - (_tmp_167_var = _tmp_167_rule(p)) // yield_expr | star_expressions + (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs ) { - D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)")); + D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions augassign annotated_rhs")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "'%s' is an illegal expression for augmented assignment" , _PyPegen_get_expr_name ( a ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -20871,7 +20853,7 @@ invalid_assignment_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_assignment[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions augassign annotated_rhs")); } _res = NULL; done: @@ -21079,11 +21061,11 @@ invalid_comprehension_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '(' | '{') starred_expression for_if_clauses")); - void *_tmp_168_var; + void *_tmp_166_var; expr_ty a; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_168_var = _tmp_168_rule(p)) // '[' | '(' | '{' + (_tmp_166_var = _tmp_166_rule(p)) // '[' | '(' | '{' && (a = starred_expression_rule(p)) // starred_expression && @@ -21110,12 +21092,12 @@ invalid_comprehension_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses")); Token * _literal; - void *_tmp_169_var; + void *_tmp_167_var; expr_ty a; asdl_expr_seq* b; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_169_var = _tmp_169_rule(p)) // '[' | '{' + (_tmp_167_var = _tmp_167_rule(p)) // '[' | '{' && (a = star_named_expression_rule(p)) // star_named_expression && @@ -21145,12 +21127,12 @@ invalid_comprehension_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses")); - void *_tmp_170_var; + void *_tmp_168_var; expr_ty a; Token * b; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_170_var = _tmp_170_rule(p)) // '[' | '{' + (_tmp_168_var = _tmp_168_rule(p)) // '[' | '{' && (a = star_named_expression_rule(p)) // star_named_expression && @@ -21285,13 +21267,13 @@ invalid_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slash_no_default | slash_with_default) param_maybe_default* '/'")); - asdl_seq * _loop0_172_var; - void *_tmp_171_var; + asdl_seq * _loop0_170_var; + void *_tmp_169_var; Token * a; if ( - (_tmp_171_var = _tmp_171_rule(p)) // slash_no_default | slash_with_default + (_tmp_169_var = _tmp_169_rule(p)) // slash_no_default | slash_with_default && - (_loop0_172_var = _loop0_172_rule(p)) // param_maybe_default* + (_loop0_170_var = _loop0_170_rule(p)) // param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) @@ -21315,7 +21297,7 @@ invalid_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default? param_no_default* invalid_parameters_helper param_no_default")); - asdl_seq * _loop0_173_var; + asdl_seq * _loop0_171_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings arg_ty a; @@ -21323,7 +21305,7 @@ invalid_parameters_rule(Parser *p) if ( (_opt_var = slash_no_default_rule(p), !p->error_indicator) // slash_no_default? && - (_loop0_173_var = _loop0_173_rule(p)) // param_no_default* + (_loop0_171_var = _loop0_171_rule(p)) // param_no_default* && (invalid_parameters_helper_var = invalid_parameters_helper_rule(p)) // invalid_parameters_helper && @@ -21349,18 +21331,18 @@ invalid_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default* '(' param_no_default+ ','? ')'")); - asdl_seq * _loop0_174_var; - asdl_seq * _loop1_175_var; + asdl_seq * _loop0_172_var; + asdl_seq * _loop1_173_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; Token * b; if ( - (_loop0_174_var = _loop0_174_rule(p)) // param_no_default* + (_loop0_172_var = _loop0_172_rule(p)) // param_no_default* && (a = _PyPegen_expect_token(p, 7)) // token='(' && - (_loop1_175_var = _loop1_175_rule(p)) // param_no_default+ + (_loop1_173_var = _loop1_173_rule(p)) // param_no_default+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -21387,22 +21369,22 @@ invalid_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "[(slash_no_default | slash_with_default)] param_maybe_default* '*' (',' | param_no_default) param_maybe_default* '/'")); Token * _literal; + asdl_seq * _loop0_175_var; asdl_seq * _loop0_177_var; - asdl_seq * _loop0_179_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_178_var; + void *_tmp_176_var; Token * a; if ( - (_opt_var = _tmp_176_rule(p), !p->error_indicator) // [(slash_no_default | slash_with_default)] + (_opt_var = _tmp_174_rule(p), !p->error_indicator) // [(slash_no_default | slash_with_default)] && - (_loop0_177_var = _loop0_177_rule(p)) // param_maybe_default* + (_loop0_175_var = _loop0_175_rule(p)) // param_maybe_default* && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_178_var = _tmp_178_rule(p)) // ',' | param_no_default + (_tmp_176_var = _tmp_176_rule(p)) // ',' | param_no_default && - (_loop0_179_var = _loop0_179_rule(p)) // param_maybe_default* + (_loop0_177_var = _loop0_177_rule(p)) // param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) @@ -21427,10 +21409,10 @@ invalid_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default+ '/' '*'")); Token * _literal; - asdl_seq * _loop1_180_var; + asdl_seq * _loop1_178_var; Token * a; if ( - (_loop1_180_var = _loop1_180_rule(p)) // param_maybe_default+ + (_loop1_178_var = _loop1_178_rule(p)) // param_maybe_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -21479,7 +21461,7 @@ invalid_default_rule(Parser *p) if ( (a = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(1, _tmp_181_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_179_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' &(')' | ',')")); @@ -21524,12 +21506,12 @@ invalid_star_etc_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))")); - void *_tmp_182_var; + void *_tmp_180_var; Token * a; if ( (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_182_var = _tmp_182_rule(p)) // ')' | ',' (')' | '**') + (_tmp_180_var = _tmp_180_rule(p)) // ')' | ',' (')' | '**') ) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))")); @@ -21612,20 +21594,20 @@ invalid_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (param_no_default | ',') param_maybe_default* '*' (param_no_default | ',')")); Token * _literal; - asdl_seq * _loop0_184_var; + asdl_seq * _loop0_182_var; + void *_tmp_181_var; void *_tmp_183_var; - void *_tmp_185_var; Token * a; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_183_var = _tmp_183_rule(p)) // param_no_default | ',' + (_tmp_181_var = _tmp_181_rule(p)) // param_no_default | ',' && - (_loop0_184_var = _loop0_184_rule(p)) // param_maybe_default* + (_loop0_182_var = _loop0_182_rule(p)) // param_maybe_default* && (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_185_var = _tmp_185_rule(p)) // param_no_default | ',' + (_tmp_183_var = _tmp_183_rule(p)) // param_no_default | ',' ) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (param_no_default | ',') param_maybe_default* '*' (param_no_default | ',')")); @@ -21740,7 +21722,7 @@ invalid_kwds_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' && - (a = (Token*)_tmp_186_rule(p)) // '*' | '**' | '/' + (a = (Token*)_tmp_184_rule(p)) // '*' | '**' | '/' ) { D(fprintf(stderr, "%*c+ invalid_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' param ',' ('*' | '**' | '/')")); @@ -21805,13 +21787,13 @@ invalid_parameters_helper_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters_helper[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default+")); - asdl_seq * _loop1_187_var; + asdl_seq * _loop1_185_var; if ( - (_loop1_187_var = _loop1_187_rule(p)) // param_with_default+ + (_loop1_185_var = _loop1_185_rule(p)) // param_with_default+ ) { D(fprintf(stderr, "%*c+ invalid_parameters_helper[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_with_default+")); - _res = _loop1_187_var; + _res = _loop1_185_var; goto done; } p->mark = _mark; @@ -21876,13 +21858,13 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* '/'")); - asdl_seq * _loop0_189_var; - void *_tmp_188_var; + asdl_seq * _loop0_187_var; + void *_tmp_186_var; Token * a; if ( - (_tmp_188_var = _tmp_188_rule(p)) // lambda_slash_no_default | lambda_slash_with_default + (_tmp_186_var = _tmp_186_rule(p)) // lambda_slash_no_default | lambda_slash_with_default && - (_loop0_189_var = _loop0_189_rule(p)) // lambda_param_maybe_default* + (_loop0_187_var = _loop0_187_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) @@ -21906,7 +21888,7 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default? lambda_param_no_default* invalid_lambda_parameters_helper lambda_param_no_default")); - asdl_seq * _loop0_190_var; + asdl_seq * _loop0_188_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings arg_ty a; @@ -21914,7 +21896,7 @@ invalid_lambda_parameters_rule(Parser *p) if ( (_opt_var = lambda_slash_no_default_rule(p), !p->error_indicator) // lambda_slash_no_default? && - (_loop0_190_var = _loop0_190_rule(p)) // lambda_param_no_default* + (_loop0_188_var = _loop0_188_rule(p)) // lambda_param_no_default* && (invalid_lambda_parameters_helper_var = invalid_lambda_parameters_helper_rule(p)) // invalid_lambda_parameters_helper && @@ -21940,18 +21922,18 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* '(' ','.lambda_param+ ','? ')'")); - asdl_seq * _gather_192_var; - asdl_seq * _loop0_191_var; + asdl_seq * _gather_190_var; + asdl_seq * _loop0_189_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; Token * b; if ( - (_loop0_191_var = _loop0_191_rule(p)) // lambda_param_no_default* + (_loop0_189_var = _loop0_189_rule(p)) // lambda_param_no_default* && (a = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_192_var = _gather_192_rule(p)) // ','.lambda_param+ + (_gather_190_var = _gather_190_rule(p)) // ','.lambda_param+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -21978,22 +21960,22 @@ invalid_lambda_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "[(lambda_slash_no_default | lambda_slash_with_default)] lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* '/'")); Token * _literal; + asdl_seq * _loop0_193_var; asdl_seq * _loop0_195_var; - asdl_seq * _loop0_197_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_196_var; + void *_tmp_194_var; Token * a; if ( - (_opt_var = _tmp_194_rule(p), !p->error_indicator) // [(lambda_slash_no_default | lambda_slash_with_default)] + (_opt_var = _tmp_192_rule(p), !p->error_indicator) // [(lambda_slash_no_default | lambda_slash_with_default)] && - (_loop0_195_var = _loop0_195_rule(p)) // lambda_param_maybe_default* + (_loop0_193_var = _loop0_193_rule(p)) // lambda_param_maybe_default* && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_196_var = _tmp_196_rule(p)) // ',' | lambda_param_no_default + (_tmp_194_var = _tmp_194_rule(p)) // ',' | lambda_param_no_default && - (_loop0_197_var = _loop0_197_rule(p)) // lambda_param_maybe_default* + (_loop0_195_var = _loop0_195_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) @@ -22018,10 +22000,10 @@ invalid_lambda_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default+ '/' '*'")); Token * _literal; - asdl_seq * _loop1_198_var; + asdl_seq * _loop1_196_var; Token * a; if ( - (_loop1_198_var = _loop1_198_rule(p)) // lambda_param_maybe_default+ + (_loop1_196_var = _loop1_196_rule(p)) // lambda_param_maybe_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -22092,13 +22074,13 @@ invalid_lambda_parameters_helper_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters_helper[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); - asdl_seq * _loop1_199_var; + asdl_seq * _loop1_197_var; if ( - (_loop1_199_var = _loop1_199_rule(p)) // lambda_param_with_default+ + (_loop1_197_var = _loop1_197_rule(p)) // lambda_param_with_default+ ) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters_helper[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); - _res = _loop1_199_var; + _res = _loop1_197_var; goto done; } p->mark = _mark; @@ -22134,11 +22116,11 @@ invalid_lambda_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))")); Token * _literal; - void *_tmp_200_var; + void *_tmp_198_var; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_200_var = _tmp_200_rule(p)) // ':' | ',' (':' | '**') + (_tmp_198_var = _tmp_198_rule(p)) // ':' | ',' (':' | '**') ) { D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))")); @@ -22191,20 +22173,20 @@ invalid_lambda_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (lambda_param_no_default | ',') lambda_param_maybe_default* '*' (lambda_param_no_default | ',')")); Token * _literal; - asdl_seq * _loop0_202_var; + asdl_seq * _loop0_200_var; + void *_tmp_199_var; void *_tmp_201_var; - void *_tmp_203_var; Token * a; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_201_var = _tmp_201_rule(p)) // lambda_param_no_default | ',' + (_tmp_199_var = _tmp_199_rule(p)) // lambda_param_no_default | ',' && - (_loop0_202_var = _loop0_202_rule(p)) // lambda_param_maybe_default* + (_loop0_200_var = _loop0_200_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_203_var = _tmp_203_rule(p)) // lambda_param_no_default | ',' + (_tmp_201_var = _tmp_201_rule(p)) // lambda_param_no_default | ',' ) { D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (lambda_param_no_default | ',') lambda_param_maybe_default* '*' (lambda_param_no_default | ',')")); @@ -22322,7 +22304,7 @@ invalid_lambda_kwds_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' && - (a = (Token*)_tmp_204_rule(p)) // '*' | '**' | '/' + (a = (Token*)_tmp_202_rule(p)) // '*' | '**' | '/' ) { D(fprintf(stderr, "%*c+ invalid_lambda_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' lambda_param ',' ('*' | '**' | '/')")); @@ -22428,7 +22410,7 @@ invalid_with_item_rule(Parser *p) && (a = expression_rule(p)) // expression && - _PyPegen_lookahead(1, _tmp_205_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_203_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' expression &(',' | ')' | ':')")); @@ -22601,14 +22583,14 @@ invalid_import_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_import[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'import' ','.dotted_name+ 'from' dotted_name")); - asdl_seq * _gather_206_var; + asdl_seq * _gather_204_var; Token * _keyword; Token * a; expr_ty dotted_name_var; if ( (a = _PyPegen_expect_token(p, 622)) // token='import' && - (_gather_206_var = _gather_206_rule(p)) // ','.dotted_name+ + (_gather_204_var = _gather_204_rule(p)) // ','.dotted_name+ && (_keyword = _PyPegen_expect_token(p, 621)) // token='from' && @@ -22831,7 +22813,7 @@ invalid_with_stmt_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ NEWLINE")); - asdl_seq * _gather_208_var; + asdl_seq * _gather_206_var; Token * _keyword; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings @@ -22841,7 +22823,7 @@ invalid_with_stmt_rule(Parser *p) && (_keyword = _PyPegen_expect_token(p, 635)) // token='with' && - (_gather_208_var = _gather_208_rule(p)) // ','.(expression ['as' star_target])+ + (_gather_206_var = _gather_206_rule(p)) // ','.(expression ['as' star_target])+ && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -22865,7 +22847,7 @@ invalid_with_stmt_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE")); - asdl_seq * _gather_210_var; + asdl_seq * _gather_208_var; Token * _keyword; Token * _literal; Token * _literal_1; @@ -22881,7 +22863,7 @@ invalid_with_stmt_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_210_var = _gather_210_rule(p)) // ','.(expressions ['as' star_target])+ + (_gather_208_var = _gather_208_rule(p)) // ','.(expressions ['as' star_target])+ && (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -22930,7 +22912,7 @@ invalid_with_stmt_indent_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt_indent[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ ':' NEWLINE !INDENT")); - asdl_seq * _gather_212_var; + asdl_seq * _gather_210_var; Token * _literal; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings @@ -22941,7 +22923,7 @@ invalid_with_stmt_indent_rule(Parser *p) && (a = _PyPegen_expect_token(p, 635)) // token='with' && - (_gather_212_var = _gather_212_rule(p)) // ','.(expression ['as' star_target])+ + (_gather_210_var = _gather_210_rule(p)) // ','.(expression ['as' star_target])+ && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22969,7 +22951,7 @@ invalid_with_stmt_indent_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt_indent[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' ':' NEWLINE !INDENT")); - asdl_seq * _gather_214_var; + asdl_seq * _gather_212_var; Token * _literal; Token * _literal_1; Token * _literal_2; @@ -22986,7 +22968,7 @@ invalid_with_stmt_indent_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_214_var = _gather_214_rule(p)) // ','.(expressions ['as' star_target])+ + (_gather_212_var = _gather_212_rule(p)) // ','.(expressions ['as' star_target])+ && (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -23083,7 +23065,7 @@ invalid_try_stmt_rule(Parser *p) && (block_var = block_rule(p)) // block && - _PyPegen_lookahead(0, _tmp_216_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_214_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_try_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'try' ':' block !('except' | 'finally')")); @@ -23108,8 +23090,8 @@ invalid_try_stmt_rule(Parser *p) Token * _keyword; Token * _literal; Token * _literal_1; - asdl_seq * _loop0_217_var; - asdl_seq * _loop1_218_var; + asdl_seq * _loop0_215_var; + asdl_seq * _loop1_216_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; @@ -23120,9 +23102,9 @@ invalid_try_stmt_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_217_var = _loop0_217_rule(p)) // block* + (_loop0_215_var = _loop0_215_rule(p)) // block* && - (_loop1_218_var = _loop1_218_rule(p)) // except_block+ + (_loop1_216_var = _loop1_216_rule(p)) // except_block+ && (a = _PyPegen_expect_token(p, 657)) // token='except' && @@ -23130,7 +23112,7 @@ invalid_try_stmt_rule(Parser *p) && (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_219_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var = _tmp_217_rule(p), !p->error_indicator) // ['as' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' ) @@ -23157,8 +23139,8 @@ invalid_try_stmt_rule(Parser *p) Token * _keyword; Token * _literal; Token * _literal_1; - asdl_seq * _loop0_220_var; - asdl_seq * _loop1_221_var; + asdl_seq * _loop0_218_var; + asdl_seq * _loop1_219_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; @@ -23167,13 +23149,13 @@ invalid_try_stmt_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_220_var = _loop0_220_rule(p)) // block* + (_loop0_218_var = _loop0_218_rule(p)) // block* && - (_loop1_221_var = _loop1_221_rule(p)) // except_star_block+ + (_loop1_219_var = _loop1_219_rule(p)) // except_star_block+ && (a = _PyPegen_expect_token(p, 657)) // token='except' && - (_opt_var = _tmp_222_rule(p), !p->error_indicator) // [expression ['as' NAME]] + (_opt_var = _tmp_220_rule(p), !p->error_indicator) // [expression ['as' NAME]] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' ) @@ -23240,7 +23222,7 @@ invalid_except_stmt_rule(Parser *p) && (expressions_var = expressions_rule(p)) // expressions && - (_opt_var_1 = _tmp_223_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var_1 = _tmp_221_rule(p), !p->error_indicator) // ['as' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' ) @@ -23278,7 +23260,7 @@ invalid_except_stmt_rule(Parser *p) && (expression_var = expression_rule(p)) // expression && - (_opt_var_1 = _tmp_224_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var_1 = _tmp_222_rule(p), !p->error_indicator) // ['as' NAME] && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -23330,14 +23312,14 @@ invalid_except_stmt_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_except_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except' '*' (NEWLINE | ':')")); Token * _literal; - void *_tmp_225_var; + void *_tmp_223_var; Token * a; if ( (a = _PyPegen_expect_token(p, 657)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_225_var = _tmp_225_rule(p)) // NEWLINE | ':' + (_tmp_223_var = _tmp_223_rule(p)) // NEWLINE | ':' ) { D(fprintf(stderr, "%*c+ invalid_except_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' '*' (NEWLINE | ':')")); @@ -23442,7 +23424,7 @@ invalid_except_stmt_indent_rule(Parser *p) && (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_226_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var = _tmp_224_rule(p), !p->error_indicator) // ['as' NAME] && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23536,7 +23518,7 @@ invalid_except_star_stmt_indent_rule(Parser *p) && (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_227_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var = _tmp_225_rule(p), !p->error_indicator) // ['as' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23900,7 +23882,7 @@ invalid_class_argument_pattern_rule(Parser *p) asdl_pattern_seq* a; asdl_seq* keyword_patterns_var; if ( - (_opt_var = _tmp_228_rule(p), !p->error_indicator) // [positional_patterns ','] + (_opt_var = _tmp_226_rule(p), !p->error_indicator) // [positional_patterns ','] && (keyword_patterns_var = keyword_patterns_rule(p)) // keyword_patterns && @@ -24392,7 +24374,7 @@ invalid_def_raw_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' && - (_opt_var_3 = _tmp_229_rule(p), !p->error_indicator) // ['->' expression] + (_opt_var_3 = _tmp_227_rule(p), !p->error_indicator) // ['->' expression] && (_literal_2 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -24455,7 +24437,7 @@ invalid_class_def_raw_rule(Parser *p) && (_opt_var = type_params_rule(p), !p->error_indicator) // type_params? && - (_opt_var_1 = _tmp_230_rule(p), !p->error_indicator) // ['(' arguments? ')'] + (_opt_var_1 = _tmp_228_rule(p), !p->error_indicator) // ['(' arguments? ')'] && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -24494,7 +24476,7 @@ invalid_class_def_raw_rule(Parser *p) && (_opt_var = type_params_rule(p), !p->error_indicator) // type_params? && - (_opt_var_1 = _tmp_231_rule(p), !p->error_indicator) // ['(' arguments? ')'] + (_opt_var_1 = _tmp_229_rule(p), !p->error_indicator) // ['(' arguments? ')'] && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -24544,11 +24526,11 @@ invalid_double_starred_kvpairs_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair")); - asdl_seq * _gather_232_var; + asdl_seq * _gather_230_var; Token * _literal; void *invalid_kvpair_var; if ( - (_gather_232_var = _gather_232_rule(p)) // ','.double_starred_kvpair+ + (_gather_230_var = _gather_230_rule(p)) // ','.double_starred_kvpair+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && @@ -24556,7 +24538,7 @@ invalid_double_starred_kvpairs_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair")); - _res = _PyPegen_dummy_name(p, _gather_232_var, _literal, invalid_kvpair_var); + _res = _PyPegen_dummy_name(p, _gather_230_var, _literal, invalid_kvpair_var); goto done; } p->mark = _mark; @@ -24609,7 +24591,7 @@ invalid_double_starred_kvpairs_rule(Parser *p) && (a = _PyPegen_expect_token(p, 11)) // token=':' && - _PyPegen_lookahead(1, _tmp_234_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_232_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); @@ -24719,7 +24701,7 @@ invalid_kvpair_rule(Parser *p) && (a = _PyPegen_expect_token(p, 11)) // token=':' && - _PyPegen_lookahead(1, _tmp_235_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_233_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); @@ -24798,13 +24780,13 @@ invalid_starred_expression_rule(Parser *p) // | '{' '!' // | '{' ':' // | '{' '}' -// | '{' !(yield_expr | star_expressions) -// | '{' (yield_expr | star_expressions) !('=' | '!' | ':' | '}') -// | '{' (yield_expr | star_expressions) '=' !('!' | ':' | '}') -// | '{' (yield_expr | star_expressions) '='? invalid_conversion_character -// | '{' (yield_expr | star_expressions) '='? ['!' NAME] !(':' | '}') -// | '{' (yield_expr | star_expressions) '='? ['!' NAME] ':' fstring_format_spec* !'}' -// | '{' (yield_expr | star_expressions) '='? ['!' NAME] !'}' +// | '{' !annotated_rhs +// | '{' annotated_rhs !('=' | '!' | ':' | '}') +// | '{' annotated_rhs '=' !('!' | ':' | '}') +// | '{' annotated_rhs '='? invalid_conversion_character +// | '{' annotated_rhs '='? ['!' NAME] !(':' | '}') +// | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' +// | '{' annotated_rhs '='? ['!' NAME] !'}' static void * invalid_replacement_field_rule(Parser *p) { @@ -24925,20 +24907,20 @@ invalid_replacement_field_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '}'")); } - { // '{' !(yield_expr | star_expressions) + { // '{' !annotated_rhs if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' !(yield_expr | star_expressions)")); + D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - _PyPegen_lookahead(0, _tmp_236_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) annotated_rhs_rule, p) ) { - D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' !(yield_expr | star_expressions)")); + D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs")); _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting a valid expression after '{'" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24949,25 +24931,25 @@ invalid_replacement_field_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' !(yield_expr | star_expressions)")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' !annotated_rhs")); } - { // '{' (yield_expr | star_expressions) !('=' | '!' | ':' | '}') + { // '{' annotated_rhs !('=' | '!' | ':' | '}') if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) !('=' | '!' | ':' | '}')")); + D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')")); Token * _literal; - void *_tmp_237_var; + expr_ty annotated_rhs_var; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (_tmp_237_var = _tmp_237_rule(p)) // yield_expr | star_expressions + (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && - _PyPegen_lookahead(0, _tmp_238_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_234_rule, p) ) { - D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) !('=' | '!' | ':' | '}')")); + D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '=', or '!', or ':', or '}'" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24978,28 +24960,28 @@ invalid_replacement_field_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' (yield_expr | star_expressions) !('=' | '!' | ':' | '}')")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')")); } - { // '{' (yield_expr | star_expressions) '=' !('!' | ':' | '}') + { // '{' annotated_rhs '=' !('!' | ':' | '}') if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '=' !('!' | ':' | '}')")); + D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')")); Token * _literal; Token * _literal_1; - void *_tmp_239_var; + expr_ty annotated_rhs_var; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (_tmp_239_var = _tmp_239_rule(p)) // yield_expr | star_expressions + (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && (_literal_1 = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(0, _tmp_240_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_235_rule, p) ) { - D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '=' !('!' | ':' | '}')")); + D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '!', or ':', or '}'" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -25010,62 +24992,62 @@ invalid_replacement_field_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' (yield_expr | star_expressions) '=' !('!' | ':' | '}')")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')")); } - { // '{' (yield_expr | star_expressions) '='? invalid_conversion_character + { // '{' annotated_rhs '='? invalid_conversion_character if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? invalid_conversion_character")); + D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? invalid_conversion_character")); Token * _literal; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_241_var; + expr_ty annotated_rhs_var; void *invalid_conversion_character_var; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (_tmp_241_var = _tmp_241_rule(p)) // yield_expr | star_expressions + (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && (invalid_conversion_character_var = invalid_conversion_character_rule(p)) // invalid_conversion_character ) { - D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? invalid_conversion_character")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_241_var, _opt_var, invalid_conversion_character_var); + D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? invalid_conversion_character")); + _res = _PyPegen_dummy_name(p, _literal, annotated_rhs_var, _opt_var, invalid_conversion_character_var); goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' (yield_expr | star_expressions) '='? invalid_conversion_character")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? invalid_conversion_character")); } - { // '{' (yield_expr | star_expressions) '='? ['!' NAME] !(':' | '}') + { // '{' annotated_rhs '='? ['!' NAME] !(':' | '}') if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] !(':' | '}')")); + D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')")); Token * _literal; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings void *_opt_var_1; UNUSED(_opt_var_1); // Silence compiler warnings - void *_tmp_242_var; + expr_ty annotated_rhs_var; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (_tmp_242_var = _tmp_242_rule(p)) // yield_expr | star_expressions + (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_243_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_236_rule(p), !p->error_indicator) // ['!' NAME] && - _PyPegen_lookahead(0, _tmp_244_rule, p) + _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_237_rule, p) ) { - D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] !(':' | '}')")); + D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting ':' or '}'" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -25076,39 +25058,39 @@ invalid_replacement_field_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] !(':' | '}')")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')")); } - { // '{' (yield_expr | star_expressions) '='? ['!' NAME] ':' fstring_format_spec* !'}' + { // '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] ':' fstring_format_spec* !'}'")); + D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'")); Token * _literal; Token * _literal_1; - asdl_seq * _loop0_247_var; + asdl_seq * _loop0_239_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings void *_opt_var_1; UNUSED(_opt_var_1); // Silence compiler warnings - void *_tmp_245_var; + expr_ty annotated_rhs_var; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (_tmp_245_var = _tmp_245_rule(p)) // yield_expr | star_expressions + (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_246_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_238_rule(p), !p->error_indicator) // ['!' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_247_var = _loop0_247_rule(p)) // fstring_format_spec* + (_loop0_239_var = _loop0_239_rule(p)) // fstring_format_spec* && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26) // token='}' ) { - D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] ':' fstring_format_spec* !'}'")); + D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '}', or format specs" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -25119,33 +25101,33 @@ invalid_replacement_field_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] ':' fstring_format_spec* !'}'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'")); } - { // '{' (yield_expr | star_expressions) '='? ['!' NAME] !'}' + { // '{' annotated_rhs '='? ['!' NAME] !'}' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] !'}'")); + D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'")); Token * _literal; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings void *_opt_var_1; UNUSED(_opt_var_1); // Silence compiler warnings - void *_tmp_248_var; + expr_ty annotated_rhs_var; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (_tmp_248_var = _tmp_248_rule(p)) // yield_expr | star_expressions + (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_249_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_240_rule(p), !p->error_indicator) // ['!' NAME] && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26) // token='}' ) { - D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] !'}'")); + D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '}'" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -25156,7 +25138,7 @@ invalid_replacement_field_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' (yield_expr | star_expressions) '='? ['!' NAME] !'}'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'")); } _res = NULL; done: @@ -25187,7 +25169,7 @@ invalid_conversion_character_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' && - _PyPegen_lookahead(1, _tmp_250_rule, p) + _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_241_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')")); @@ -25254,14 +25236,14 @@ invalid_arithmetic_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_arithmetic[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "sum ('+' | '-' | '*' | '/' | '%' | '//' | '@') 'not' inversion")); - void *_tmp_251_var; + void *_tmp_242_var; Token * a; expr_ty b; expr_ty sum_var; if ( (sum_var = sum_rule(p)) // sum && - (_tmp_251_var = _tmp_251_rule(p)) // '+' | '-' | '*' | '/' | '%' | '//' | '@' + (_tmp_242_var = _tmp_242_rule(p)) // '+' | '-' | '*' | '/' | '%' | '//' | '@' && (a = _PyPegen_expect_token(p, 679)) // token='not' && @@ -25306,11 +25288,11 @@ invalid_factor_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_factor[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('+' | '-' | '~') 'not' factor")); - void *_tmp_252_var; + void *_tmp_243_var; Token * a; expr_ty b; if ( - (_tmp_252_var = _tmp_252_rule(p)) // '+' | '-' | '~' + (_tmp_243_var = _tmp_243_rule(p)) // '+' | '-' | '~' && (a = _PyPegen_expect_token(p, 679)) // token='not' && @@ -26151,12 +26133,12 @@ _loop1_14_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_14[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_253_var; + void *_tmp_244_var; while ( - (_tmp_253_var = _tmp_253_rule(p)) // star_targets '=' + (_tmp_244_var = _tmp_244_rule(p)) // star_targets '=' ) { - _res = _tmp_253_var; + _res = _tmp_244_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -26720,12 +26702,12 @@ _loop0_24_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_24[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_254_var; + void *_tmp_245_var; while ( - (_tmp_254_var = _tmp_254_rule(p)) // '.' | '...' + (_tmp_245_var = _tmp_245_rule(p)) // '.' | '...' ) { - _res = _tmp_254_var; + _res = _tmp_245_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -26787,12 +26769,12 @@ _loop1_25_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_25[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_255_var; + void *_tmp_246_var; while ( - (_tmp_255_var = _tmp_255_rule(p)) // '.' | '...' + (_tmp_246_var = _tmp_246_rule(p)) // '.' | '...' ) { - _res = _tmp_255_var; + _res = _tmp_246_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -27185,12 +27167,12 @@ _loop1_32_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_32[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('@' named_expression NEWLINE)")); - void *_tmp_256_var; + void *_tmp_247_var; while ( - (_tmp_256_var = _tmp_256_rule(p)) // '@' named_expression NEWLINE + (_tmp_247_var = _tmp_247_rule(p)) // '@' named_expression NEWLINE ) { - _res = _tmp_256_var; + _res = _tmp_247_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -30315,12 +30297,12 @@ _loop1_82_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_82[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' expression)")); - void *_tmp_257_var; + void *_tmp_248_var; while ( - (_tmp_257_var = _tmp_257_rule(p)) // ',' expression + (_tmp_248_var = _tmp_248_rule(p)) // ',' expression ) { - _res = _tmp_257_var; + _res = _tmp_248_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -30387,12 +30369,12 @@ _loop1_83_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_83[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)")); - void *_tmp_258_var; + void *_tmp_249_var; while ( - (_tmp_258_var = _tmp_258_rule(p)) // ',' star_expression + (_tmp_249_var = _tmp_249_rule(p)) // ',' star_expression ) { - _res = _tmp_258_var; + _res = _tmp_249_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -30576,12 +30558,12 @@ _loop1_86_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)")); - void *_tmp_259_var; + void *_tmp_250_var; while ( - (_tmp_259_var = _tmp_259_rule(p)) // 'or' conjunction + (_tmp_250_var = _tmp_250_rule(p)) // 'or' conjunction ) { - _res = _tmp_259_var; + _res = _tmp_250_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -30648,12 +30630,12 @@ _loop1_87_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)")); - void *_tmp_260_var; + void *_tmp_251_var; while ( - (_tmp_260_var = _tmp_260_rule(p)) // 'and' inversion + (_tmp_251_var = _tmp_251_rule(p)) // 'and' inversion ) { - _res = _tmp_260_var; + _res = _tmp_251_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -30840,7 +30822,7 @@ _loop0_91_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_261_rule(p)) // slice | starred_expression + (elem = _tmp_252_rule(p)) // slice | starred_expression ) { _res = elem; @@ -30905,7 +30887,7 @@ _gather_90_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_261_rule(p)) // slice | starred_expression + (elem = _tmp_252_rule(p)) // slice | starred_expression && (seq = _loop0_91_rule(p)) // _loop0_91 ) @@ -32285,66 +32267,9 @@ _loop1_111_rule(Parser *p) return _seq; } -// _tmp_112: yield_expr | star_expressions -static void * -_tmp_112_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_112[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_112[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_112[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_112[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _loop0_113: fstring_format_spec +// _loop0_112: fstring_format_spec static asdl_seq * -_loop0_113_rule(Parser *p) +_loop0_112_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32369,7 +32294,7 @@ _loop0_113_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_format_spec")); + D(fprintf(stderr, "%*c> _loop0_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_format_spec")); expr_ty fstring_format_spec_var; while ( (fstring_format_spec_var = fstring_format_spec_rule(p)) // fstring_format_spec @@ -32392,7 +32317,7 @@ _loop0_113_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_113[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_112[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring_format_spec")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32409,9 +32334,9 @@ _loop0_113_rule(Parser *p) return _seq; } -// _loop0_114: fstring_middle +// _loop0_113: fstring_middle static asdl_seq * -_loop0_114_rule(Parser *p) +_loop0_113_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32436,7 +32361,7 @@ _loop0_114_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_middle")); + D(fprintf(stderr, "%*c> _loop0_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_middle")); expr_ty fstring_middle_var; while ( (fstring_middle_var = fstring_middle_rule(p)) // fstring_middle @@ -32459,7 +32384,7 @@ _loop0_114_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_114[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_113[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring_middle")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32476,9 +32401,9 @@ _loop0_114_rule(Parser *p) return _seq; } -// _loop1_115: (fstring | string) +// _loop1_114: (fstring | string) static asdl_seq * -_loop1_115_rule(Parser *p) +_loop1_114_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32503,13 +32428,13 @@ _loop1_115_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(fstring | string)")); - void *_tmp_262_var; + D(fprintf(stderr, "%*c> _loop1_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(fstring | string)")); + void *_tmp_253_var; while ( - (_tmp_262_var = _tmp_262_rule(p)) // fstring | string + (_tmp_253_var = _tmp_253_rule(p)) // fstring | string ) { - _res = _tmp_262_var; + _res = _tmp_253_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -32526,7 +32451,7 @@ _loop1_115_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_115[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_114[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(fstring | string)")); } if (_n == 0 || p->error_indicator) { @@ -32548,9 +32473,9 @@ _loop1_115_rule(Parser *p) return _seq; } -// _tmp_116: star_named_expression ',' star_named_expressions? +// _tmp_115: star_named_expression ',' star_named_expressions? static void * -_tmp_116_rule(Parser *p) +_tmp_115_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32566,7 +32491,7 @@ _tmp_116_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?")); + D(fprintf(stderr, "%*c> _tmp_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?")); Token * _literal; expr_ty y; void *z; @@ -32578,7 +32503,7 @@ _tmp_116_rule(Parser *p) (z = star_named_expressions_rule(p), !p->error_indicator) // star_named_expressions? ) { - D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?")); + D(fprintf(stderr, "%*c+ _tmp_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?")); _res = _PyPegen_seq_insert_in_front ( p , y , z ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -32588,7 +32513,7 @@ _tmp_116_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_115[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression ',' star_named_expressions?")); } _res = NULL; @@ -32597,9 +32522,9 @@ _tmp_116_rule(Parser *p) return _res; } -// _loop0_118: ',' double_starred_kvpair +// _loop0_117: ',' double_starred_kvpair static asdl_seq * -_loop0_118_rule(Parser *p) +_loop0_117_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32624,7 +32549,7 @@ _loop0_118_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair")); + D(fprintf(stderr, "%*c> _loop0_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair")); Token * _literal; KeyValuePair* elem; while ( @@ -32656,7 +32581,7 @@ _loop0_118_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_118[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_117[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' double_starred_kvpair")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32673,9 +32598,9 @@ _loop0_118_rule(Parser *p) return _seq; } -// _gather_117: double_starred_kvpair _loop0_118 +// _gather_116: double_starred_kvpair _loop0_117 static asdl_seq * -_gather_117_rule(Parser *p) +_gather_116_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32686,27 +32611,27 @@ _gather_117_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // double_starred_kvpair _loop0_118 + { // double_starred_kvpair _loop0_117 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_118")); + D(fprintf(stderr, "%*c> _gather_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_117")); KeyValuePair* elem; asdl_seq * seq; if ( (elem = double_starred_kvpair_rule(p)) // double_starred_kvpair && - (seq = _loop0_118_rule(p)) // _loop0_118 + (seq = _loop0_117_rule(p)) // _loop0_117 ) { - D(fprintf(stderr, "%*c+ _gather_117[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_118")); + D(fprintf(stderr, "%*c+ _gather_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_117")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_117[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_118")); + D(fprintf(stderr, "%*c%s _gather_116[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_117")); } _res = NULL; done: @@ -32714,9 +32639,9 @@ _gather_117_rule(Parser *p) return _res; } -// _loop1_119: for_if_clause +// _loop1_118: for_if_clause static asdl_seq * -_loop1_119_rule(Parser *p) +_loop1_118_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32741,7 +32666,7 @@ _loop1_119_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause")); + D(fprintf(stderr, "%*c> _loop1_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause")); comprehension_ty for_if_clause_var; while ( (for_if_clause_var = for_if_clause_rule(p)) // for_if_clause @@ -32764,7 +32689,7 @@ _loop1_119_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_119[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_118[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "for_if_clause")); } if (_n == 0 || p->error_indicator) { @@ -32786,9 +32711,9 @@ _loop1_119_rule(Parser *p) return _seq; } -// _loop0_120: ('if' disjunction) +// _loop0_119: ('if' disjunction) static asdl_seq * -_loop0_120_rule(Parser *p) +_loop0_119_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32813,13 +32738,13 @@ _loop0_120_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_263_var; + D(fprintf(stderr, "%*c> _loop0_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); + void *_tmp_254_var; while ( - (_tmp_263_var = _tmp_263_rule(p)) // 'if' disjunction + (_tmp_254_var = _tmp_254_rule(p)) // 'if' disjunction ) { - _res = _tmp_263_var; + _res = _tmp_254_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -32836,7 +32761,7 @@ _loop0_120_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_120[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_119[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('if' disjunction)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32853,9 +32778,9 @@ _loop0_120_rule(Parser *p) return _seq; } -// _loop0_121: ('if' disjunction) +// _loop0_120: ('if' disjunction) static asdl_seq * -_loop0_121_rule(Parser *p) +_loop0_120_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32880,13 +32805,13 @@ _loop0_121_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_264_var; + D(fprintf(stderr, "%*c> _loop0_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); + void *_tmp_255_var; while ( - (_tmp_264_var = _tmp_264_rule(p)) // 'if' disjunction + (_tmp_255_var = _tmp_255_rule(p)) // 'if' disjunction ) { - _res = _tmp_264_var; + _res = _tmp_255_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -32903,7 +32828,7 @@ _loop0_121_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_121[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_120[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('if' disjunction)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32920,9 +32845,9 @@ _loop0_121_rule(Parser *p) return _seq; } -// _tmp_122: bitwise_or ((',' bitwise_or))* ','? +// _tmp_121: bitwise_or ((',' bitwise_or))* ','? static void * -_tmp_122_rule(Parser *p) +_tmp_121_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32938,25 +32863,25 @@ _tmp_122_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); - asdl_seq * _loop0_265_var; + D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); + asdl_seq * _loop0_256_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty bitwise_or_var; if ( (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or && - (_loop0_265_var = _loop0_265_rule(p)) // ((',' bitwise_or))* + (_loop0_256_var = _loop0_256_rule(p)) // ((',' bitwise_or))* && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { - D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); - _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_265_var, _opt_var); + D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); + _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_256_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); } _res = NULL; @@ -32965,9 +32890,9 @@ _tmp_122_rule(Parser *p) return _res; } -// _tmp_123: assignment_expression | expression !':=' +// _tmp_122: assignment_expression | expression !':=' static void * -_tmp_123_rule(Parser *p) +_tmp_122_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -32983,18 +32908,18 @@ _tmp_123_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); expr_ty assignment_expression_var; if ( (assignment_expression_var = assignment_expression_rule(p)) // assignment_expression ) { - D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); _res = assignment_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "assignment_expression")); } { // expression !':=' @@ -33002,7 +32927,7 @@ _tmp_123_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression @@ -33010,12 +32935,12 @@ _tmp_123_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 53) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); _res = expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); } _res = NULL; @@ -33024,9 +32949,9 @@ _tmp_123_rule(Parser *p) return _res; } -// _loop0_125: ',' (starred_expression | (assignment_expression | expression !':=') !'=') +// _loop0_124: ',' (starred_expression | (assignment_expression | expression !':=') !'=') static asdl_seq * -_loop0_125_rule(Parser *p) +_loop0_124_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33051,13 +32976,13 @@ _loop0_125_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); + D(fprintf(stderr, "%*c> _loop0_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_266_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_257_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' ) { _res = elem; @@ -33083,7 +33008,7 @@ _loop0_125_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_125[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_124[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33100,10 +33025,10 @@ _loop0_125_rule(Parser *p) return _seq; } -// _gather_124: -// | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_125 +// _gather_123: +// | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_124 static asdl_seq * -_gather_124_rule(Parser *p) +_gather_123_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33114,27 +33039,27 @@ _gather_124_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_125 + { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_124 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_125")); + D(fprintf(stderr, "%*c> _gather_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_124")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_266_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_257_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' && - (seq = _loop0_125_rule(p)) // _loop0_125 + (seq = _loop0_124_rule(p)) // _loop0_124 ) { - D(fprintf(stderr, "%*c+ _gather_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_125")); + D(fprintf(stderr, "%*c+ _gather_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_124")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_124[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_125")); + D(fprintf(stderr, "%*c%s _gather_123[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_124")); } _res = NULL; done: @@ -33142,9 +33067,9 @@ _gather_124_rule(Parser *p) return _res; } -// _tmp_126: ',' kwargs +// _tmp_125: ',' kwargs static void * -_tmp_126_rule(Parser *p) +_tmp_125_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33160,7 +33085,7 @@ _tmp_126_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwargs")); + D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwargs")); Token * _literal; asdl_seq* k; if ( @@ -33169,7 +33094,7 @@ _tmp_126_rule(Parser *p) (k = kwargs_rule(p)) // kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' kwargs")); + D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' kwargs")); _res = k; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -33179,7 +33104,7 @@ _tmp_126_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwargs")); } _res = NULL; @@ -33188,9 +33113,9 @@ _tmp_126_rule(Parser *p) return _res; } -// _loop0_128: ',' kwarg_or_starred +// _loop0_127: ',' kwarg_or_starred static asdl_seq * -_loop0_128_rule(Parser *p) +_loop0_127_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33215,7 +33140,7 @@ _loop0_128_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred")); + D(fprintf(stderr, "%*c> _loop0_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred")); Token * _literal; KeywordOrStarred* elem; while ( @@ -33247,7 +33172,7 @@ _loop0_128_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_128[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_127[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_starred")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33264,9 +33189,9 @@ _loop0_128_rule(Parser *p) return _seq; } -// _gather_127: kwarg_or_starred _loop0_128 +// _gather_126: kwarg_or_starred _loop0_127 static asdl_seq * -_gather_127_rule(Parser *p) +_gather_126_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33277,27 +33202,27 @@ _gather_127_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // kwarg_or_starred _loop0_128 + { // kwarg_or_starred _loop0_127 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_128")); + D(fprintf(stderr, "%*c> _gather_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_127")); KeywordOrStarred* elem; asdl_seq * seq; if ( (elem = kwarg_or_starred_rule(p)) // kwarg_or_starred && - (seq = _loop0_128_rule(p)) // _loop0_128 + (seq = _loop0_127_rule(p)) // _loop0_127 ) { - D(fprintf(stderr, "%*c+ _gather_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_128")); + D(fprintf(stderr, "%*c+ _gather_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_127")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_127[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_128")); + D(fprintf(stderr, "%*c%s _gather_126[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_127")); } _res = NULL; done: @@ -33305,9 +33230,9 @@ _gather_127_rule(Parser *p) return _res; } -// _loop0_130: ',' kwarg_or_double_starred +// _loop0_129: ',' kwarg_or_double_starred static asdl_seq * -_loop0_130_rule(Parser *p) +_loop0_129_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33332,7 +33257,7 @@ _loop0_130_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred")); + D(fprintf(stderr, "%*c> _loop0_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred")); Token * _literal; KeywordOrStarred* elem; while ( @@ -33364,7 +33289,7 @@ _loop0_130_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_130[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_129[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_double_starred")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33381,9 +33306,9 @@ _loop0_130_rule(Parser *p) return _seq; } -// _gather_129: kwarg_or_double_starred _loop0_130 +// _gather_128: kwarg_or_double_starred _loop0_129 static asdl_seq * -_gather_129_rule(Parser *p) +_gather_128_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33394,27 +33319,27 @@ _gather_129_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // kwarg_or_double_starred _loop0_130 + { // kwarg_or_double_starred _loop0_129 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_130")); + D(fprintf(stderr, "%*c> _gather_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_129")); KeywordOrStarred* elem; asdl_seq * seq; if ( (elem = kwarg_or_double_starred_rule(p)) // kwarg_or_double_starred && - (seq = _loop0_130_rule(p)) // _loop0_130 + (seq = _loop0_129_rule(p)) // _loop0_129 ) { - D(fprintf(stderr, "%*c+ _gather_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_130")); + D(fprintf(stderr, "%*c+ _gather_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_129")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_129[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_130")); + D(fprintf(stderr, "%*c%s _gather_128[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_129")); } _res = NULL; done: @@ -33422,9 +33347,9 @@ _gather_129_rule(Parser *p) return _res; } -// _loop0_132: ',' kwarg_or_starred +// _loop0_131: ',' kwarg_or_starred static asdl_seq * -_loop0_132_rule(Parser *p) +_loop0_131_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33449,7 +33374,7 @@ _loop0_132_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred")); + D(fprintf(stderr, "%*c> _loop0_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred")); Token * _literal; KeywordOrStarred* elem; while ( @@ -33481,7 +33406,7 @@ _loop0_132_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_132[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_131[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_starred")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33498,9 +33423,9 @@ _loop0_132_rule(Parser *p) return _seq; } -// _gather_131: kwarg_or_starred _loop0_132 +// _gather_130: kwarg_or_starred _loop0_131 static asdl_seq * -_gather_131_rule(Parser *p) +_gather_130_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33511,27 +33436,27 @@ _gather_131_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // kwarg_or_starred _loop0_132 + { // kwarg_or_starred _loop0_131 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_132")); + D(fprintf(stderr, "%*c> _gather_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_131")); KeywordOrStarred* elem; asdl_seq * seq; if ( (elem = kwarg_or_starred_rule(p)) // kwarg_or_starred && - (seq = _loop0_132_rule(p)) // _loop0_132 + (seq = _loop0_131_rule(p)) // _loop0_131 ) { - D(fprintf(stderr, "%*c+ _gather_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_132")); + D(fprintf(stderr, "%*c+ _gather_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_131")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_131[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_132")); + D(fprintf(stderr, "%*c%s _gather_130[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_131")); } _res = NULL; done: @@ -33539,9 +33464,9 @@ _gather_131_rule(Parser *p) return _res; } -// _loop0_134: ',' kwarg_or_double_starred +// _loop0_133: ',' kwarg_or_double_starred static asdl_seq * -_loop0_134_rule(Parser *p) +_loop0_133_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33566,7 +33491,7 @@ _loop0_134_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred")); + D(fprintf(stderr, "%*c> _loop0_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred")); Token * _literal; KeywordOrStarred* elem; while ( @@ -33598,7 +33523,7 @@ _loop0_134_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_134[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_133[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_double_starred")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33615,9 +33540,9 @@ _loop0_134_rule(Parser *p) return _seq; } -// _gather_133: kwarg_or_double_starred _loop0_134 +// _gather_132: kwarg_or_double_starred _loop0_133 static asdl_seq * -_gather_133_rule(Parser *p) +_gather_132_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33628,27 +33553,27 @@ _gather_133_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // kwarg_or_double_starred _loop0_134 + { // kwarg_or_double_starred _loop0_133 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_134")); + D(fprintf(stderr, "%*c> _gather_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_133")); KeywordOrStarred* elem; asdl_seq * seq; if ( (elem = kwarg_or_double_starred_rule(p)) // kwarg_or_double_starred && - (seq = _loop0_134_rule(p)) // _loop0_134 + (seq = _loop0_133_rule(p)) // _loop0_133 ) { - D(fprintf(stderr, "%*c+ _gather_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_134")); + D(fprintf(stderr, "%*c+ _gather_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_133")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_133[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_134")); + D(fprintf(stderr, "%*c%s _gather_132[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_133")); } _res = NULL; done: @@ -33656,9 +33581,9 @@ _gather_133_rule(Parser *p) return _res; } -// _loop0_135: (',' star_target) +// _loop0_134: (',' star_target) static asdl_seq * -_loop0_135_rule(Parser *p) +_loop0_134_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33683,13 +33608,13 @@ _loop0_135_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_267_var; + D(fprintf(stderr, "%*c> _loop0_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); + void *_tmp_258_var; while ( - (_tmp_267_var = _tmp_267_rule(p)) // ',' star_target + (_tmp_258_var = _tmp_258_rule(p)) // ',' star_target ) { - _res = _tmp_267_var; + _res = _tmp_258_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33706,7 +33631,7 @@ _loop0_135_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_135[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_134[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_target)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33723,9 +33648,9 @@ _loop0_135_rule(Parser *p) return _seq; } -// _loop0_137: ',' star_target +// _loop0_136: ',' star_target static asdl_seq * -_loop0_137_rule(Parser *p) +_loop0_136_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33750,7 +33675,7 @@ _loop0_137_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c> _loop0_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); Token * _literal; expr_ty elem; while ( @@ -33782,7 +33707,7 @@ _loop0_137_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_137[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_136[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33799,9 +33724,9 @@ _loop0_137_rule(Parser *p) return _seq; } -// _gather_136: star_target _loop0_137 +// _gather_135: star_target _loop0_136 static asdl_seq * -_gather_136_rule(Parser *p) +_gather_135_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33812,27 +33737,27 @@ _gather_136_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // star_target _loop0_137 + { // star_target _loop0_136 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target _loop0_137")); + D(fprintf(stderr, "%*c> _gather_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target _loop0_136")); expr_ty elem; asdl_seq * seq; if ( (elem = star_target_rule(p)) // star_target && - (seq = _loop0_137_rule(p)) // _loop0_137 + (seq = _loop0_136_rule(p)) // _loop0_136 ) { - D(fprintf(stderr, "%*c+ _gather_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target _loop0_137")); + D(fprintf(stderr, "%*c+ _gather_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target _loop0_136")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_136[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target _loop0_137")); + D(fprintf(stderr, "%*c%s _gather_135[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target _loop0_136")); } _res = NULL; done: @@ -33840,9 +33765,9 @@ _gather_136_rule(Parser *p) return _res; } -// _loop1_138: (',' star_target) +// _loop1_137: (',' star_target) static asdl_seq * -_loop1_138_rule(Parser *p) +_loop1_137_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33867,13 +33792,13 @@ _loop1_138_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_268_var; + D(fprintf(stderr, "%*c> _loop1_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); + void *_tmp_259_var; while ( - (_tmp_268_var = _tmp_268_rule(p)) // ',' star_target + (_tmp_259_var = _tmp_259_rule(p)) // ',' star_target ) { - _res = _tmp_268_var; + _res = _tmp_259_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33890,7 +33815,7 @@ _loop1_138_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_138[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_137[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_target)")); } if (_n == 0 || p->error_indicator) { @@ -33912,9 +33837,9 @@ _loop1_138_rule(Parser *p) return _seq; } -// _tmp_139: !'*' star_target +// _tmp_138: !'*' star_target static void * -_tmp_139_rule(Parser *p) +_tmp_138_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33930,7 +33855,7 @@ _tmp_139_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); + D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); expr_ty star_target_var; if ( _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 16) // token='*' @@ -33938,12 +33863,12 @@ _tmp_139_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); + D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); _res = star_target_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!'*' star_target")); } _res = NULL; @@ -33952,9 +33877,9 @@ _tmp_139_rule(Parser *p) return _res; } -// _loop0_141: ',' del_target +// _loop0_140: ',' del_target static asdl_seq * -_loop0_141_rule(Parser *p) +_loop0_140_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -33979,7 +33904,7 @@ _loop0_141_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target")); + D(fprintf(stderr, "%*c> _loop0_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target")); Token * _literal; expr_ty elem; while ( @@ -34011,7 +33936,7 @@ _loop0_141_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_141[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_140[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' del_target")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34028,9 +33953,9 @@ _loop0_141_rule(Parser *p) return _seq; } -// _gather_140: del_target _loop0_141 +// _gather_139: del_target _loop0_140 static asdl_seq * -_gather_140_rule(Parser *p) +_gather_139_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34041,27 +33966,27 @@ _gather_140_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // del_target _loop0_141 + { // del_target _loop0_140 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_141")); + D(fprintf(stderr, "%*c> _gather_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_140")); expr_ty elem; asdl_seq * seq; if ( (elem = del_target_rule(p)) // del_target && - (seq = _loop0_141_rule(p)) // _loop0_141 + (seq = _loop0_140_rule(p)) // _loop0_140 ) { - D(fprintf(stderr, "%*c+ _gather_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_141")); + D(fprintf(stderr, "%*c+ _gather_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_140")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_140[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_141")); + D(fprintf(stderr, "%*c%s _gather_139[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_140")); } _res = NULL; done: @@ -34069,9 +33994,9 @@ _gather_140_rule(Parser *p) return _res; } -// _loop0_143: ',' expression +// _loop0_142: ',' expression static asdl_seq * -_loop0_143_rule(Parser *p) +_loop0_142_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34096,7 +34021,7 @@ _loop0_143_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c> _loop0_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); Token * _literal; expr_ty elem; while ( @@ -34128,7 +34053,7 @@ _loop0_143_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_143[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_142[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34145,9 +34070,9 @@ _loop0_143_rule(Parser *p) return _seq; } -// _gather_142: expression _loop0_143 +// _gather_141: expression _loop0_142 static asdl_seq * -_gather_142_rule(Parser *p) +_gather_141_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34158,27 +34083,27 @@ _gather_142_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // expression _loop0_143 + { // expression _loop0_142 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_143")); + D(fprintf(stderr, "%*c> _gather_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_142")); expr_ty elem; asdl_seq * seq; if ( (elem = expression_rule(p)) // expression && - (seq = _loop0_143_rule(p)) // _loop0_143 + (seq = _loop0_142_rule(p)) // _loop0_142 ) { - D(fprintf(stderr, "%*c+ _gather_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_143")); + D(fprintf(stderr, "%*c+ _gather_141[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_142")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_142[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_143")); + D(fprintf(stderr, "%*c%s _gather_141[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_142")); } _res = NULL; done: @@ -34186,9 +34111,9 @@ _gather_142_rule(Parser *p) return _res; } -// _loop0_145: ',' expression +// _loop0_144: ',' expression static asdl_seq * -_loop0_145_rule(Parser *p) +_loop0_144_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34213,7 +34138,7 @@ _loop0_145_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c> _loop0_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); Token * _literal; expr_ty elem; while ( @@ -34245,7 +34170,7 @@ _loop0_145_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_145[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_144[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34262,9 +34187,9 @@ _loop0_145_rule(Parser *p) return _seq; } -// _gather_144: expression _loop0_145 +// _gather_143: expression _loop0_144 static asdl_seq * -_gather_144_rule(Parser *p) +_gather_143_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34275,27 +34200,27 @@ _gather_144_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // expression _loop0_145 + { // expression _loop0_144 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_145")); + D(fprintf(stderr, "%*c> _gather_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_144")); expr_ty elem; asdl_seq * seq; if ( (elem = expression_rule(p)) // expression && - (seq = _loop0_145_rule(p)) // _loop0_145 + (seq = _loop0_144_rule(p)) // _loop0_144 ) { - D(fprintf(stderr, "%*c+ _gather_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_145")); + D(fprintf(stderr, "%*c+ _gather_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_144")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_144[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_145")); + D(fprintf(stderr, "%*c%s _gather_143[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_144")); } _res = NULL; done: @@ -34303,9 +34228,9 @@ _gather_144_rule(Parser *p) return _res; } -// _loop0_147: ',' expression +// _loop0_146: ',' expression static asdl_seq * -_loop0_147_rule(Parser *p) +_loop0_146_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34330,7 +34255,7 @@ _loop0_147_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c> _loop0_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); Token * _literal; expr_ty elem; while ( @@ -34362,7 +34287,7 @@ _loop0_147_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_147[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_146[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34379,9 +34304,9 @@ _loop0_147_rule(Parser *p) return _seq; } -// _gather_146: expression _loop0_147 +// _gather_145: expression _loop0_146 static asdl_seq * -_gather_146_rule(Parser *p) +_gather_145_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34392,27 +34317,27 @@ _gather_146_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // expression _loop0_147 + { // expression _loop0_146 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_147")); + D(fprintf(stderr, "%*c> _gather_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_146")); expr_ty elem; asdl_seq * seq; if ( (elem = expression_rule(p)) // expression && - (seq = _loop0_147_rule(p)) // _loop0_147 + (seq = _loop0_146_rule(p)) // _loop0_146 ) { - D(fprintf(stderr, "%*c+ _gather_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_147")); + D(fprintf(stderr, "%*c+ _gather_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_146")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_146[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_147")); + D(fprintf(stderr, "%*c%s _gather_145[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_146")); } _res = NULL; done: @@ -34420,9 +34345,9 @@ _gather_146_rule(Parser *p) return _res; } -// _loop0_149: ',' expression +// _loop0_148: ',' expression static asdl_seq * -_loop0_149_rule(Parser *p) +_loop0_148_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34447,7 +34372,7 @@ _loop0_149_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c> _loop0_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); Token * _literal; expr_ty elem; while ( @@ -34479,7 +34404,7 @@ _loop0_149_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_149[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_148[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34496,9 +34421,9 @@ _loop0_149_rule(Parser *p) return _seq; } -// _gather_148: expression _loop0_149 +// _gather_147: expression _loop0_148 static asdl_seq * -_gather_148_rule(Parser *p) +_gather_147_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34509,27 +34434,27 @@ _gather_148_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // expression _loop0_149 + { // expression _loop0_148 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_149")); + D(fprintf(stderr, "%*c> _gather_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_148")); expr_ty elem; asdl_seq * seq; if ( (elem = expression_rule(p)) // expression && - (seq = _loop0_149_rule(p)) // _loop0_149 + (seq = _loop0_148_rule(p)) // _loop0_148 ) { - D(fprintf(stderr, "%*c+ _gather_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_149")); + D(fprintf(stderr, "%*c+ _gather_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_148")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_148[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_149")); + D(fprintf(stderr, "%*c%s _gather_147[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_148")); } _res = NULL; done: @@ -34537,9 +34462,9 @@ _gather_148_rule(Parser *p) return _res; } -// _tmp_150: NEWLINE INDENT +// _tmp_149: NEWLINE INDENT static void * -_tmp_150_rule(Parser *p) +_tmp_149_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34555,7 +34480,7 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT")); + D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT")); Token * indent_var; Token * newline_var; if ( @@ -34564,12 +34489,12 @@ _tmp_150_rule(Parser *p) (indent_var = _PyPegen_expect_token(p, INDENT)) // token='INDENT' ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT")); + D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT")); _res = _PyPegen_dummy_name(p, newline_var, indent_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE INDENT")); } _res = NULL; @@ -34578,11 +34503,11 @@ _tmp_150_rule(Parser *p) return _res; } -// _tmp_151: +// _tmp_150: // | (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) // | kwargs static void * -_tmp_151_rule(Parser *p) +_tmp_150_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34598,18 +34523,18 @@ _tmp_151_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); - void *_tmp_269_var; + D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); + void *_tmp_260_var; if ( - (_tmp_269_var = _tmp_269_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs + (_tmp_260_var = _tmp_260_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); - _res = _tmp_269_var; + D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); + _res = _tmp_260_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); } { // kwargs @@ -34617,18 +34542,18 @@ _tmp_151_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwargs")); + D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwargs")); asdl_seq* kwargs_var; if ( (kwargs_var = kwargs_rule(p)) // kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwargs")); + D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwargs")); _res = kwargs_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwargs")); } _res = NULL; @@ -34637,9 +34562,9 @@ _tmp_151_rule(Parser *p) return _res; } -// _loop0_153: ',' (starred_expression !'=') +// _loop0_152: ',' (starred_expression !'=') static asdl_seq * -_loop0_153_rule(Parser *p) +_loop0_152_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34664,13 +34589,13 @@ _loop0_153_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression !'=')")); + D(fprintf(stderr, "%*c> _loop0_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression !'=')")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_270_rule(p)) // starred_expression !'=' + (elem = _tmp_261_rule(p)) // starred_expression !'=' ) { _res = elem; @@ -34696,7 +34621,7 @@ _loop0_153_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_153[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression !'=')")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34713,9 +34638,9 @@ _loop0_153_rule(Parser *p) return _seq; } -// _gather_152: (starred_expression !'=') _loop0_153 +// _gather_151: (starred_expression !'=') _loop0_152 static asdl_seq * -_gather_152_rule(Parser *p) +_gather_151_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34726,27 +34651,27 @@ _gather_152_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (starred_expression !'=') _loop0_153 + { // (starred_expression !'=') _loop0_152 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_153")); + D(fprintf(stderr, "%*c> _gather_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_152")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_270_rule(p)) // starred_expression !'=' + (elem = _tmp_261_rule(p)) // starred_expression !'=' && - (seq = _loop0_153_rule(p)) // _loop0_153 + (seq = _loop0_152_rule(p)) // _loop0_152 ) { - D(fprintf(stderr, "%*c+ _gather_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_153")); + D(fprintf(stderr, "%*c+ _gather_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_152")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_152[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression !'=') _loop0_153")); + D(fprintf(stderr, "%*c%s _gather_151[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression !'=') _loop0_152")); } _res = NULL; done: @@ -34754,9 +34679,9 @@ _gather_152_rule(Parser *p) return _res; } -// _tmp_154: args | expression for_if_clauses +// _tmp_153: args | expression for_if_clauses static void * -_tmp_154_rule(Parser *p) +_tmp_153_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34772,18 +34697,18 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args")); + D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args")); expr_ty args_var; if ( (args_var = args_rule(p)) // args ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args")); + D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args")); _res = args_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args")); } { // expression for_if_clauses @@ -34791,7 +34716,7 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); + D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); expr_ty expression_var; asdl_comprehension_seq* for_if_clauses_var; if ( @@ -34800,12 +34725,12 @@ _tmp_154_rule(Parser *p) (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); + D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); _res = _PyPegen_dummy_name(p, expression_var, for_if_clauses_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses")); } _res = NULL; @@ -34814,9 +34739,9 @@ _tmp_154_rule(Parser *p) return _res; } -// _tmp_155: args ',' +// _tmp_154: args ',' static void * -_tmp_155_rule(Parser *p) +_tmp_154_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34832,7 +34757,7 @@ _tmp_155_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ','")); + D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ','")); Token * _literal; expr_ty args_var; if ( @@ -34841,12 +34766,12 @@ _tmp_155_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ','")); + D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ','")); _res = _PyPegen_dummy_name(p, args_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ','")); } _res = NULL; @@ -34855,9 +34780,9 @@ _tmp_155_rule(Parser *p) return _res; } -// _tmp_156: ',' | ')' +// _tmp_155: ',' | ')' static void * -_tmp_156_rule(Parser *p) +_tmp_155_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34873,18 +34798,18 @@ _tmp_156_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // ')' @@ -34892,18 +34817,18 @@ _tmp_156_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } _res = NULL; @@ -34912,9 +34837,9 @@ _tmp_156_rule(Parser *p) return _res; } -// _tmp_157: 'True' | 'False' | 'None' +// _tmp_156: 'True' | 'False' | 'None' static void * -_tmp_157_rule(Parser *p) +_tmp_156_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -34930,18 +34855,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 613)) // token='True' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'True'")); } { // 'False' @@ -34949,18 +34874,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 615)) // token='False' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'False'")); } { // 'None' @@ -34968,18 +34893,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 614)) // token='None' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'")); } _res = NULL; @@ -34988,9 +34913,9 @@ _tmp_157_rule(Parser *p) return _res; } -// _tmp_158: NAME '=' +// _tmp_157: NAME '=' static void * -_tmp_158_rule(Parser *p) +_tmp_157_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35006,7 +34931,7 @@ _tmp_158_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME '='")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME '='")); Token * _literal; expr_ty name_var; if ( @@ -35015,12 +34940,12 @@ _tmp_158_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '='")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '='")); _res = _PyPegen_dummy_name(p, name_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME '='")); } _res = NULL; @@ -35029,9 +34954,9 @@ _tmp_158_rule(Parser *p) return _res; } -// _tmp_159: NAME STRING | SOFT_KEYWORD +// _tmp_158: NAME STRING | SOFT_KEYWORD static void * -_tmp_159_rule(Parser *p) +_tmp_158_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35047,7 +34972,7 @@ _tmp_159_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME STRING")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME STRING")); expr_ty name_var; expr_ty string_var; if ( @@ -35056,12 +34981,12 @@ _tmp_159_rule(Parser *p) (string_var = _PyPegen_string_token(p)) // STRING ) { - D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME STRING")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME STRING")); _res = _PyPegen_dummy_name(p, name_var, string_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME STRING")); } { // SOFT_KEYWORD @@ -35069,18 +34994,18 @@ _tmp_159_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); expr_ty soft_keyword_var; if ( (soft_keyword_var = _PyPegen_soft_keyword_token(p)) // SOFT_KEYWORD ) { - D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); _res = soft_keyword_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "SOFT_KEYWORD")); } _res = NULL; @@ -35089,9 +35014,9 @@ _tmp_159_rule(Parser *p) return _res; } -// _tmp_160: 'else' | ':' +// _tmp_159: 'else' | ':' static void * -_tmp_160_rule(Parser *p) +_tmp_159_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35107,18 +35032,18 @@ _tmp_160_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); + D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 665)) // token='else' ) { - D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); + D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'else'")); } { // ':' @@ -35126,18 +35051,18 @@ _tmp_160_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } _res = NULL; @@ -35146,9 +35071,9 @@ _tmp_160_rule(Parser *p) return _res; } -// _tmp_161: '=' | ':=' +// _tmp_160: '=' | ':=' static void * -_tmp_161_rule(Parser *p) +_tmp_160_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35164,18 +35089,18 @@ _tmp_161_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); } { // ':=' @@ -35183,18 +35108,18 @@ _tmp_161_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 53)) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':='")); } _res = NULL; @@ -35203,9 +35128,9 @@ _tmp_161_rule(Parser *p) return _res; } -// _tmp_162: list | tuple | genexp | 'True' | 'None' | 'False' +// _tmp_161: list | tuple | genexp | 'True' | 'None' | 'False' static void * -_tmp_162_rule(Parser *p) +_tmp_161_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35221,18 +35146,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list")); + D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list")); expr_ty list_var; if ( (list_var = list_rule(p)) // list ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list")); + D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list")); _res = list_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "list")); } { // tuple @@ -35240,18 +35165,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple")); + D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple")); expr_ty tuple_var; if ( (tuple_var = tuple_rule(p)) // tuple ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple")); + D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple")); _res = tuple_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tuple")); } { // genexp @@ -35259,18 +35184,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp")); + D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp")); expr_ty genexp_var; if ( (genexp_var = genexp_rule(p)) // genexp ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp")); + D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp")); _res = genexp_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "genexp")); } { // 'True' @@ -35278,18 +35203,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 613)) // token='True' ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'True'")); } { // 'None' @@ -35297,18 +35222,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 614)) // token='None' ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'")); } { // 'False' @@ -35316,18 +35241,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 615)) // token='False' ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'False'")); } _res = NULL; @@ -35336,9 +35261,9 @@ _tmp_162_rule(Parser *p) return _res; } -// _tmp_163: '=' | ':=' +// _tmp_162: '=' | ':=' static void * -_tmp_163_rule(Parser *p) +_tmp_162_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35354,18 +35279,18 @@ _tmp_163_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); } { // ':=' @@ -35373,18 +35298,18 @@ _tmp_163_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 53)) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':='")); } _res = NULL; @@ -35393,9 +35318,9 @@ _tmp_163_rule(Parser *p) return _res; } -// _loop0_164: star_named_expressions +// _loop0_163: star_named_expressions static asdl_seq * -_loop0_164_rule(Parser *p) +_loop0_163_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35420,7 +35345,7 @@ _loop0_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions")); + D(fprintf(stderr, "%*c> _loop0_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions")); asdl_expr_seq* star_named_expressions_var; while ( (star_named_expressions_var = star_named_expressions_rule(p)) // star_named_expressions @@ -35443,7 +35368,7 @@ _loop0_164_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_163[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expressions")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35460,6 +35385,73 @@ _loop0_164_rule(Parser *p) return _seq; } +// _loop0_164: (star_targets '=') +static asdl_seq * +_loop0_164_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void *_res = NULL; + int _mark = p->mark; + void **_children = PyMem_Malloc(sizeof(void *)); + if (!_children) { + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + Py_ssize_t _children_capacity = 1; + Py_ssize_t _n = 0; + { // (star_targets '=') + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _loop0_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); + void *_tmp_262_var; + while ( + (_tmp_262_var = _tmp_262_rule(p)) // star_targets '=' + ) + { + _res = _tmp_262_var; + if (_n == _children_capacity) { + _children_capacity *= 2; + void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); + if (!_new_children) { + PyMem_Free(_children); + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + _children = _new_children; + } + _children[_n++] = _res; + _mark = p->mark; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _loop0_164[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')")); + } + asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); + if (!_seq) { + PyMem_Free(_children); + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + for (int i = 0; i < _n; i++) asdl_seq_SET_UNTYPED(_seq, i, _children[i]); + PyMem_Free(_children); + p->level--; + return _seq; +} + // _loop0_165: (star_targets '=') static asdl_seq * _loop0_165_rule(Parser *p) @@ -35488,12 +35480,12 @@ _loop0_165_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_271_var; + void *_tmp_263_var; while ( - (_tmp_271_var = _tmp_271_rule(p)) // star_targets '=' + (_tmp_263_var = _tmp_263_rule(p)) // star_targets '=' ) { - _res = _tmp_271_var; + _res = _tmp_263_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -35527,133 +35519,9 @@ _loop0_165_rule(Parser *p) return _seq; } -// _loop0_166: (star_targets '=') -static asdl_seq * -_loop0_166_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void *_res = NULL; - int _mark = p->mark; - void **_children = PyMem_Malloc(sizeof(void *)); - if (!_children) { - p->error_indicator = 1; - PyErr_NoMemory(); - p->level--; - return NULL; - } - Py_ssize_t _children_capacity = 1; - Py_ssize_t _n = 0; - { // (star_targets '=') - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _loop0_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_272_var; - while ( - (_tmp_272_var = _tmp_272_rule(p)) // star_targets '=' - ) - { - _res = _tmp_272_var; - if (_n == _children_capacity) { - _children_capacity *= 2; - void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); - if (!_new_children) { - PyMem_Free(_children); - p->error_indicator = 1; - PyErr_NoMemory(); - p->level--; - return NULL; - } - _children = _new_children; - } - _children[_n++] = _res; - _mark = p->mark; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_166[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')")); - } - asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); - if (!_seq) { - PyMem_Free(_children); - p->error_indicator = 1; - PyErr_NoMemory(); - p->level--; - return NULL; - } - for (int i = 0; i < _n; i++) asdl_seq_SET_UNTYPED(_seq, i, _children[i]); - PyMem_Free(_children); - p->level--; - return _seq; -} - -// _tmp_167: yield_expr | star_expressions +// _tmp_166: '[' | '(' | '{' static void * -_tmp_167_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_168: '[' | '(' | '{' -static void * -_tmp_168_rule(Parser *p) +_tmp_166_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35669,18 +35537,18 @@ _tmp_168_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' ) { - D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); } { // '(' @@ -35688,18 +35556,18 @@ _tmp_168_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('")); + D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' ) { - D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('")); + D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'('")); } { // '{' @@ -35707,18 +35575,18 @@ _tmp_168_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' ) { - D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); } _res = NULL; @@ -35727,9 +35595,9 @@ _tmp_168_rule(Parser *p) return _res; } -// _tmp_169: '[' | '{' +// _tmp_167: '[' | '{' static void * -_tmp_169_rule(Parser *p) +_tmp_167_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35745,18 +35613,18 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); } { // '{' @@ -35764,18 +35632,18 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); } _res = NULL; @@ -35784,9 +35652,9 @@ _tmp_169_rule(Parser *p) return _res; } -// _tmp_170: '[' | '{' +// _tmp_168: '[' | '{' static void * -_tmp_170_rule(Parser *p) +_tmp_168_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35802,18 +35670,18 @@ _tmp_170_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' ) { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); } { // '{' @@ -35821,18 +35689,18 @@ _tmp_170_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' ) { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); } _res = NULL; @@ -35841,9 +35709,9 @@ _tmp_170_rule(Parser *p) return _res; } -// _tmp_171: slash_no_default | slash_with_default +// _tmp_169: slash_no_default | slash_with_default static void * -_tmp_171_rule(Parser *p) +_tmp_169_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35859,18 +35727,18 @@ _tmp_171_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); asdl_arg_seq* slash_no_default_var; if ( (slash_no_default_var = slash_no_default_rule(p)) // slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); _res = slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_no_default")); } { // slash_with_default @@ -35878,18 +35746,18 @@ _tmp_171_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); SlashWithDefault* slash_with_default_var; if ( (slash_with_default_var = slash_with_default_rule(p)) // slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); _res = slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_with_default")); } _res = NULL; @@ -35898,9 +35766,9 @@ _tmp_171_rule(Parser *p) return _res; } -// _loop0_172: param_maybe_default +// _loop0_170: param_maybe_default static asdl_seq * -_loop0_172_rule(Parser *p) +_loop0_170_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35925,7 +35793,7 @@ _loop0_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -35948,7 +35816,7 @@ _loop0_172_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_170[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35965,9 +35833,9 @@ _loop0_172_rule(Parser *p) return _seq; } -// _loop0_173: param_no_default +// _loop0_171: param_no_default static asdl_seq * -_loop0_173_rule(Parser *p) +_loop0_171_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -35992,7 +35860,7 @@ _loop0_173_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _loop0_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; while ( (param_no_default_var = param_no_default_rule(p)) // param_no_default @@ -36015,7 +35883,7 @@ _loop0_173_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_173[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_171[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36032,9 +35900,9 @@ _loop0_173_rule(Parser *p) return _seq; } -// _loop0_174: param_no_default +// _loop0_172: param_no_default static asdl_seq * -_loop0_174_rule(Parser *p) +_loop0_172_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36059,7 +35927,7 @@ _loop0_174_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _loop0_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; while ( (param_no_default_var = param_no_default_rule(p)) // param_no_default @@ -36082,7 +35950,7 @@ _loop0_174_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_174[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_172[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36099,9 +35967,9 @@ _loop0_174_rule(Parser *p) return _seq; } -// _loop1_175: param_no_default +// _loop1_173: param_no_default static asdl_seq * -_loop1_175_rule(Parser *p) +_loop1_173_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36126,7 +35994,7 @@ _loop1_175_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _loop1_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; while ( (param_no_default_var = param_no_default_rule(p)) // param_no_default @@ -36149,7 +36017,7 @@ _loop1_175_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_175[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_173[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } if (_n == 0 || p->error_indicator) { @@ -36171,9 +36039,9 @@ _loop1_175_rule(Parser *p) return _seq; } -// _tmp_176: slash_no_default | slash_with_default +// _tmp_174: slash_no_default | slash_with_default static void * -_tmp_176_rule(Parser *p) +_tmp_174_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36189,18 +36057,18 @@ _tmp_176_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); asdl_arg_seq* slash_no_default_var; if ( (slash_no_default_var = slash_no_default_rule(p)) // slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); _res = slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_no_default")); } { // slash_with_default @@ -36208,18 +36076,18 @@ _tmp_176_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); SlashWithDefault* slash_with_default_var; if ( (slash_with_default_var = slash_with_default_rule(p)) // slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); _res = slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_with_default")); } _res = NULL; @@ -36228,9 +36096,9 @@ _tmp_176_rule(Parser *p) return _res; } -// _loop0_177: param_maybe_default +// _loop0_175: param_maybe_default static asdl_seq * -_loop0_177_rule(Parser *p) +_loop0_175_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36255,7 +36123,7 @@ _loop0_177_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_177[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -36278,7 +36146,7 @@ _loop0_177_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_177[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36295,9 +36163,9 @@ _loop0_177_rule(Parser *p) return _seq; } -// _tmp_178: ',' | param_no_default +// _tmp_176: ',' | param_no_default static void * -_tmp_178_rule(Parser *p) +_tmp_176_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36313,18 +36181,18 @@ _tmp_178_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_178[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_178[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_178[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // param_no_default @@ -36332,18 +36200,18 @@ _tmp_178_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_178[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; if ( (param_no_default_var = param_no_default_rule(p)) // param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_178[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); _res = param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_178[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } _res = NULL; @@ -36352,9 +36220,9 @@ _tmp_178_rule(Parser *p) return _res; } -// _loop0_179: param_maybe_default +// _loop0_177: param_maybe_default static asdl_seq * -_loop0_179_rule(Parser *p) +_loop0_177_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36379,7 +36247,7 @@ _loop0_179_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_179[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_177[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -36402,7 +36270,7 @@ _loop0_179_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_179[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_177[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36419,9 +36287,9 @@ _loop0_179_rule(Parser *p) return _seq; } -// _loop1_180: param_maybe_default +// _loop1_178: param_maybe_default static asdl_seq * -_loop1_180_rule(Parser *p) +_loop1_178_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36446,7 +36314,7 @@ _loop1_180_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_180[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop1_178[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -36469,7 +36337,7 @@ _loop1_180_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_180[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_178[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } if (_n == 0 || p->error_indicator) { @@ -36491,9 +36359,9 @@ _loop1_180_rule(Parser *p) return _seq; } -// _tmp_181: ')' | ',' +// _tmp_179: ')' | ',' static void * -_tmp_181_rule(Parser *p) +_tmp_179_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36509,18 +36377,18 @@ _tmp_181_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_181[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_179[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_181[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_179[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_181[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_179[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ',' @@ -36528,18 +36396,18 @@ _tmp_181_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_181[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_179[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_181[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_179[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_181[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_179[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -36548,9 +36416,9 @@ _tmp_181_rule(Parser *p) return _res; } -// _tmp_182: ')' | ',' (')' | '**') +// _tmp_180: ')' | ',' (')' | '**') static void * -_tmp_182_rule(Parser *p) +_tmp_180_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36566,18 +36434,18 @@ _tmp_182_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_182[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_180[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_182[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_180[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_182[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_180[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ',' (')' | '**') @@ -36585,21 +36453,21 @@ _tmp_182_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_182[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); + D(fprintf(stderr, "%*c> _tmp_180[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); Token * _literal; - void *_tmp_273_var; + void *_tmp_264_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_273_var = _tmp_273_rule(p)) // ')' | '**' + (_tmp_264_var = _tmp_264_rule(p)) // ')' | '**' ) { - D(fprintf(stderr, "%*c+ _tmp_182[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_273_var); + D(fprintf(stderr, "%*c+ _tmp_180[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); + _res = _PyPegen_dummy_name(p, _literal, _tmp_264_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_182[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_180[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (')' | '**')")); } _res = NULL; @@ -36608,9 +36476,9 @@ _tmp_182_rule(Parser *p) return _res; } -// _tmp_183: param_no_default | ',' +// _tmp_181: param_no_default | ',' static void * -_tmp_183_rule(Parser *p) +_tmp_181_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36626,18 +36494,18 @@ _tmp_183_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_183[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _tmp_181[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; if ( (param_no_default_var = param_no_default_rule(p)) // param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_183[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_181[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); _res = param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_183[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_181[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } { // ',' @@ -36645,18 +36513,18 @@ _tmp_183_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_183[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_181[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_183[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_181[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_183[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_181[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -36665,9 +36533,9 @@ _tmp_183_rule(Parser *p) return _res; } -// _loop0_184: param_maybe_default +// _loop0_182: param_maybe_default static asdl_seq * -_loop0_184_rule(Parser *p) +_loop0_182_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36692,7 +36560,7 @@ _loop0_184_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_184[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_182[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -36715,7 +36583,7 @@ _loop0_184_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_184[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_182[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36732,9 +36600,9 @@ _loop0_184_rule(Parser *p) return _seq; } -// _tmp_185: param_no_default | ',' +// _tmp_183: param_no_default | ',' static void * -_tmp_185_rule(Parser *p) +_tmp_183_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36750,18 +36618,18 @@ _tmp_185_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_185[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _tmp_183[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; if ( (param_no_default_var = param_no_default_rule(p)) // param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_185[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_183[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); _res = param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_185[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_183[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } { // ',' @@ -36769,18 +36637,18 @@ _tmp_185_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_185[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_183[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_185[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_183[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_185[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_183[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -36789,9 +36657,9 @@ _tmp_185_rule(Parser *p) return _res; } -// _tmp_186: '*' | '**' | '/' +// _tmp_184: '*' | '**' | '/' static void * -_tmp_186_rule(Parser *p) +_tmp_184_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36807,18 +36675,18 @@ _tmp_186_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_186[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c> _tmp_184[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ _tmp_186[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c+ _tmp_184[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_186[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_184[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } { // '**' @@ -36826,18 +36694,18 @@ _tmp_186_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_186[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_184[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_186[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_184[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_186[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_184[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } { // '/' @@ -36845,18 +36713,18 @@ _tmp_186_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_186[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c> _tmp_184[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 17)) // token='/' ) { - D(fprintf(stderr, "%*c+ _tmp_186[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c+ _tmp_184[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_186[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_184[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); } _res = NULL; @@ -36865,9 +36733,9 @@ _tmp_186_rule(Parser *p) return _res; } -// _loop1_187: param_with_default +// _loop1_185: param_with_default static asdl_seq * -_loop1_187_rule(Parser *p) +_loop1_185_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36892,7 +36760,7 @@ _loop1_187_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_187[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default")); + D(fprintf(stderr, "%*c> _loop1_185[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default")); NameDefaultPair* param_with_default_var; while ( (param_with_default_var = param_with_default_rule(p)) // param_with_default @@ -36915,7 +36783,7 @@ _loop1_187_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_187[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_185[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_with_default")); } if (_n == 0 || p->error_indicator) { @@ -36937,9 +36805,9 @@ _loop1_187_rule(Parser *p) return _seq; } -// _tmp_188: lambda_slash_no_default | lambda_slash_with_default +// _tmp_186: lambda_slash_no_default | lambda_slash_with_default static void * -_tmp_188_rule(Parser *p) +_tmp_186_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -36955,18 +36823,18 @@ _tmp_188_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_188[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_186[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); asdl_arg_seq* lambda_slash_no_default_var; if ( (lambda_slash_no_default_var = lambda_slash_no_default_rule(p)) // lambda_slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_188[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_186[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); _res = lambda_slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_188[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_186[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_no_default")); } { // lambda_slash_with_default @@ -36974,18 +36842,18 @@ _tmp_188_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_188[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_186[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); SlashWithDefault* lambda_slash_with_default_var; if ( (lambda_slash_with_default_var = lambda_slash_with_default_rule(p)) // lambda_slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_188[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_186[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); _res = lambda_slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_188[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_186[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_with_default")); } _res = NULL; @@ -36994,9 +36862,9 @@ _tmp_188_rule(Parser *p) return _res; } -// _loop0_189: lambda_param_maybe_default +// _loop0_187: lambda_param_maybe_default static asdl_seq * -_loop0_189_rule(Parser *p) +_loop0_187_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37021,7 +36889,7 @@ _loop0_189_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_189[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_187[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -37044,7 +36912,7 @@ _loop0_189_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_189[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_187[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37061,9 +36929,9 @@ _loop0_189_rule(Parser *p) return _seq; } -// _loop0_190: lambda_param_no_default +// _loop0_188: lambda_param_no_default static asdl_seq * -_loop0_190_rule(Parser *p) +_loop0_188_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37088,7 +36956,7 @@ _loop0_190_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_190[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _loop0_188[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; while ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default @@ -37111,7 +36979,7 @@ _loop0_190_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_190[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_188[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37128,9 +36996,9 @@ _loop0_190_rule(Parser *p) return _seq; } -// _loop0_191: lambda_param_no_default +// _loop0_189: lambda_param_no_default static asdl_seq * -_loop0_191_rule(Parser *p) +_loop0_189_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37155,7 +37023,7 @@ _loop0_191_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_191[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _loop0_189[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; while ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default @@ -37178,7 +37046,7 @@ _loop0_191_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_191[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_189[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37195,9 +37063,9 @@ _loop0_191_rule(Parser *p) return _seq; } -// _loop0_193: ',' lambda_param +// _loop0_191: ',' lambda_param static asdl_seq * -_loop0_193_rule(Parser *p) +_loop0_191_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37222,7 +37090,7 @@ _loop0_193_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_193[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' lambda_param")); + D(fprintf(stderr, "%*c> _loop0_191[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' lambda_param")); Token * _literal; arg_ty elem; while ( @@ -37254,7 +37122,7 @@ _loop0_193_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_193[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_191[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' lambda_param")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37271,9 +37139,9 @@ _loop0_193_rule(Parser *p) return _seq; } -// _gather_192: lambda_param _loop0_193 +// _gather_190: lambda_param _loop0_191 static asdl_seq * -_gather_192_rule(Parser *p) +_gather_190_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37284,27 +37152,27 @@ _gather_192_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // lambda_param _loop0_193 + { // lambda_param _loop0_191 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_192[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_193")); + D(fprintf(stderr, "%*c> _gather_190[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_191")); arg_ty elem; asdl_seq * seq; if ( (elem = lambda_param_rule(p)) // lambda_param && - (seq = _loop0_193_rule(p)) // _loop0_193 + (seq = _loop0_191_rule(p)) // _loop0_191 ) { - D(fprintf(stderr, "%*c+ _gather_192[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_193")); + D(fprintf(stderr, "%*c+ _gather_190[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_191")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_192[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param _loop0_193")); + D(fprintf(stderr, "%*c%s _gather_190[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param _loop0_191")); } _res = NULL; done: @@ -37312,9 +37180,9 @@ _gather_192_rule(Parser *p) return _res; } -// _tmp_194: lambda_slash_no_default | lambda_slash_with_default +// _tmp_192: lambda_slash_no_default | lambda_slash_with_default static void * -_tmp_194_rule(Parser *p) +_tmp_192_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37330,18 +37198,18 @@ _tmp_194_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_194[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_192[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); asdl_arg_seq* lambda_slash_no_default_var; if ( (lambda_slash_no_default_var = lambda_slash_no_default_rule(p)) // lambda_slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_194[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_192[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); _res = lambda_slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_194[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_192[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_no_default")); } { // lambda_slash_with_default @@ -37349,18 +37217,18 @@ _tmp_194_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_194[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_192[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); SlashWithDefault* lambda_slash_with_default_var; if ( (lambda_slash_with_default_var = lambda_slash_with_default_rule(p)) // lambda_slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_194[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_192[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); _res = lambda_slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_194[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_192[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_with_default")); } _res = NULL; @@ -37369,9 +37237,9 @@ _tmp_194_rule(Parser *p) return _res; } -// _loop0_195: lambda_param_maybe_default +// _loop0_193: lambda_param_maybe_default static asdl_seq * -_loop0_195_rule(Parser *p) +_loop0_193_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37396,7 +37264,7 @@ _loop0_195_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_195[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_193[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -37419,7 +37287,7 @@ _loop0_195_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_195[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_193[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37436,9 +37304,9 @@ _loop0_195_rule(Parser *p) return _seq; } -// _tmp_196: ',' | lambda_param_no_default +// _tmp_194: ',' | lambda_param_no_default static void * -_tmp_196_rule(Parser *p) +_tmp_194_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37454,18 +37322,18 @@ _tmp_196_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_196[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_194[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_196[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_194[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_196[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_194[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // lambda_param_no_default @@ -37473,18 +37341,18 @@ _tmp_196_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_196[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _tmp_194[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; if ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_196[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_194[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); _res = lambda_param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_196[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_194[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } _res = NULL; @@ -37493,9 +37361,9 @@ _tmp_196_rule(Parser *p) return _res; } -// _loop0_197: lambda_param_maybe_default +// _loop0_195: lambda_param_maybe_default static asdl_seq * -_loop0_197_rule(Parser *p) +_loop0_195_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37520,7 +37388,7 @@ _loop0_197_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_197[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_195[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -37543,7 +37411,7 @@ _loop0_197_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_197[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_195[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37560,9 +37428,9 @@ _loop0_197_rule(Parser *p) return _seq; } -// _loop1_198: lambda_param_maybe_default +// _loop1_196: lambda_param_maybe_default static asdl_seq * -_loop1_198_rule(Parser *p) +_loop1_196_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37587,7 +37455,7 @@ _loop1_198_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_198[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop1_196[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -37610,7 +37478,7 @@ _loop1_198_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_198[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_196[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } if (_n == 0 || p->error_indicator) { @@ -37632,9 +37500,9 @@ _loop1_198_rule(Parser *p) return _seq; } -// _loop1_199: lambda_param_with_default +// _loop1_197: lambda_param_with_default static asdl_seq * -_loop1_199_rule(Parser *p) +_loop1_197_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37659,7 +37527,7 @@ _loop1_199_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_199[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); + D(fprintf(stderr, "%*c> _loop1_197[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); NameDefaultPair* lambda_param_with_default_var; while ( (lambda_param_with_default_var = lambda_param_with_default_rule(p)) // lambda_param_with_default @@ -37682,7 +37550,7 @@ _loop1_199_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_199[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_197[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default")); } if (_n == 0 || p->error_indicator) { @@ -37704,9 +37572,9 @@ _loop1_199_rule(Parser *p) return _seq; } -// _tmp_200: ':' | ',' (':' | '**') +// _tmp_198: ':' | ',' (':' | '**') static void * -_tmp_200_rule(Parser *p) +_tmp_198_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37722,18 +37590,18 @@ _tmp_200_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_200[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_198[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_200[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_198[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_200[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_198[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // ',' (':' | '**') @@ -37741,21 +37609,21 @@ _tmp_200_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_200[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); + D(fprintf(stderr, "%*c> _tmp_198[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); Token * _literal; - void *_tmp_274_var; + void *_tmp_265_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_274_var = _tmp_274_rule(p)) // ':' | '**' + (_tmp_265_var = _tmp_265_rule(p)) // ':' | '**' ) { - D(fprintf(stderr, "%*c+ _tmp_200[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_274_var); + D(fprintf(stderr, "%*c+ _tmp_198[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); + _res = _PyPegen_dummy_name(p, _literal, _tmp_265_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_200[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_198[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (':' | '**')")); } _res = NULL; @@ -37764,9 +37632,9 @@ _tmp_200_rule(Parser *p) return _res; } -// _tmp_201: lambda_param_no_default | ',' +// _tmp_199: lambda_param_no_default | ',' static void * -_tmp_201_rule(Parser *p) +_tmp_199_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37782,18 +37650,18 @@ _tmp_201_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_201[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _tmp_199[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; if ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_201[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_199[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); _res = lambda_param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_201[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_199[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } { // ',' @@ -37801,18 +37669,18 @@ _tmp_201_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_201[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_199[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_201[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_199[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_201[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_199[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -37821,9 +37689,9 @@ _tmp_201_rule(Parser *p) return _res; } -// _loop0_202: lambda_param_maybe_default +// _loop0_200: lambda_param_maybe_default static asdl_seq * -_loop0_202_rule(Parser *p) +_loop0_200_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37848,7 +37716,7 @@ _loop0_202_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_202[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_200[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -37871,7 +37739,7 @@ _loop0_202_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_202[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_200[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37888,9 +37756,9 @@ _loop0_202_rule(Parser *p) return _seq; } -// _tmp_203: lambda_param_no_default | ',' +// _tmp_201: lambda_param_no_default | ',' static void * -_tmp_203_rule(Parser *p) +_tmp_201_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37906,18 +37774,18 @@ _tmp_203_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_203[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _tmp_201[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; if ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_203[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_201[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); _res = lambda_param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_203[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_201[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } { // ',' @@ -37925,18 +37793,18 @@ _tmp_203_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_203[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_201[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_203[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_201[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_203[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_201[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -37945,9 +37813,9 @@ _tmp_203_rule(Parser *p) return _res; } -// _tmp_204: '*' | '**' | '/' +// _tmp_202: '*' | '**' | '/' static void * -_tmp_204_rule(Parser *p) +_tmp_202_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -37963,18 +37831,18 @@ _tmp_204_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_204[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c> _tmp_202[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ _tmp_204[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c+ _tmp_202[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_204[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_202[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } { // '**' @@ -37982,18 +37850,18 @@ _tmp_204_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_204[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_202[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_204[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_202[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_204[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_202[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } { // '/' @@ -38001,18 +37869,18 @@ _tmp_204_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_204[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c> _tmp_202[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 17)) // token='/' ) { - D(fprintf(stderr, "%*c+ _tmp_204[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c+ _tmp_202[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_204[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_202[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); } _res = NULL; @@ -38021,9 +37889,9 @@ _tmp_204_rule(Parser *p) return _res; } -// _tmp_205: ',' | ')' | ':' +// _tmp_203: ',' | ')' | ':' static void * -_tmp_205_rule(Parser *p) +_tmp_203_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38039,18 +37907,18 @@ _tmp_205_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_205[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_203[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_205[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_203[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_205[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_203[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // ')' @@ -38058,18 +37926,18 @@ _tmp_205_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_205[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_203[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_205[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_203[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_205[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_203[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ':' @@ -38077,18 +37945,18 @@ _tmp_205_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_205[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_203[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_205[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_203[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_205[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_203[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } _res = NULL; @@ -38097,9 +37965,9 @@ _tmp_205_rule(Parser *p) return _res; } -// _loop0_207: ',' dotted_name +// _loop0_205: ',' dotted_name static asdl_seq * -_loop0_207_rule(Parser *p) +_loop0_205_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38124,7 +37992,7 @@ _loop0_207_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_207[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' dotted_name")); + D(fprintf(stderr, "%*c> _loop0_205[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' dotted_name")); Token * _literal; expr_ty elem; while ( @@ -38156,7 +38024,7 @@ _loop0_207_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_207[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_205[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' dotted_name")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -38173,9 +38041,9 @@ _loop0_207_rule(Parser *p) return _seq; } -// _gather_206: dotted_name _loop0_207 +// _gather_204: dotted_name _loop0_205 static asdl_seq * -_gather_206_rule(Parser *p) +_gather_204_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38186,27 +38054,27 @@ _gather_206_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // dotted_name _loop0_207 + { // dotted_name _loop0_205 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_206[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_207")); + D(fprintf(stderr, "%*c> _gather_204[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_205")); expr_ty elem; asdl_seq * seq; if ( (elem = dotted_name_rule(p)) // dotted_name && - (seq = _loop0_207_rule(p)) // _loop0_207 + (seq = _loop0_205_rule(p)) // _loop0_205 ) { - D(fprintf(stderr, "%*c+ _gather_206[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_207")); + D(fprintf(stderr, "%*c+ _gather_204[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_205")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_206[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dotted_name _loop0_207")); + D(fprintf(stderr, "%*c%s _gather_204[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dotted_name _loop0_205")); } _res = NULL; done: @@ -38214,9 +38082,9 @@ _gather_206_rule(Parser *p) return _res; } -// _loop0_209: ',' (expression ['as' star_target]) +// _loop0_207: ',' (expression ['as' star_target]) static asdl_seq * -_loop0_209_rule(Parser *p) +_loop0_207_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38241,13 +38109,13 @@ _loop0_209_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_209[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_207[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_275_rule(p)) // expression ['as' star_target] + (elem = _tmp_266_rule(p)) // expression ['as' star_target] ) { _res = elem; @@ -38273,7 +38141,7 @@ _loop0_209_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_209[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_207[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expression ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -38290,9 +38158,9 @@ _loop0_209_rule(Parser *p) return _seq; } -// _gather_208: (expression ['as' star_target]) _loop0_209 +// _gather_206: (expression ['as' star_target]) _loop0_207 static asdl_seq * -_gather_208_rule(Parser *p) +_gather_206_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38303,27 +38171,27 @@ _gather_208_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expression ['as' star_target]) _loop0_209 + { // (expression ['as' star_target]) _loop0_207 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_208[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_209")); + D(fprintf(stderr, "%*c> _gather_206[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_207")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_275_rule(p)) // expression ['as' star_target] + (elem = _tmp_266_rule(p)) // expression ['as' star_target] && - (seq = _loop0_209_rule(p)) // _loop0_209 + (seq = _loop0_207_rule(p)) // _loop0_207 ) { - D(fprintf(stderr, "%*c+ _gather_208[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_209")); + D(fprintf(stderr, "%*c+ _gather_206[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_207")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_208[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_209")); + D(fprintf(stderr, "%*c%s _gather_206[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_207")); } _res = NULL; done: @@ -38331,9 +38199,9 @@ _gather_208_rule(Parser *p) return _res; } -// _loop0_211: ',' (expressions ['as' star_target]) +// _loop0_209: ',' (expressions ['as' star_target]) static asdl_seq * -_loop0_211_rule(Parser *p) +_loop0_209_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38358,13 +38226,13 @@ _loop0_211_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_211[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_209[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_276_rule(p)) // expressions ['as' star_target] + (elem = _tmp_267_rule(p)) // expressions ['as' star_target] ) { _res = elem; @@ -38390,7 +38258,7 @@ _loop0_211_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_211[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_209[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expressions ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -38407,9 +38275,9 @@ _loop0_211_rule(Parser *p) return _seq; } -// _gather_210: (expressions ['as' star_target]) _loop0_211 +// _gather_208: (expressions ['as' star_target]) _loop0_209 static asdl_seq * -_gather_210_rule(Parser *p) +_gather_208_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38420,27 +38288,27 @@ _gather_210_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expressions ['as' star_target]) _loop0_211 + { // (expressions ['as' star_target]) _loop0_209 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_210[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_211")); + D(fprintf(stderr, "%*c> _gather_208[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_209")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_276_rule(p)) // expressions ['as' star_target] + (elem = _tmp_267_rule(p)) // expressions ['as' star_target] && - (seq = _loop0_211_rule(p)) // _loop0_211 + (seq = _loop0_209_rule(p)) // _loop0_209 ) { - D(fprintf(stderr, "%*c+ _gather_210[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_211")); + D(fprintf(stderr, "%*c+ _gather_208[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_209")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_210[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_211")); + D(fprintf(stderr, "%*c%s _gather_208[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_209")); } _res = NULL; done: @@ -38448,9 +38316,9 @@ _gather_210_rule(Parser *p) return _res; } -// _loop0_213: ',' (expression ['as' star_target]) +// _loop0_211: ',' (expression ['as' star_target]) static asdl_seq * -_loop0_213_rule(Parser *p) +_loop0_211_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38475,13 +38343,13 @@ _loop0_213_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_213[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_211[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_277_rule(p)) // expression ['as' star_target] + (elem = _tmp_268_rule(p)) // expression ['as' star_target] ) { _res = elem; @@ -38507,7 +38375,7 @@ _loop0_213_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_213[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_211[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expression ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -38524,9 +38392,9 @@ _loop0_213_rule(Parser *p) return _seq; } -// _gather_212: (expression ['as' star_target]) _loop0_213 +// _gather_210: (expression ['as' star_target]) _loop0_211 static asdl_seq * -_gather_212_rule(Parser *p) +_gather_210_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38537,27 +38405,27 @@ _gather_212_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expression ['as' star_target]) _loop0_213 + { // (expression ['as' star_target]) _loop0_211 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_212[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_213")); + D(fprintf(stderr, "%*c> _gather_210[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_211")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_277_rule(p)) // expression ['as' star_target] + (elem = _tmp_268_rule(p)) // expression ['as' star_target] && - (seq = _loop0_213_rule(p)) // _loop0_213 + (seq = _loop0_211_rule(p)) // _loop0_211 ) { - D(fprintf(stderr, "%*c+ _gather_212[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_213")); + D(fprintf(stderr, "%*c+ _gather_210[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_211")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_212[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_213")); + D(fprintf(stderr, "%*c%s _gather_210[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_211")); } _res = NULL; done: @@ -38565,9 +38433,9 @@ _gather_212_rule(Parser *p) return _res; } -// _loop0_215: ',' (expressions ['as' star_target]) +// _loop0_213: ',' (expressions ['as' star_target]) static asdl_seq * -_loop0_215_rule(Parser *p) +_loop0_213_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38592,13 +38460,13 @@ _loop0_215_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_215[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_213[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_278_rule(p)) // expressions ['as' star_target] + (elem = _tmp_269_rule(p)) // expressions ['as' star_target] ) { _res = elem; @@ -38624,7 +38492,7 @@ _loop0_215_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_215[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_213[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expressions ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -38641,9 +38509,9 @@ _loop0_215_rule(Parser *p) return _seq; } -// _gather_214: (expressions ['as' star_target]) _loop0_215 +// _gather_212: (expressions ['as' star_target]) _loop0_213 static asdl_seq * -_gather_214_rule(Parser *p) +_gather_212_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38654,27 +38522,27 @@ _gather_214_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expressions ['as' star_target]) _loop0_215 + { // (expressions ['as' star_target]) _loop0_213 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_214[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_215")); + D(fprintf(stderr, "%*c> _gather_212[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_213")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_278_rule(p)) // expressions ['as' star_target] + (elem = _tmp_269_rule(p)) // expressions ['as' star_target] && - (seq = _loop0_215_rule(p)) // _loop0_215 + (seq = _loop0_213_rule(p)) // _loop0_213 ) { - D(fprintf(stderr, "%*c+ _gather_214[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_215")); + D(fprintf(stderr, "%*c+ _gather_212[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_213")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_214[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_215")); + D(fprintf(stderr, "%*c%s _gather_212[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_213")); } _res = NULL; done: @@ -38682,9 +38550,9 @@ _gather_214_rule(Parser *p) return _res; } -// _tmp_216: 'except' | 'finally' +// _tmp_214: 'except' | 'finally' static void * -_tmp_216_rule(Parser *p) +_tmp_214_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38700,18 +38568,18 @@ _tmp_216_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_216[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); + D(fprintf(stderr, "%*c> _tmp_214[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 657)) // token='except' ) { - D(fprintf(stderr, "%*c+ _tmp_216[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); + D(fprintf(stderr, "%*c+ _tmp_214[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_216[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_214[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'except'")); } { // 'finally' @@ -38719,18 +38587,18 @@ _tmp_216_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_216[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); + D(fprintf(stderr, "%*c> _tmp_214[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 653)) // token='finally' ) { - D(fprintf(stderr, "%*c+ _tmp_216[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); + D(fprintf(stderr, "%*c+ _tmp_214[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_216[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_214[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'finally'")); } _res = NULL; @@ -38739,9 +38607,9 @@ _tmp_216_rule(Parser *p) return _res; } -// _loop0_217: block +// _loop0_215: block static asdl_seq * -_loop0_217_rule(Parser *p) +_loop0_215_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38766,7 +38634,7 @@ _loop0_217_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_217[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); + D(fprintf(stderr, "%*c> _loop0_215[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); asdl_stmt_seq* block_var; while ( (block_var = block_rule(p)) // block @@ -38789,7 +38657,7 @@ _loop0_217_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_217[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_215[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "block")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -38806,9 +38674,9 @@ _loop0_217_rule(Parser *p) return _seq; } -// _loop1_218: except_block +// _loop1_216: except_block static asdl_seq * -_loop1_218_rule(Parser *p) +_loop1_216_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38833,7 +38701,7 @@ _loop1_218_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_218[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "except_block")); + D(fprintf(stderr, "%*c> _loop1_216[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "except_block")); excepthandler_ty except_block_var; while ( (except_block_var = except_block_rule(p)) // except_block @@ -38856,7 +38724,7 @@ _loop1_218_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_218[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_216[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "except_block")); } if (_n == 0 || p->error_indicator) { @@ -38878,9 +38746,9 @@ _loop1_218_rule(Parser *p) return _seq; } -// _tmp_219: 'as' NAME +// _tmp_217: 'as' NAME static void * -_tmp_219_rule(Parser *p) +_tmp_217_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38896,7 +38764,7 @@ _tmp_219_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_219[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_217[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -38905,12 +38773,12 @@ _tmp_219_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_219[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_217[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_219[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_217[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -38919,9 +38787,9 @@ _tmp_219_rule(Parser *p) return _res; } -// _loop0_220: block +// _loop0_218: block static asdl_seq * -_loop0_220_rule(Parser *p) +_loop0_218_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -38946,7 +38814,7 @@ _loop0_220_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_220[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); + D(fprintf(stderr, "%*c> _loop0_218[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); asdl_stmt_seq* block_var; while ( (block_var = block_rule(p)) // block @@ -38969,7 +38837,7 @@ _loop0_220_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_220[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_218[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "block")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -38986,9 +38854,9 @@ _loop0_220_rule(Parser *p) return _seq; } -// _loop1_221: except_star_block +// _loop1_219: except_star_block static asdl_seq * -_loop1_221_rule(Parser *p) +_loop1_219_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39013,7 +38881,7 @@ _loop1_221_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_221[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "except_star_block")); + D(fprintf(stderr, "%*c> _loop1_219[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "except_star_block")); excepthandler_ty except_star_block_var; while ( (except_star_block_var = except_star_block_rule(p)) // except_star_block @@ -39036,7 +38904,7 @@ _loop1_221_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_221[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_219[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "except_star_block")); } if (_n == 0 || p->error_indicator) { @@ -39058,9 +38926,9 @@ _loop1_221_rule(Parser *p) return _seq; } -// _tmp_222: expression ['as' NAME] +// _tmp_220: expression ['as' NAME] static void * -_tmp_222_rule(Parser *p) +_tmp_220_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39076,22 +38944,22 @@ _tmp_222_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_222[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); + D(fprintf(stderr, "%*c> _tmp_220[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_279_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var = _tmp_270_rule(p), !p->error_indicator) // ['as' NAME] ) { - D(fprintf(stderr, "%*c+ _tmp_222[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); + D(fprintf(stderr, "%*c+ _tmp_220[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_222[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_220[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' NAME]")); } _res = NULL; @@ -39100,9 +38968,9 @@ _tmp_222_rule(Parser *p) return _res; } -// _tmp_223: 'as' NAME +// _tmp_221: 'as' NAME static void * -_tmp_223_rule(Parser *p) +_tmp_221_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39118,7 +38986,7 @@ _tmp_223_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_223[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_221[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -39127,12 +38995,12 @@ _tmp_223_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_223[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_221[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_223[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_221[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -39141,9 +39009,9 @@ _tmp_223_rule(Parser *p) return _res; } -// _tmp_224: 'as' NAME +// _tmp_222: 'as' NAME static void * -_tmp_224_rule(Parser *p) +_tmp_222_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39159,7 +39027,7 @@ _tmp_224_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_224[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_222[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -39168,12 +39036,12 @@ _tmp_224_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_224[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_222[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_224[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_222[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -39182,9 +39050,9 @@ _tmp_224_rule(Parser *p) return _res; } -// _tmp_225: NEWLINE | ':' +// _tmp_223: NEWLINE | ':' static void * -_tmp_225_rule(Parser *p) +_tmp_223_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39200,18 +39068,18 @@ _tmp_225_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_225[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_223[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); Token * newline_var; if ( (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_225[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_223[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); _res = newline_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_225[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_223[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE")); } { // ':' @@ -39219,18 +39087,18 @@ _tmp_225_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_225[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_223[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_225[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_223[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_225[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_223[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } _res = NULL; @@ -39239,9 +39107,9 @@ _tmp_225_rule(Parser *p) return _res; } -// _tmp_226: 'as' NAME +// _tmp_224: 'as' NAME static void * -_tmp_226_rule(Parser *p) +_tmp_224_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39257,7 +39125,7 @@ _tmp_226_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_226[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_224[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -39266,12 +39134,12 @@ _tmp_226_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_226[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_224[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_226[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_224[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -39280,9 +39148,9 @@ _tmp_226_rule(Parser *p) return _res; } -// _tmp_227: 'as' NAME +// _tmp_225: 'as' NAME static void * -_tmp_227_rule(Parser *p) +_tmp_225_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39298,7 +39166,7 @@ _tmp_227_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_227[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_225[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -39307,12 +39175,12 @@ _tmp_227_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_227[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_225[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_227[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_225[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -39321,9 +39189,9 @@ _tmp_227_rule(Parser *p) return _res; } -// _tmp_228: positional_patterns ',' +// _tmp_226: positional_patterns ',' static void * -_tmp_228_rule(Parser *p) +_tmp_226_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39339,7 +39207,7 @@ _tmp_228_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_228[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); + D(fprintf(stderr, "%*c> _tmp_226[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); Token * _literal; asdl_pattern_seq* positional_patterns_var; if ( @@ -39348,12 +39216,12 @@ _tmp_228_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_228[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); + D(fprintf(stderr, "%*c+ _tmp_226[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); _res = _PyPegen_dummy_name(p, positional_patterns_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_228[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_226[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "positional_patterns ','")); } _res = NULL; @@ -39362,9 +39230,9 @@ _tmp_228_rule(Parser *p) return _res; } -// _tmp_229: '->' expression +// _tmp_227: '->' expression static void * -_tmp_229_rule(Parser *p) +_tmp_227_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39380,7 +39248,7 @@ _tmp_229_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_229[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'->' expression")); + D(fprintf(stderr, "%*c> _tmp_227[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'->' expression")); Token * _literal; expr_ty expression_var; if ( @@ -39389,12 +39257,12 @@ _tmp_229_rule(Parser *p) (expression_var = expression_rule(p)) // expression ) { - D(fprintf(stderr, "%*c+ _tmp_229[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'->' expression")); + D(fprintf(stderr, "%*c+ _tmp_227[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'->' expression")); _res = _PyPegen_dummy_name(p, _literal, expression_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_229[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_227[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'->' expression")); } _res = NULL; @@ -39403,9 +39271,9 @@ _tmp_229_rule(Parser *p) return _res; } -// _tmp_230: '(' arguments? ')' +// _tmp_228: '(' arguments? ')' static void * -_tmp_230_rule(Parser *p) +_tmp_228_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39421,7 +39289,7 @@ _tmp_230_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_230[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); + D(fprintf(stderr, "%*c> _tmp_228[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); Token * _literal; Token * _literal_1; void *_opt_var; @@ -39434,12 +39302,12 @@ _tmp_230_rule(Parser *p) (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_230[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); + D(fprintf(stderr, "%*c+ _tmp_228[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); _res = _PyPegen_dummy_name(p, _literal, _opt_var, _literal_1); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_230[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_228[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' arguments? ')'")); } _res = NULL; @@ -39448,9 +39316,9 @@ _tmp_230_rule(Parser *p) return _res; } -// _tmp_231: '(' arguments? ')' +// _tmp_229: '(' arguments? ')' static void * -_tmp_231_rule(Parser *p) +_tmp_229_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39466,7 +39334,7 @@ _tmp_231_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_231[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); + D(fprintf(stderr, "%*c> _tmp_229[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); Token * _literal; Token * _literal_1; void *_opt_var; @@ -39479,12 +39347,12 @@ _tmp_231_rule(Parser *p) (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_231[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); + D(fprintf(stderr, "%*c+ _tmp_229[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); _res = _PyPegen_dummy_name(p, _literal, _opt_var, _literal_1); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_231[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_229[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' arguments? ')'")); } _res = NULL; @@ -39493,9 +39361,9 @@ _tmp_231_rule(Parser *p) return _res; } -// _loop0_233: ',' double_starred_kvpair +// _loop0_231: ',' double_starred_kvpair static asdl_seq * -_loop0_233_rule(Parser *p) +_loop0_231_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39520,7 +39388,7 @@ _loop0_233_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_233[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair")); + D(fprintf(stderr, "%*c> _loop0_231[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair")); Token * _literal; KeyValuePair* elem; while ( @@ -39552,7 +39420,7 @@ _loop0_233_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_233[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_231[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' double_starred_kvpair")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -39569,9 +39437,9 @@ _loop0_233_rule(Parser *p) return _seq; } -// _gather_232: double_starred_kvpair _loop0_233 +// _gather_230: double_starred_kvpair _loop0_231 static asdl_seq * -_gather_232_rule(Parser *p) +_gather_230_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39582,27 +39450,27 @@ _gather_232_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // double_starred_kvpair _loop0_233 + { // double_starred_kvpair _loop0_231 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_232[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_233")); + D(fprintf(stderr, "%*c> _gather_230[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_231")); KeyValuePair* elem; asdl_seq * seq; if ( (elem = double_starred_kvpair_rule(p)) // double_starred_kvpair && - (seq = _loop0_233_rule(p)) // _loop0_233 + (seq = _loop0_231_rule(p)) // _loop0_231 ) { - D(fprintf(stderr, "%*c+ _gather_232[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_233")); + D(fprintf(stderr, "%*c+ _gather_230[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_231")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_232[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_233")); + D(fprintf(stderr, "%*c%s _gather_230[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_231")); } _res = NULL; done: @@ -39610,9 +39478,9 @@ _gather_232_rule(Parser *p) return _res; } -// _tmp_234: '}' | ',' +// _tmp_232: '}' | ',' static void * -_tmp_234_rule(Parser *p) +_tmp_232_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39628,18 +39496,18 @@ _tmp_234_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_232[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_232[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_234[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_232[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } { // ',' @@ -39647,18 +39515,18 @@ _tmp_234_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_232[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_232[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_234[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_232[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -39667,9 +39535,9 @@ _tmp_234_rule(Parser *p) return _res; } -// _tmp_235: '}' | ',' +// _tmp_233: '}' | ',' static void * -_tmp_235_rule(Parser *p) +_tmp_233_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39685,18 +39553,18 @@ _tmp_235_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_235[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_233[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_235[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_233[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_235[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_233[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } { // ',' @@ -39704,18 +39572,18 @@ _tmp_235_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_235[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_233[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_235[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_233[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_235[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_233[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -39724,123 +39592,9 @@ _tmp_235_rule(Parser *p) return _res; } -// _tmp_236: yield_expr | star_expressions -static void * -_tmp_236_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_236[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_236[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_236[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_236[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_236[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_236[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_237: yield_expr | star_expressions -static void * -_tmp_237_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_237[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_237[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_237[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_237[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_237[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_237[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_238: '=' | '!' | ':' | '}' +// _tmp_234: '=' | '!' | ':' | '}' static void * -_tmp_238_rule(Parser *p) +_tmp_234_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -39856,18 +39610,18 @@ _tmp_238_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_238[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_238[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_238[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_234[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); } { // '!' @@ -39875,18 +39629,18 @@ _tmp_238_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_238[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); + D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' ) { - D(fprintf(stderr, "%*c+ _tmp_238[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); + D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_238[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_234[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!'")); } { // ':' @@ -39894,18 +39648,18 @@ _tmp_238_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_238[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_238[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_238[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_234[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '}' @@ -39913,18 +39667,18 @@ _tmp_238_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_238[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_238[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_238[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_234[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } _res = NULL; @@ -39933,66 +39687,9 @@ _tmp_238_rule(Parser *p) return _res; } -// _tmp_239: yield_expr | star_expressions +// _tmp_235: '!' | ':' | '}' static void * -_tmp_239_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_239[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_239[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_239[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_239[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_239[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_239[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_240: '!' | ':' | '}' -static void * -_tmp_240_rule(Parser *p) +_tmp_235_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40008,18 +39705,18 @@ _tmp_240_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_240[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); + D(fprintf(stderr, "%*c> _tmp_235[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' ) { - D(fprintf(stderr, "%*c+ _tmp_240[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); + D(fprintf(stderr, "%*c+ _tmp_235[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_240[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_235[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!'")); } { // ':' @@ -40027,18 +39724,18 @@ _tmp_240_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_240[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_235[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_240[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_235[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_240[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_235[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '}' @@ -40046,18 +39743,18 @@ _tmp_240_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_240[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_235[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_240[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_235[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_240[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_235[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } _res = NULL; @@ -40066,123 +39763,9 @@ _tmp_240_rule(Parser *p) return _res; } -// _tmp_241: yield_expr | star_expressions +// _tmp_236: '!' NAME static void * -_tmp_241_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_241[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_241[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_241[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_241[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_241[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_241[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_242: yield_expr | star_expressions -static void * -_tmp_242_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_243: '!' NAME -static void * -_tmp_243_rule(Parser *p) +_tmp_236_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40198,7 +39781,7 @@ _tmp_243_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_243[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c> _tmp_236[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); Token * _literal; expr_ty name_var; if ( @@ -40207,12 +39790,12 @@ _tmp_243_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_243[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c+ _tmp_236[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); _res = _PyPegen_dummy_name(p, _literal, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_243[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_236[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' NAME")); } _res = NULL; @@ -40221,9 +39804,9 @@ _tmp_243_rule(Parser *p) return _res; } -// _tmp_244: ':' | '}' +// _tmp_237: ':' | '}' static void * -_tmp_244_rule(Parser *p) +_tmp_237_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40239,18 +39822,18 @@ _tmp_244_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_244[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_237[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_244[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_237[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_244[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_237[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '}' @@ -40258,18 +39841,18 @@ _tmp_244_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_244[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_237[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_244[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_237[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_244[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_237[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } _res = NULL; @@ -40278,66 +39861,9 @@ _tmp_244_rule(Parser *p) return _res; } -// _tmp_245: yield_expr | star_expressions -static void * -_tmp_245_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_245[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_245[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_245[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_245[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_245[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_245[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_246: '!' NAME +// _tmp_238: '!' NAME static void * -_tmp_246_rule(Parser *p) +_tmp_238_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40353,7 +39879,7 @@ _tmp_246_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_246[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c> _tmp_238[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); Token * _literal; expr_ty name_var; if ( @@ -40362,12 +39888,12 @@ _tmp_246_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_246[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c+ _tmp_238[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); _res = _PyPegen_dummy_name(p, _literal, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_246[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_238[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' NAME")); } _res = NULL; @@ -40376,9 +39902,9 @@ _tmp_246_rule(Parser *p) return _res; } -// _loop0_247: fstring_format_spec +// _loop0_239: fstring_format_spec static asdl_seq * -_loop0_247_rule(Parser *p) +_loop0_239_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40403,7 +39929,7 @@ _loop0_247_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_247[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_format_spec")); + D(fprintf(stderr, "%*c> _loop0_239[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_format_spec")); expr_ty fstring_format_spec_var; while ( (fstring_format_spec_var = fstring_format_spec_rule(p)) // fstring_format_spec @@ -40426,7 +39952,7 @@ _loop0_247_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_247[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_239[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring_format_spec")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -40443,66 +39969,9 @@ _loop0_247_rule(Parser *p) return _seq; } -// _tmp_248: yield_expr | star_expressions -static void * -_tmp_248_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // yield_expr - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_248[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); - expr_ty yield_expr_var; - if ( - (yield_expr_var = yield_expr_rule(p)) // yield_expr - ) - { - D(fprintf(stderr, "%*c+ _tmp_248[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); - _res = yield_expr_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_248[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); - } - { // star_expressions - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_248[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); - expr_ty star_expressions_var; - if ( - (star_expressions_var = star_expressions_rule(p)) // star_expressions - ) - { - D(fprintf(stderr, "%*c+ _tmp_248[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); - _res = star_expressions_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_248[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_249: '!' NAME +// _tmp_240: '!' NAME static void * -_tmp_249_rule(Parser *p) +_tmp_240_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40518,7 +39987,7 @@ _tmp_249_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_249[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c> _tmp_240[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); Token * _literal; expr_ty name_var; if ( @@ -40527,12 +39996,12 @@ _tmp_249_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_249[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c+ _tmp_240[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); _res = _PyPegen_dummy_name(p, _literal, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_249[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_240[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' NAME")); } _res = NULL; @@ -40541,9 +40010,9 @@ _tmp_249_rule(Parser *p) return _res; } -// _tmp_250: ':' | '}' +// _tmp_241: ':' | '}' static void * -_tmp_250_rule(Parser *p) +_tmp_241_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40559,18 +40028,18 @@ _tmp_250_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_250[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_241[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_250[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_241[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_250[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_241[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '}' @@ -40578,18 +40047,18 @@ _tmp_250_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_250[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_241[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_250[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_241[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_250[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_241[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } _res = NULL; @@ -40598,9 +40067,9 @@ _tmp_250_rule(Parser *p) return _res; } -// _tmp_251: '+' | '-' | '*' | '/' | '%' | '//' | '@' +// _tmp_242: '+' | '-' | '*' | '/' | '%' | '//' | '@' static void * -_tmp_251_rule(Parser *p) +_tmp_242_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40616,18 +40085,18 @@ _tmp_251_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); + D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 14)) // token='+' ) { - D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); + D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+'")); } { // '-' @@ -40635,18 +40104,18 @@ _tmp_251_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); + D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 15)) // token='-' ) { - D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); + D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'")); } { // '*' @@ -40654,18 +40123,18 @@ _tmp_251_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } { // '/' @@ -40673,18 +40142,18 @@ _tmp_251_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 17)) // token='/' ) { - D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); } { // '%' @@ -40692,18 +40161,18 @@ _tmp_251_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'%'")); + D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'%'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 24)) // token='%' ) { - D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%'")); + D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'%'")); } { // '//' @@ -40711,18 +40180,18 @@ _tmp_251_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'//'")); + D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'//'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 47)) // token='//' ) { - D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//'")); + D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'//'")); } { // '@' @@ -40730,18 +40199,18 @@ _tmp_251_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@'")); + D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 49)) // token='@' ) { - D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@'")); + D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@'")); } _res = NULL; @@ -40750,9 +40219,9 @@ _tmp_251_rule(Parser *p) return _res; } -// _tmp_252: '+' | '-' | '~' +// _tmp_243: '+' | '-' | '~' static void * -_tmp_252_rule(Parser *p) +_tmp_243_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40768,18 +40237,18 @@ _tmp_252_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_252[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); + D(fprintf(stderr, "%*c> _tmp_243[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 14)) // token='+' ) { - D(fprintf(stderr, "%*c+ _tmp_252[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); + D(fprintf(stderr, "%*c+ _tmp_243[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_252[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_243[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+'")); } { // '-' @@ -40787,18 +40256,18 @@ _tmp_252_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_252[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); + D(fprintf(stderr, "%*c> _tmp_243[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 15)) // token='-' ) { - D(fprintf(stderr, "%*c+ _tmp_252[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); + D(fprintf(stderr, "%*c+ _tmp_243[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_252[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_243[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'")); } { // '~' @@ -40806,18 +40275,18 @@ _tmp_252_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_252[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'~'")); + D(fprintf(stderr, "%*c> _tmp_243[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'~'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 31)) // token='~' ) { - D(fprintf(stderr, "%*c+ _tmp_252[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'~'")); + D(fprintf(stderr, "%*c+ _tmp_243[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'~'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_252[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_243[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'~'")); } _res = NULL; @@ -40826,9 +40295,9 @@ _tmp_252_rule(Parser *p) return _res; } -// _tmp_253: star_targets '=' +// _tmp_244: star_targets '=' static void * -_tmp_253_rule(Parser *p) +_tmp_244_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40844,7 +40313,7 @@ _tmp_253_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_253[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_244[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty z; if ( @@ -40853,7 +40322,7 @@ _tmp_253_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_253[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_244[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -40863,7 +40332,7 @@ _tmp_253_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_253[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_244[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -40872,9 +40341,9 @@ _tmp_253_rule(Parser *p) return _res; } -// _tmp_254: '.' | '...' +// _tmp_245: '.' | '...' static void * -_tmp_254_rule(Parser *p) +_tmp_245_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40890,18 +40359,18 @@ _tmp_254_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_254[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c> _tmp_245[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 23)) // token='.' ) { - D(fprintf(stderr, "%*c+ _tmp_254[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c+ _tmp_245[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_254[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_245[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'")); } { // '...' @@ -40909,18 +40378,18 @@ _tmp_254_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_254[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c> _tmp_245[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 52)) // token='...' ) { - D(fprintf(stderr, "%*c+ _tmp_254[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c+ _tmp_245[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_254[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_245[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'")); } _res = NULL; @@ -40929,9 +40398,9 @@ _tmp_254_rule(Parser *p) return _res; } -// _tmp_255: '.' | '...' +// _tmp_246: '.' | '...' static void * -_tmp_255_rule(Parser *p) +_tmp_246_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -40947,18 +40416,18 @@ _tmp_255_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_255[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c> _tmp_246[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 23)) // token='.' ) { - D(fprintf(stderr, "%*c+ _tmp_255[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c+ _tmp_246[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_255[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_246[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'")); } { // '...' @@ -40966,18 +40435,18 @@ _tmp_255_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_255[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c> _tmp_246[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 52)) // token='...' ) { - D(fprintf(stderr, "%*c+ _tmp_255[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c+ _tmp_246[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_255[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_246[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'")); } _res = NULL; @@ -40986,9 +40455,9 @@ _tmp_255_rule(Parser *p) return _res; } -// _tmp_256: '@' named_expression NEWLINE +// _tmp_247: '@' named_expression NEWLINE static void * -_tmp_256_rule(Parser *p) +_tmp_247_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41004,7 +40473,7 @@ _tmp_256_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_256[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_247[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); Token * _literal; expr_ty f; Token * newline_var; @@ -41016,7 +40485,7 @@ _tmp_256_rule(Parser *p) (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_256[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_247[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); _res = f; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41026,7 +40495,7 @@ _tmp_256_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_256[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_247[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@' named_expression NEWLINE")); } _res = NULL; @@ -41035,9 +40504,9 @@ _tmp_256_rule(Parser *p) return _res; } -// _tmp_257: ',' expression +// _tmp_248: ',' expression static void * -_tmp_257_rule(Parser *p) +_tmp_248_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41053,7 +40522,7 @@ _tmp_257_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_257[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c> _tmp_248[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); Token * _literal; expr_ty c; if ( @@ -41062,7 +40531,7 @@ _tmp_257_rule(Parser *p) (c = expression_rule(p)) // expression ) { - D(fprintf(stderr, "%*c+ _tmp_257[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c+ _tmp_248[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41072,7 +40541,7 @@ _tmp_257_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_257[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_248[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression")); } _res = NULL; @@ -41081,9 +40550,9 @@ _tmp_257_rule(Parser *p) return _res; } -// _tmp_258: ',' star_expression +// _tmp_249: ',' star_expression static void * -_tmp_258_rule(Parser *p) +_tmp_249_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41099,7 +40568,7 @@ _tmp_258_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_258[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c> _tmp_249[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); Token * _literal; expr_ty c; if ( @@ -41108,7 +40577,7 @@ _tmp_258_rule(Parser *p) (c = star_expression_rule(p)) // star_expression ) { - D(fprintf(stderr, "%*c+ _tmp_258[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c+ _tmp_249[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41118,7 +40587,7 @@ _tmp_258_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_258[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_249[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_expression")); } _res = NULL; @@ -41127,9 +40596,9 @@ _tmp_258_rule(Parser *p) return _res; } -// _tmp_259: 'or' conjunction +// _tmp_250: 'or' conjunction static void * -_tmp_259_rule(Parser *p) +_tmp_250_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41145,7 +40614,7 @@ _tmp_259_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_259[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c> _tmp_250[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); Token * _keyword; expr_ty c; if ( @@ -41154,7 +40623,7 @@ _tmp_259_rule(Parser *p) (c = conjunction_rule(p)) // conjunction ) { - D(fprintf(stderr, "%*c+ _tmp_259[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c+ _tmp_250[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41164,7 +40633,7 @@ _tmp_259_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_259[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_250[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'or' conjunction")); } _res = NULL; @@ -41173,9 +40642,9 @@ _tmp_259_rule(Parser *p) return _res; } -// _tmp_260: 'and' inversion +// _tmp_251: 'and' inversion static void * -_tmp_260_rule(Parser *p) +_tmp_251_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41191,7 +40660,7 @@ _tmp_260_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_260[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); Token * _keyword; expr_ty c; if ( @@ -41200,7 +40669,7 @@ _tmp_260_rule(Parser *p) (c = inversion_rule(p)) // inversion ) { - D(fprintf(stderr, "%*c+ _tmp_260[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41210,7 +40679,7 @@ _tmp_260_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_260[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'and' inversion")); } _res = NULL; @@ -41219,9 +40688,9 @@ _tmp_260_rule(Parser *p) return _res; } -// _tmp_261: slice | starred_expression +// _tmp_252: slice | starred_expression static void * -_tmp_261_rule(Parser *p) +_tmp_252_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41237,18 +40706,18 @@ _tmp_261_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_261[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); + D(fprintf(stderr, "%*c> _tmp_252[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); expr_ty slice_var; if ( (slice_var = slice_rule(p)) // slice ) { - D(fprintf(stderr, "%*c+ _tmp_261[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); + D(fprintf(stderr, "%*c+ _tmp_252[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); _res = slice_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_261[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_252[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice")); } { // starred_expression @@ -41256,18 +40725,18 @@ _tmp_261_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_261[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_252[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_261[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_252[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_261[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_252[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } _res = NULL; @@ -41276,9 +40745,9 @@ _tmp_261_rule(Parser *p) return _res; } -// _tmp_262: fstring | string +// _tmp_253: fstring | string static void * -_tmp_262_rule(Parser *p) +_tmp_253_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41294,18 +40763,18 @@ _tmp_262_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_262[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring")); + D(fprintf(stderr, "%*c> _tmp_253[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring")); expr_ty fstring_var; if ( (fstring_var = fstring_rule(p)) // fstring ) { - D(fprintf(stderr, "%*c+ _tmp_262[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "fstring")); + D(fprintf(stderr, "%*c+ _tmp_253[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "fstring")); _res = fstring_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_262[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_253[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring")); } { // string @@ -41313,18 +40782,18 @@ _tmp_262_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_262[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "string")); + D(fprintf(stderr, "%*c> _tmp_253[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "string")); expr_ty string_var; if ( (string_var = string_rule(p)) // string ) { - D(fprintf(stderr, "%*c+ _tmp_262[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "string")); + D(fprintf(stderr, "%*c+ _tmp_253[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "string")); _res = string_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_262[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_253[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "string")); } _res = NULL; @@ -41333,9 +40802,9 @@ _tmp_262_rule(Parser *p) return _res; } -// _tmp_263: 'if' disjunction +// _tmp_254: 'if' disjunction static void * -_tmp_263_rule(Parser *p) +_tmp_254_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41351,7 +40820,7 @@ _tmp_263_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_263[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_254[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( @@ -41360,7 +40829,7 @@ _tmp_263_rule(Parser *p) (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_263[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_254[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41370,7 +40839,7 @@ _tmp_263_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_263[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_254[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -41379,9 +40848,9 @@ _tmp_263_rule(Parser *p) return _res; } -// _tmp_264: 'if' disjunction +// _tmp_255: 'if' disjunction static void * -_tmp_264_rule(Parser *p) +_tmp_255_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41397,7 +40866,7 @@ _tmp_264_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_264[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_255[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( @@ -41406,7 +40875,7 @@ _tmp_264_rule(Parser *p) (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_264[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_255[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41416,7 +40885,7 @@ _tmp_264_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_264[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_255[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -41425,9 +40894,9 @@ _tmp_264_rule(Parser *p) return _res; } -// _loop0_265: (',' bitwise_or) +// _loop0_256: (',' bitwise_or) static asdl_seq * -_loop0_265_rule(Parser *p) +_loop0_256_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41452,13 +40921,13 @@ _loop0_265_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_265[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)")); - void *_tmp_280_var; + D(fprintf(stderr, "%*c> _loop0_256[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)")); + void *_tmp_271_var; while ( - (_tmp_280_var = _tmp_280_rule(p)) // ',' bitwise_or + (_tmp_271_var = _tmp_271_rule(p)) // ',' bitwise_or ) { - _res = _tmp_280_var; + _res = _tmp_271_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -41475,7 +40944,7 @@ _loop0_265_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_265[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_256[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' bitwise_or)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -41492,9 +40961,9 @@ _loop0_265_rule(Parser *p) return _seq; } -// _tmp_266: starred_expression | (assignment_expression | expression !':=') !'=' +// _tmp_257: starred_expression | (assignment_expression | expression !':=') !'=' static void * -_tmp_266_rule(Parser *p) +_tmp_257_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41510,18 +40979,18 @@ _tmp_266_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_266[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_257[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_266[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_257[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_266[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_257[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } { // (assignment_expression | expression !':=') !'=' @@ -41529,20 +40998,20 @@ _tmp_266_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_266[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - void *_tmp_281_var; + D(fprintf(stderr, "%*c> _tmp_257[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + void *_tmp_272_var; if ( - (_tmp_281_var = _tmp_281_rule(p)) // assignment_expression | expression !':=' + (_tmp_272_var = _tmp_272_rule(p)) // assignment_expression | expression !':=' && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_266[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - _res = _tmp_281_var; + D(fprintf(stderr, "%*c+ _tmp_257[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + _res = _tmp_272_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_266[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_257[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='")); } _res = NULL; @@ -41551,9 +41020,9 @@ _tmp_266_rule(Parser *p) return _res; } -// _tmp_267: ',' star_target +// _tmp_258: ',' star_target static void * -_tmp_267_rule(Parser *p) +_tmp_258_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41569,7 +41038,7 @@ _tmp_267_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_267[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c> _tmp_258[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); Token * _literal; expr_ty c; if ( @@ -41578,7 +41047,7 @@ _tmp_267_rule(Parser *p) (c = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_267[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c+ _tmp_258[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41588,7 +41057,7 @@ _tmp_267_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_267[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_258[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); } _res = NULL; @@ -41597,9 +41066,9 @@ _tmp_267_rule(Parser *p) return _res; } -// _tmp_268: ',' star_target +// _tmp_259: ',' star_target static void * -_tmp_268_rule(Parser *p) +_tmp_259_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41615,7 +41084,7 @@ _tmp_268_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_268[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c> _tmp_259[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); Token * _literal; expr_ty c; if ( @@ -41624,7 +41093,7 @@ _tmp_268_rule(Parser *p) (c = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_268[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c+ _tmp_259[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -41634,7 +41103,7 @@ _tmp_268_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_268[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_259[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); } _res = NULL; @@ -41643,10 +41112,10 @@ _tmp_268_rule(Parser *p) return _res; } -// _tmp_269: +// _tmp_260: // | ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs static void * -_tmp_269_rule(Parser *p) +_tmp_260_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41662,24 +41131,24 @@ _tmp_269_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_269[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); - asdl_seq * _gather_282_var; + D(fprintf(stderr, "%*c> _tmp_260[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + asdl_seq * _gather_273_var; Token * _literal; asdl_seq* kwargs_var; if ( - (_gather_282_var = _gather_282_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ + (_gather_273_var = _gather_273_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && (kwargs_var = kwargs_rule(p)) // kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_269[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); - _res = _PyPegen_dummy_name(p, _gather_282_var, _literal, kwargs_var); + D(fprintf(stderr, "%*c+ _tmp_260[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + _res = _PyPegen_dummy_name(p, _gather_273_var, _literal, kwargs_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_269[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_260[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); } _res = NULL; @@ -41688,9 +41157,9 @@ _tmp_269_rule(Parser *p) return _res; } -// _tmp_270: starred_expression !'=' +// _tmp_261: starred_expression !'=' static void * -_tmp_270_rule(Parser *p) +_tmp_261_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41706,7 +41175,7 @@ _tmp_270_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_270[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); + D(fprintf(stderr, "%*c> _tmp_261[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression @@ -41714,12 +41183,12 @@ _tmp_270_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_270[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); + D(fprintf(stderr, "%*c+ _tmp_261[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_270[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_261[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression !'='")); } _res = NULL; @@ -41728,9 +41197,9 @@ _tmp_270_rule(Parser *p) return _res; } -// _tmp_271: star_targets '=' +// _tmp_262: star_targets '=' static void * -_tmp_271_rule(Parser *p) +_tmp_262_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41746,7 +41215,7 @@ _tmp_271_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_271[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_262[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty star_targets_var; if ( @@ -41755,12 +41224,12 @@ _tmp_271_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_271[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_262[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = _PyPegen_dummy_name(p, star_targets_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_271[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_262[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -41769,9 +41238,9 @@ _tmp_271_rule(Parser *p) return _res; } -// _tmp_272: star_targets '=' +// _tmp_263: star_targets '=' static void * -_tmp_272_rule(Parser *p) +_tmp_263_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41787,7 +41256,7 @@ _tmp_272_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_272[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_263[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty star_targets_var; if ( @@ -41796,12 +41265,12 @@ _tmp_272_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_272[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_263[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = _PyPegen_dummy_name(p, star_targets_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_272[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_263[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -41810,9 +41279,9 @@ _tmp_272_rule(Parser *p) return _res; } -// _tmp_273: ')' | '**' +// _tmp_264: ')' | '**' static void * -_tmp_273_rule(Parser *p) +_tmp_264_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41828,18 +41297,18 @@ _tmp_273_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_273[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_264[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_273[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_264[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_273[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_264[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // '**' @@ -41847,18 +41316,18 @@ _tmp_273_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_273[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_264[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_273[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_264[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_273[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_264[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -41867,9 +41336,9 @@ _tmp_273_rule(Parser *p) return _res; } -// _tmp_274: ':' | '**' +// _tmp_265: ':' | '**' static void * -_tmp_274_rule(Parser *p) +_tmp_265_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41885,18 +41354,18 @@ _tmp_274_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_274[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_265[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_274[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_265[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_274[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_265[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '**' @@ -41904,18 +41373,18 @@ _tmp_274_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_274[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_265[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_274[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_265[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_274[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_265[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -41924,9 +41393,9 @@ _tmp_274_rule(Parser *p) return _res; } -// _tmp_275: expression ['as' star_target] +// _tmp_266: expression ['as' star_target] static void * -_tmp_275_rule(Parser *p) +_tmp_266_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41942,22 +41411,22 @@ _tmp_275_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_275[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_266[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_284_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_275_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_275[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_266[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_275[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_266[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' star_target]")); } _res = NULL; @@ -41966,9 +41435,9 @@ _tmp_275_rule(Parser *p) return _res; } -// _tmp_276: expressions ['as' star_target] +// _tmp_267: expressions ['as' star_target] static void * -_tmp_276_rule(Parser *p) +_tmp_267_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -41984,22 +41453,22 @@ _tmp_276_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_276[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_267[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expressions_var; if ( (expressions_var = expressions_rule(p)) // expressions && - (_opt_var = _tmp_285_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_276_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_276[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_267[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); _res = _PyPegen_dummy_name(p, expressions_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_276[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_267[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expressions ['as' star_target]")); } _res = NULL; @@ -42008,9 +41477,9 @@ _tmp_276_rule(Parser *p) return _res; } -// _tmp_277: expression ['as' star_target] +// _tmp_268: expression ['as' star_target] static void * -_tmp_277_rule(Parser *p) +_tmp_268_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42026,22 +41495,22 @@ _tmp_277_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_277[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_268[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_286_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_277_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_277[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_268[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_277[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_268[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' star_target]")); } _res = NULL; @@ -42050,9 +41519,9 @@ _tmp_277_rule(Parser *p) return _res; } -// _tmp_278: expressions ['as' star_target] +// _tmp_269: expressions ['as' star_target] static void * -_tmp_278_rule(Parser *p) +_tmp_269_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42068,22 +41537,22 @@ _tmp_278_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_278[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_269[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expressions_var; if ( (expressions_var = expressions_rule(p)) // expressions && - (_opt_var = _tmp_287_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_278_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_278[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_269[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); _res = _PyPegen_dummy_name(p, expressions_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_278[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_269[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expressions ['as' star_target]")); } _res = NULL; @@ -42092,9 +41561,9 @@ _tmp_278_rule(Parser *p) return _res; } -// _tmp_279: 'as' NAME +// _tmp_270: 'as' NAME static void * -_tmp_279_rule(Parser *p) +_tmp_270_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42110,7 +41579,7 @@ _tmp_279_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_279[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_270[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -42119,12 +41588,12 @@ _tmp_279_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_279[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_270[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_279[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_270[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -42133,9 +41602,9 @@ _tmp_279_rule(Parser *p) return _res; } -// _tmp_280: ',' bitwise_or +// _tmp_271: ',' bitwise_or static void * -_tmp_280_rule(Parser *p) +_tmp_271_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42151,7 +41620,7 @@ _tmp_280_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_280[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); + D(fprintf(stderr, "%*c> _tmp_271[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); Token * _literal; expr_ty bitwise_or_var; if ( @@ -42160,12 +41629,12 @@ _tmp_280_rule(Parser *p) (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or ) { - D(fprintf(stderr, "%*c+ _tmp_280[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); + D(fprintf(stderr, "%*c+ _tmp_271[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); _res = _PyPegen_dummy_name(p, _literal, bitwise_or_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_280[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_271[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' bitwise_or")); } _res = NULL; @@ -42174,9 +41643,9 @@ _tmp_280_rule(Parser *p) return _res; } -// _tmp_281: assignment_expression | expression !':=' +// _tmp_272: assignment_expression | expression !':=' static void * -_tmp_281_rule(Parser *p) +_tmp_272_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42192,18 +41661,18 @@ _tmp_281_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_281[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c> _tmp_272[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); expr_ty assignment_expression_var; if ( (assignment_expression_var = assignment_expression_rule(p)) // assignment_expression ) { - D(fprintf(stderr, "%*c+ _tmp_281[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c+ _tmp_272[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); _res = assignment_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_281[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_272[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "assignment_expression")); } { // expression !':=' @@ -42211,7 +41680,7 @@ _tmp_281_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_281[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c> _tmp_272[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression @@ -42219,12 +41688,12 @@ _tmp_281_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 53) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_281[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c+ _tmp_272[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); _res = expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_281[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_272[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); } _res = NULL; @@ -42233,9 +41702,9 @@ _tmp_281_rule(Parser *p) return _res; } -// _loop0_283: ',' (starred_expression | (assignment_expression | expression !':=') !'=') +// _loop0_274: ',' (starred_expression | (assignment_expression | expression !':=') !'=') static asdl_seq * -_loop0_283_rule(Parser *p) +_loop0_274_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42260,13 +41729,13 @@ _loop0_283_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_283[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); + D(fprintf(stderr, "%*c> _loop0_274[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_288_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_279_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' ) { _res = elem; @@ -42292,7 +41761,7 @@ _loop0_283_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_283[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_274[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -42309,10 +41778,10 @@ _loop0_283_rule(Parser *p) return _seq; } -// _gather_282: -// | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_283 +// _gather_273: +// | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_274 static asdl_seq * -_gather_282_rule(Parser *p) +_gather_273_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42323,27 +41792,27 @@ _gather_282_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_283 + { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_274 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_282[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_283")); + D(fprintf(stderr, "%*c> _gather_273[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_274")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_288_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_279_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' && - (seq = _loop0_283_rule(p)) // _loop0_283 + (seq = _loop0_274_rule(p)) // _loop0_274 ) { - D(fprintf(stderr, "%*c+ _gather_282[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_283")); + D(fprintf(stderr, "%*c+ _gather_273[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_274")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_282[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_283")); + D(fprintf(stderr, "%*c%s _gather_273[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_274")); } _res = NULL; done: @@ -42351,9 +41820,9 @@ _gather_282_rule(Parser *p) return _res; } -// _tmp_284: 'as' star_target +// _tmp_275: 'as' star_target static void * -_tmp_284_rule(Parser *p) +_tmp_275_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42369,7 +41838,7 @@ _tmp_284_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_284[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_275[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -42378,12 +41847,12 @@ _tmp_284_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_284[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_275[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_284[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_275[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; @@ -42392,9 +41861,9 @@ _tmp_284_rule(Parser *p) return _res; } -// _tmp_285: 'as' star_target +// _tmp_276: 'as' star_target static void * -_tmp_285_rule(Parser *p) +_tmp_276_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42410,7 +41879,7 @@ _tmp_285_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_285[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_276[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -42419,12 +41888,12 @@ _tmp_285_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_285[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_276[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_285[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_276[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; @@ -42433,9 +41902,9 @@ _tmp_285_rule(Parser *p) return _res; } -// _tmp_286: 'as' star_target +// _tmp_277: 'as' star_target static void * -_tmp_286_rule(Parser *p) +_tmp_277_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42451,7 +41920,7 @@ _tmp_286_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_286[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_277[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -42460,12 +41929,12 @@ _tmp_286_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_286[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_277[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_286[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_277[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; @@ -42474,9 +41943,9 @@ _tmp_286_rule(Parser *p) return _res; } -// _tmp_287: 'as' star_target +// _tmp_278: 'as' star_target static void * -_tmp_287_rule(Parser *p) +_tmp_278_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42492,7 +41961,7 @@ _tmp_287_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_287[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_278[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -42501,12 +41970,12 @@ _tmp_287_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_287[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_278[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_287[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_278[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; @@ -42515,9 +41984,9 @@ _tmp_287_rule(Parser *p) return _res; } -// _tmp_288: starred_expression | (assignment_expression | expression !':=') !'=' +// _tmp_279: starred_expression | (assignment_expression | expression !':=') !'=' static void * -_tmp_288_rule(Parser *p) +_tmp_279_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42533,18 +42002,18 @@ _tmp_288_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_288[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_279[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_288[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_279[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_288[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_279[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } { // (assignment_expression | expression !':=') !'=' @@ -42552,20 +42021,20 @@ _tmp_288_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_288[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - void *_tmp_289_var; + D(fprintf(stderr, "%*c> _tmp_279[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + void *_tmp_280_var; if ( - (_tmp_289_var = _tmp_289_rule(p)) // assignment_expression | expression !':=' + (_tmp_280_var = _tmp_280_rule(p)) // assignment_expression | expression !':=' && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_288[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - _res = _tmp_289_var; + D(fprintf(stderr, "%*c+ _tmp_279[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + _res = _tmp_280_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_288[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_279[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='")); } _res = NULL; @@ -42574,9 +42043,9 @@ _tmp_288_rule(Parser *p) return _res; } -// _tmp_289: assignment_expression | expression !':=' +// _tmp_280: assignment_expression | expression !':=' static void * -_tmp_289_rule(Parser *p) +_tmp_280_rule(Parser *p) { if (p->level++ == MAXSTACK) { _Pypegen_stack_overflow(p); @@ -42592,18 +42061,18 @@ _tmp_289_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_289[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c> _tmp_280[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); expr_ty assignment_expression_var; if ( (assignment_expression_var = assignment_expression_rule(p)) // assignment_expression ) { - D(fprintf(stderr, "%*c+ _tmp_289[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c+ _tmp_280[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); _res = assignment_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_289[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_280[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "assignment_expression")); } { // expression !':=' @@ -42611,7 +42080,7 @@ _tmp_289_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_289[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c> _tmp_280[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression @@ -42619,12 +42088,12 @@ _tmp_289_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 53) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_289[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c+ _tmp_280[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); _res = expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_289[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_280[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); } _res = NULL; diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 7cdd5debe9a225..84ed183c762e40 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -253,7 +253,7 @@ def lookahead_call_helper(self, node: Lookahead, positive: int) -> FunctionCall: else: return FunctionCall( function=f"_PyPegen_lookahead", - arguments=[positive, call.function, *call.arguments], + arguments=[positive, f"(void *(*)(Parser *)) {call.function}", *call.arguments], return_type="int", ) From 5865fa5f9b33ce003dad99cac5e5378d6aed47c5 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 24 Apr 2024 11:42:01 -0600 Subject: [PATCH 039/217] gh-117953: Add Internal struct _Py_ext_module_loader_info (gh-118194) This helps with a later change that splits up _PyImport_LoadDynamicModuleWithSpec(). --- Include/internal/pycore_importdl.h | 30 +++++- Python/import.c | 94 ++++++++++-------- Python/importdl.c | 150 +++++++++++++++++++---------- 3 files changed, 180 insertions(+), 94 deletions(-) diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index c8583582b358ac..8bf7c2a48f66be 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -14,10 +14,38 @@ extern "C" { extern const char *_PyImport_DynLoadFiletab[]; -extern PyObject *_PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *); typedef PyObject *(*PyModInitFunction)(void); +struct _Py_ext_module_loader_info { + PyObject *filename; +#ifndef MS_WINDOWS + PyObject *filename_encoded; +#endif + PyObject *name; + PyObject *name_encoded; + /* path is always a borrowed ref of name or filename, + * depending on if it's builtin or not. */ + PyObject *path; + const char *hook_prefix; + const char *newcontext; +}; +extern void _Py_ext_module_loader_info_clear( + struct _Py_ext_module_loader_info *info); +extern int _Py_ext_module_loader_info_init( + struct _Py_ext_module_loader_info *info, + PyObject *name, + PyObject *filename); +extern int _Py_ext_module_loader_info_init_from_spec( + struct _Py_ext_module_loader_info *info, + PyObject *spec); + +extern PyObject *_PyImport_LoadDynamicModuleWithSpec( + struct _Py_ext_module_loader_info *info, + PyObject *spec, + FILE *fp); + + /* Max length of module suffix searched for -- accommodates "module.slb" */ #define MAXSUFFIXSIZE 12 diff --git a/Python/import.c b/Python/import.c index 739f506fe03100..56011295f95190 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1328,11 +1328,11 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, static PyObject * -import_find_extension(PyThreadState *tstate, PyObject *name, - PyObject *path) +import_find_extension(PyThreadState *tstate, + struct _Py_ext_module_loader_info *info) { /* Only single-phase init modules will be in the cache. */ - PyModuleDef *def = _extensions_cache_get(path, name); + PyModuleDef *def = _extensions_cache_get(info->path, info->name); if (def == NULL) { return NULL; } @@ -1340,7 +1340,7 @@ import_find_extension(PyThreadState *tstate, PyObject *name, /* It may have been successfully imported previously in an interpreter that allows legacy modules but is not allowed in the current interpreter. */ - const char *name_buf = PyUnicode_AsUTF8(name); + const char *name_buf = PyUnicode_AsUTF8(info->name); assert(name_buf != NULL); if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { return NULL; @@ -1355,12 +1355,13 @@ import_find_extension(PyThreadState *tstate, PyObject *name, if (m_copy == NULL) { /* It might be a core module (e.g. sys & builtins), for which we don't set m_copy. */ - m_copy = get_core_module_dict(tstate->interp, name, path); + m_copy = get_core_module_dict( + tstate->interp, info->name, info->path); if (m_copy == NULL) { return NULL; } } - mod = import_add_module(tstate, name); + mod = import_add_module(tstate, info->name); if (mod == NULL) { return NULL; } @@ -1378,15 +1379,16 @@ import_find_extension(PyThreadState *tstate, PyObject *name, if (def->m_base.m_init == NULL) return NULL; mod = def->m_base.m_init(); - if (mod == NULL) + if (mod == NULL) { return NULL; - if (PyObject_SetItem(modules, name, mod) == -1) { + } + if (PyObject_SetItem(modules, info->name, mod) == -1) { Py_DECREF(mod); return NULL; } } if (_modules_by_index_set(tstate->interp, def, mod) < 0) { - PyMapping_DelItem(modules, name); + PyMapping_DelItem(modules, info->name); Py_DECREF(mod); return NULL; } @@ -1394,7 +1396,7 @@ import_find_extension(PyThreadState *tstate, PyObject *name, int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; if (verbose) { PySys_FormatStderr("import %U # previously loaded (%R)\n", - name, path); + info->name, info->path); } return mod; } @@ -1505,44 +1507,56 @@ static PyObject* create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) { PyModuleDef *def = NULL; - PyObject *mod = import_find_extension(tstate, name, name); + + struct _Py_ext_module_loader_info info; + if (_Py_ext_module_loader_info_init(&info, name, NULL) < 0) { + return NULL; + } + + PyObject *mod = import_find_extension(tstate, &info); if (mod || _PyErr_Occurred(tstate)) { - return mod; + goto finally; } struct _inittab *found = NULL; for (struct _inittab *p = INITTAB; p->name != NULL; p++) { - if (_PyUnicode_EqualToASCIIString(name, p->name)) { + if (_PyUnicode_EqualToASCIIString(info.name, p->name)) { found = p; } } if (found == NULL) { // not found - Py_RETURN_NONE; + mod = Py_NewRef(Py_None); + goto finally; } PyModInitFunction p0 = (PyModInitFunction)found->initfunc; if (p0 == NULL) { /* Cannot re-init internal module ("sys" or "builtins") */ - assert(is_core_module(tstate->interp, name, name)); - return import_add_module(tstate, name); + assert(is_core_module(tstate->interp, info.name, info.path)); + mod = import_add_module(tstate, info.name); + goto finally; } mod = p0(); if (mod == NULL) { - return NULL; + goto finally; } if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { def = (PyModuleDef*)mod; assert(!is_singlephase(def)); - return PyModule_FromDefAndSpec(def, spec); + mod = PyModule_FromDefAndSpec(def, spec); + if (mod == NULL) { + goto finally; + } } else { assert(PyModule_Check(mod)); def = PyModule_GetDef(mod); if (def == NULL) { - return NULL; + Py_CLEAR(mod); + goto finally; } assert(is_singlephase(def)); @@ -1553,22 +1567,29 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) // gh-88216: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. if (def->m_size == -1 - && !is_core_module(tstate->interp, name, name)) + && !is_core_module(tstate->interp, info.name, info.path)) { singlephase.m_dict = PyModule_GetDict(mod); assert(singlephase.m_dict != NULL); } if (update_global_state_for_extension( - tstate, name, name, def, &singlephase) < 0) + tstate, info.name, info.path, def, &singlephase) < 0) { - return NULL; + Py_CLEAR(mod); + goto finally; } PyObject *modules = get_modules_dict(tstate, true); - if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) { - return NULL; + if (finish_singlephase_extension( + tstate, mod, def, info.name, modules) < 0) + { + Py_CLEAR(mod); + goto finally; } - return mod; } + +finally: + _Py_ext_module_loader_info_clear(&info); + return mod; } @@ -3878,28 +3899,22 @@ static PyObject * _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) /*[clinic end generated code: output=83249b827a4fde77 input=c31b954f4cf4e09d]*/ { - PyObject *mod, *name, *filename; + PyObject *mod = NULL; FILE *fp; - name = PyObject_GetAttrString(spec, "name"); - if (name == NULL) { - return NULL; - } - - filename = PyObject_GetAttrString(spec, "origin"); - if (filename == NULL) { - Py_DECREF(name); + struct _Py_ext_module_loader_info info; + if (_Py_ext_module_loader_info_init_from_spec(&info, spec) < 0) { return NULL; } PyThreadState *tstate = _PyThreadState_GET(); - mod = import_find_extension(tstate, name, filename); + mod = import_find_extension(tstate, &info); if (mod != NULL || _PyErr_Occurred(tstate)) { assert(mod == NULL || !_PyErr_Occurred(tstate)); goto finally; } - if (PySys_Audit("import", "OOOOO", name, filename, + if (PySys_Audit("import", "OOOOO", info.name, info.filename, Py_None, Py_None, Py_None) < 0) { goto finally; @@ -3911,7 +3926,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) * _PyImport_GetModInitFunc(), but it isn't clear if the intervening * code relies on fp still being open. */ if (file != NULL) { - fp = _Py_fopen_obj(filename, "r"); + fp = _Py_fopen_obj(info.filename, "r"); if (fp == NULL) { goto finally; } @@ -3920,7 +3935,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) fp = NULL; } - mod = _PyImport_LoadDynamicModuleWithSpec(spec, fp); + mod = _PyImport_LoadDynamicModuleWithSpec(&info, spec, fp); // XXX Shouldn't this happen in the error cases too. if (fp) { @@ -3928,8 +3943,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } finally: - Py_DECREF(name); - Py_DECREF(filename); + _Py_ext_module_loader_info_clear(&info); return mod; } diff --git a/Python/importdl.c b/Python/importdl.c index e512161d3071f2..65370249493325 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -93,57 +93,108 @@ get_encoded_name(PyObject *name, const char **hook_prefix) { return NULL; } -PyObject * -_PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) +void +_Py_ext_module_loader_info_clear(struct _Py_ext_module_loader_info *info) { + Py_CLEAR(info->filename); #ifndef MS_WINDOWS - PyObject *filename_bytes = NULL; - const char *filename_buf; + Py_CLEAR(info->filename_encoded); #endif - PyObject *name_unicode = NULL, *name = NULL, *filename = NULL, *m = NULL; - const char *name_buf, *hook_prefix; - const char *oldcontext, *newcontext; - dl_funcptr exportfunc; - PyModuleDef *def; - PyModInitFunction p0; + Py_CLEAR(info->name); + Py_CLEAR(info->name_encoded); +} - name_unicode = PyObject_GetAttrString(spec, "name"); - if (name_unicode == NULL) { - return NULL; - } - if (!PyUnicode_Check(name_unicode)) { +int +_Py_ext_module_loader_info_init(struct _Py_ext_module_loader_info *p_info, + PyObject *name, PyObject *filename) +{ + struct _Py_ext_module_loader_info info = {0}; + + assert(name != NULL); + if (!PyUnicode_Check(name)) { PyErr_SetString(PyExc_TypeError, - "spec.name must be a string"); - goto error; + "module name must be a string"); + _Py_ext_module_loader_info_clear(&info); + return -1; } - newcontext = PyUnicode_AsUTF8(name_unicode); - if (newcontext == NULL) { - goto error; + info.name = Py_NewRef(name); + + info.name_encoded = get_encoded_name(info.name, &info.hook_prefix); + if (info.name_encoded == NULL) { + _Py_ext_module_loader_info_clear(&info); + return -1; } - name = get_encoded_name(name_unicode, &hook_prefix); - if (name == NULL) { - goto error; + info.newcontext = PyUnicode_AsUTF8(info.name); + if (info.newcontext == NULL) { + _Py_ext_module_loader_info_clear(&info); + return -1; } - name_buf = PyBytes_AS_STRING(name); - filename = PyObject_GetAttrString(spec, "origin"); + if (filename != NULL) { + if (!PyUnicode_Check(filename)) { + PyErr_SetString(PyExc_TypeError, + "module filename must be a string"); + _Py_ext_module_loader_info_clear(&info); + return -1; + } + info.filename = Py_NewRef(filename); + +#ifndef MS_WINDOWS + info.filename_encoded = PyUnicode_EncodeFSDefault(info.filename); + if (info.filename_encoded == NULL) { + _Py_ext_module_loader_info_clear(&info); + return -1; + } +#endif + + info.path = info.filename; + } + else { + info.path = info.name; + } + + *p_info = info; + return 0; +} + +int +_Py_ext_module_loader_info_init_from_spec( + struct _Py_ext_module_loader_info *p_info, + PyObject *spec) +{ + PyObject *name = PyObject_GetAttrString(spec, "name"); + if (name == NULL) { + return -1; + } + PyObject *filename = PyObject_GetAttrString(spec, "origin"); if (filename == NULL) { - goto error; + return -1; } + return _Py_ext_module_loader_info_init(p_info, name, filename); +} + + +PyObject * +_PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info, + PyObject *spec, FILE *fp) +{ + PyObject *m = NULL; + const char *name_buf = PyBytes_AS_STRING(info->name_encoded); + const char *oldcontext; + dl_funcptr exportfunc; + PyModInitFunction p0; + PyModuleDef *def; #ifdef MS_WINDOWS exportfunc = _PyImport_FindSharedFuncptrWindows( - hook_prefix, name_buf, filename, fp); + info->hook_prefix, name_buf, info->filename, fp); #else - filename_bytes = PyUnicode_EncodeFSDefault(filename); - if (filename_bytes == NULL) { - goto error; + { + const char *path_buf = PyBytes_AS_STRING(info->filename_encoded); + exportfunc = _PyImport_FindSharedFuncptr( + info->hook_prefix, name_buf, path_buf, fp); } - filename_buf = PyBytes_AS_STRING(filename_bytes); - exportfunc = _PyImport_FindSharedFuncptr( - hook_prefix, name_buf, filename_buf, fp); - Py_DECREF(filename_bytes); #endif if (exportfunc == NULL) { @@ -152,11 +203,11 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) msg = PyUnicode_FromFormat( "dynamic module does not define " "module export function (%s_%s)", - hook_prefix, name_buf); - if (msg == NULL) - goto error; - PyErr_SetImportError(msg, name_unicode, filename); - Py_DECREF(msg); + info->hook_prefix, name_buf); + if (msg != NULL) { + PyErr_SetImportError(msg, info->name, info->filename); + Py_DECREF(msg); + } } goto error; } @@ -164,7 +215,7 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) p0 = (PyModInitFunction)exportfunc; /* Package context is needed for single-phase init */ - oldcontext = _PyImport_SwapPackageContext(newcontext); + oldcontext = _PyImport_SwapPackageContext(info->newcontext); m = p0(); _PyImport_SwapPackageContext(oldcontext); @@ -195,9 +246,6 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) goto error; } if (PyObject_TypeCheck(m, &PyModuleDef_Type)) { - Py_DECREF(name_unicode); - Py_DECREF(name); - Py_DECREF(filename); return PyModule_FromDefAndSpec((PyModuleDef*)m, spec); } @@ -207,7 +255,7 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) goto error; } - if (hook_prefix == nonascii_prefix) { + if (info->hook_prefix == nonascii_prefix) { /* don't allow legacy init for non-ASCII module names */ PyErr_Format( PyExc_SystemError, @@ -227,24 +275,20 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) def->m_base.m_init = p0; /* Remember the filename as the __file__ attribute */ - if (PyModule_AddObjectRef(m, "__file__", filename) < 0) { + if (PyModule_AddObjectRef(m, "__file__", info->filename) < 0) { PyErr_Clear(); /* Not important enough to report */ } PyObject *modules = PyImport_GetModuleDict(); - if (_PyImport_FixupExtensionObject(m, name_unicode, filename, modules) < 0) + if (_PyImport_FixupExtensionObject( + m, info->name, info->filename, modules) < 0) + { goto error; - - Py_DECREF(name_unicode); - Py_DECREF(name); - Py_DECREF(filename); + } return m; error: - Py_DECREF(name_unicode); - Py_XDECREF(name); - Py_XDECREF(filename); Py_XDECREF(m); return NULL; } From 809aa9a682fc865f7502e7421da0a74d204aab6d Mon Sep 17 00:00:00 2001 From: edson duarte Date: Wed, 24 Apr 2024 16:53:52 -0300 Subject: [PATCH 040/217] gh-85453: Adapt datetime.rst to devguide recommendations for code snippets and variables (#118068) Also remove formatting from numeric literals. Co-authored-by: Serhiy Storchaka Co-authored-by: Jelle Zijlstra Co-authored-by: Erlend E. Aasland --- Doc/library/datetime.rst | 90 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index e8bd51ba20802e..84fede472eeacc 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -85,7 +85,7 @@ The :mod:`!datetime` module exports the following constants: .. data:: MINYEAR The smallest year number allowed in a :class:`date` or :class:`.datetime` object. - :const:`MINYEAR` is ``1``. + :const:`MINYEAR` is 1. .. data:: MAXYEAR @@ -207,7 +207,7 @@ A :class:`timedelta` object represents a duration, the difference between two .. class:: timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0) - All arguments are optional and default to ``0``. Arguments may be integers + All arguments are optional and default to 0. Arguments may be integers or floats, and may be positive or negative. Only *days*, *seconds* and *microseconds* are stored internally. @@ -280,7 +280,7 @@ Class attributes: The smallest possible difference between non-equal :class:`timedelta` objects, ``timedelta(microseconds=1)``. -Note that, because of normalization, ``timedelta.max`` > ``-timedelta.min``. +Note that, because of normalization, ``timedelta.max`` is greater than ``-timedelta.min``. ``-timedelta.max`` is not representable as a :class:`timedelta` object. Instance attributes (read-only): @@ -302,26 +302,27 @@ Supported operations: +--------------------------------+-----------------------------------------------+ | Operation | Result | +================================+===============================================+ -| ``t1 = t2 + t3`` | Sum of *t2* and *t3*. Afterwards *t1*-*t2* == | -| | *t3* and *t1*-*t3* == *t2* are true. (1) | +| ``t1 = t2 + t3`` | Sum of ``t2`` and ``t3``. | +| | Afterwards ``t1 - t2 == t3`` and | +| | ``t1 - t3 == t2`` are true. (1) | +--------------------------------+-----------------------------------------------+ -| ``t1 = t2 - t3`` | Difference of *t2* and *t3*. Afterwards *t1* | -| | == *t2* - *t3* and *t2* == *t1* + *t3* are | +| ``t1 = t2 - t3`` | Difference of ``t2`` and ``t3``. Afterwards | +| | ``t1 == t2 - t3`` and ``t2 == t1 + t3`` are | | | true. (1)(6) | +--------------------------------+-----------------------------------------------+ | ``t1 = t2 * i or t1 = i * t2`` | Delta multiplied by an integer. | -| | Afterwards *t1* // i == *t2* is true, | +| | Afterwards ``t1 // i == t2`` is true, | | | provided ``i != 0``. | +--------------------------------+-----------------------------------------------+ -| | In general, *t1* \* i == *t1* \* (i-1) + *t1* | +| | In general, ``t1 * i == t1 * (i-1) + t1`` | | | is true. (1) | +--------------------------------+-----------------------------------------------+ | ``t1 = t2 * f or t1 = f * t2`` | Delta multiplied by a float. The result is | | | rounded to the nearest multiple of | | | timedelta.resolution using round-half-to-even.| +--------------------------------+-----------------------------------------------+ -| ``f = t2 / t3`` | Division (3) of overall duration *t2* by | -| | interval unit *t3*. Returns a :class:`float` | +| ``f = t2 / t3`` | Division (3) of overall duration ``t2`` by | +| | interval unit ``t3``. Returns a :class:`float`| | | object. | +--------------------------------+-----------------------------------------------+ | ``t1 = t2 / f or t1 = t2 / i`` | Delta divided by a float or an int. The result| @@ -343,13 +344,12 @@ Supported operations: | ``+t1`` | Returns a :class:`timedelta` object with the | | | same value. (2) | +--------------------------------+-----------------------------------------------+ -| ``-t1`` | equivalent to | -| | :class:`timedelta`\ (-*t1.days*, | -| | -*t1.seconds*, -*t1.microseconds*), | -| | and to *t1*\* -1. (1)(4) | +| ``-t1`` | Equivalent to ``timedelta(-t1.days, | +| | -t1.seconds*, -t1.microseconds)``, | +| | and to ``t1 * -1``. (1)(4) | +--------------------------------+-----------------------------------------------+ -| ``abs(t)`` | equivalent to +\ *t* when ``t.days >= 0``, | -| | and to -*t* when ``t.days < 0``. (2) | +| ``abs(t)`` | Equivalent to ``+t`` when ``t.days >= 0``, | +| | and to ``-t`` when ``t.days < 0``. (2) | +--------------------------------+-----------------------------------------------+ | ``str(t)`` | Returns a string in the form | | | ``[D day[s], ][H]H:MM:SS[.UUUUUU]``, where D | @@ -370,10 +370,10 @@ Notes: This is exact and cannot overflow. (3) - Division by 0 raises :exc:`ZeroDivisionError`. + Division by zero raises :exc:`ZeroDivisionError`. (4) - -*timedelta.max* is not representable as a :class:`timedelta` object. + ``-timedelta.max`` is not representable as a :class:`timedelta` object. (5) String representations of :class:`timedelta` objects are normalized @@ -583,10 +583,10 @@ Supported operations: +-------------------------------+----------------------------------------------+ | Operation | Result | +===============================+==============================================+ -| ``date2 = date1 + timedelta`` | *date2* will be ``timedelta.days`` days | -| | after *date1*. (1) | +| ``date2 = date1 + timedelta`` | ``date2`` will be ``timedelta.days`` days | +| | after ``date1``. (1) | +-------------------------------+----------------------------------------------+ -| ``date2 = date1 - timedelta`` | Computes *date2* such that ``date2 + | +| ``date2 = date1 - timedelta`` | Computes ``date2`` such that ``date2 + | | | timedelta == date1``. (2) | +-------------------------------+----------------------------------------------+ | ``timedelta = date1 - date2`` | \(3) | @@ -613,8 +613,8 @@ Notes: ``timedelta.seconds`` and ``timedelta.microseconds`` are ignored. (3) - This is exact, and cannot overflow. timedelta.seconds and - timedelta.microseconds are 0, and date2 + timedelta == date1 after. + This is exact, and cannot overflow. ``timedelta.seconds`` and + ``timedelta.microseconds`` are 0, and ``date2 + timedelta == date1`` after. (4) :class:`date` objects are equal if they represent the same date. @@ -671,7 +671,7 @@ Instance methods: time.struct_time((d.year, d.month, d.day, 0, 0, 0, d.weekday(), yday, -1)) where ``yday = d.toordinal() - date(d.year, 1, 1).toordinal() + 1`` - is the day number within the current year starting with ``1`` for January 1st. + is the day number within the current year starting with 1 for January 1st. .. method:: date.toordinal() @@ -991,8 +991,8 @@ Other constructors, all class methods: .. classmethod:: datetime.fromordinal(ordinal) Return the :class:`.datetime` corresponding to the proleptic Gregorian ordinal, - where January 1 of year 1 has ordinal 1. :exc:`ValueError` is raised unless ``1 - <= ordinal <= datetime.max.toordinal()``. The hour, minute, second and + where January 1 of year 1 has ordinal 1. :exc:`ValueError` is raised unless + ``1 <= ordinal <= datetime.max.toordinal()``. The hour, minute, second and microsecond of the result are all 0, and :attr:`.tzinfo` is ``None``. @@ -1167,8 +1167,8 @@ Instance attributes (read-only): In ``[0, 1]``. Used to disambiguate wall times during a repeated interval. (A repeated interval occurs when clocks are rolled back at the end of daylight saving time or when the UTC offset for the current zone is decreased for political reasons.) - The value 0 (1) represents the earlier (later) of the two moments with the same wall - time representation. + The values 0 and 1 represent, respectively, the earlier and later of the two + moments with the same wall time representation. .. versionadded:: 3.6 @@ -1193,16 +1193,16 @@ Supported operations: +---------------------------------------+--------------------------------+ (1) - datetime2 is a duration of timedelta removed from datetime1, moving forward in - time if ``timedelta.days`` > 0, or backward if ``timedelta.days`` < 0. The + ``datetime2`` is a duration of ``timedelta`` removed from ``datetime1``, moving forward in + time if ``timedelta.days > 0``, or backward if ``timedelta.days < 0``. The result has the same :attr:`~.datetime.tzinfo` attribute as the input datetime, and - datetime2 - datetime1 == timedelta after. :exc:`OverflowError` is raised if - datetime2.year would be smaller than :const:`MINYEAR` or larger than + ``datetime2 - datetime1 == timedelta`` after. :exc:`OverflowError` is raised if + ``datetime2.year`` would be smaller than :const:`MINYEAR` or larger than :const:`MAXYEAR`. Note that no time zone adjustments are done even if the input is an aware object. (2) - Computes the datetime2 such that datetime2 + timedelta == datetime1. As for + Computes the ``datetime2`` such that ``datetime2 + timedelta == datetime1``. As for addition, the result has the same :attr:`~.datetime.tzinfo` attribute as the input datetime, and no time zone adjustments are done even if the input is aware. @@ -1387,12 +1387,12 @@ Instance methods: d.weekday(), yday, dst)) where ``yday = d.toordinal() - date(d.year, 1, 1).toordinal() + 1`` - is the day number within the current year starting with ``1`` for January + is the day number within the current year starting with 1 for January 1st. The :attr:`~time.struct_time.tm_isdst` flag of the result is set according to the :meth:`dst` method: :attr:`.tzinfo` is ``None`` or :meth:`dst` returns ``None``, :attr:`!tm_isdst` is set to ``-1``; else if :meth:`dst` returns a - non-zero value, :attr:`!tm_isdst` is set to ``1``; else :attr:`!tm_isdst` is - set to ``0``. + non-zero value, :attr:`!tm_isdst` is set to 1; else :attr:`!tm_isdst` is + set to 0. .. method:: datetime.utctimetuple() @@ -1404,7 +1404,7 @@ Instance methods: If *d* is aware, *d* is normalized to UTC time, by subtracting ``d.utcoffset()``, and a :class:`time.struct_time` for the normalized time is returned. :attr:`!tm_isdst` is forced to 0. Note - that an :exc:`OverflowError` may be raised if *d*.year was + that an :exc:`OverflowError` may be raised if ``d.year`` was ``MINYEAR`` or ``MAXYEAR`` and UTC adjustment spills over a year boundary. @@ -1735,7 +1735,7 @@ day, and subject to adjustment via a :class:`tzinfo` object. * ``fold in [0, 1]``. If an argument outside those ranges is given, :exc:`ValueError` is raised. All - default to ``0`` except *tzinfo*, which defaults to :const:`None`. + default to 0 except *tzinfo*, which defaults to :const:`None`. Class attributes: @@ -1790,8 +1790,8 @@ Instance attributes (read-only): In ``[0, 1]``. Used to disambiguate wall times during a repeated interval. (A repeated interval occurs when clocks are rolled back at the end of daylight saving time or when the UTC offset for the current zone is decreased for political reasons.) - The value 0 (1) represents the earlier (later) of the two moments with the same wall - time representation. + The values 0 and 1 represent, respectively, the earlier and later of the two + moments with the same wall time representation. .. versionadded:: 3.6 @@ -2083,7 +2083,7 @@ Examples of working with a :class:`.time` object:: ``tz.utcoffset(dt) - tz.dst(dt)`` must return the same result for every :class:`.datetime` *dt* with ``dt.tzinfo == - tz`` For sane :class:`tzinfo` subclasses, this expression yields the time + tz``. For sane :class:`tzinfo` subclasses, this expression yields the time zone's "standard offset", which should not depend on the date or the time, but only on geographic location. The implementation of :meth:`datetime.astimezone` relies on this, but cannot detect violations; it's the programmer's @@ -2120,7 +2120,7 @@ Examples of working with a :class:`.time` object:: Return the time zone name corresponding to the :class:`.datetime` object *dt*, as a string. Nothing about string names is defined by the :mod:`!datetime` module, and there's no requirement that it mean anything in particular. For example, - "GMT", "UTC", "-500", "-5:00", "EDT", "US/Eastern", "America/New York" are all + ``"GMT"``, ``"UTC"``, ``"-500"``, ``"-5:00"``, ``"EDT"``, ``"US/Eastern"``, ``"America/New York"`` are all valid replies. Return ``None`` if a string name isn't known. Note that this is a method rather than a fixed string primarily because some :class:`tzinfo` subclasses will wish to return different names depending on the specific value @@ -2561,11 +2561,11 @@ information, which are supported in ``datetime.strptime`` but are discarded by For :class:`.time` objects, the format codes for year, month, and day should not be used, as :class:`!time` objects have no such values. If they're used anyway, -``1900`` is substituted for the year, and ``1`` for the month and day. +``1900`` is substituted for the year, and 1 for the month and day. For :class:`date` objects, the format codes for hours, minutes, seconds, and microseconds should not be used, as :class:`date` objects have no such -values. If they're used anyway, ``0`` is substituted for them. +values. If they're used anyway, 0 is substituted for them. For the same reason, handling of format strings containing Unicode code points that can't be represented in the charset of the current locale is also From 59a4d52973ca73bd739f914e88243a31dbef6b32 Mon Sep 17 00:00:00 2001 From: edson duarte Date: Wed, 24 Apr 2024 17:19:54 -0300 Subject: [PATCH 041/217] gh-85453: Make numeric literals consistent across datetime.rst (#118245) Remove code formatting from remaining numeric literals. --- Doc/library/datetime.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 84fede472eeacc..b1b5e04c74b066 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -91,7 +91,7 @@ The :mod:`!datetime` module exports the following constants: .. data:: MAXYEAR The largest year number allowed in a :class:`date` or :class:`.datetime` object. - :const:`MAXYEAR` is ``9999``. + :const:`MAXYEAR` is 9999. .. attribute:: UTC @@ -2561,7 +2561,7 @@ information, which are supported in ``datetime.strptime`` but are discarded by For :class:`.time` objects, the format codes for year, month, and day should not be used, as :class:`!time` objects have no such values. If they're used anyway, -``1900`` is substituted for the year, and 1 for the month and day. +1900 is substituted for the year, and 1 for the month and day. For :class:`date` objects, the format codes for hours, minutes, seconds, and microseconds should not be used, as :class:`date` objects have no such @@ -2708,4 +2708,4 @@ Notes: `_ for a good explanation. -.. [#] Passing ``datetime.strptime('Feb 29', '%b %d')`` will fail since ``1900`` is not a leap year. +.. [#] Passing ``datetime.strptime('Feb 29', '%b %d')`` will fail since 1900 is not a leap year. From 345e1e04ec72698a1e257c805b3840d9f55eb80d Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 24 Apr 2024 21:25:22 +0100 Subject: [PATCH 042/217] gh-112730: Make the test suite resilient to color-activation environment variables (#117672) --- .github/workflows/reusable-ubuntu.yml | 1 + Lib/doctest.py | 10 +++++++++- Lib/idlelib/idle_test/test_run.py | 3 +++ Lib/test/support/__init__.py | 20 ++++++++++++++++++++ Lib/test/test_cmd_line.py | 2 ++ Lib/test/test_exceptions.py | 7 ++++++- Lib/test/test_interpreters/test_api.py | 2 ++ Lib/test/test_sys.py | 5 +++++ Lib/test/test_threading.py | 3 +++ Lib/test/test_traceback.py | 21 ++++++++++++++++++--- Lib/test/test_tracemalloc.py | 2 +- Lib/test/test_warnings/__init__.py | 3 +++ Lib/traceback.py | 26 ++++++++++++++++---------- 13 files changed, 89 insertions(+), 16 deletions(-) diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml index ee64fe62a0bd0a..e6fbaaf74c5a4b 100644 --- a/.github/workflows/reusable-ubuntu.yml +++ b/.github/workflows/reusable-ubuntu.yml @@ -14,6 +14,7 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-20.04 env: + FORCE_COLOR: 1 OPENSSL_VER: 3.0.13 PYTHONSTRICTEXTENSIONBUILD: 1 steps: diff --git a/Lib/doctest.py b/Lib/doctest.py index a3b42fdfb12254..d8c632e47e7b7a 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1556,7 +1556,11 @@ def out(s): # Make sure sys.displayhook just prints the value to stdout save_displayhook = sys.displayhook sys.displayhook = sys.__displayhook__ - + saved_can_colorize = traceback._can_colorize + traceback._can_colorize = lambda: False + color_variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None} + for key in color_variables: + color_variables[key] = os.environ.pop(key, None) try: return self.__run(test, compileflags, out) finally: @@ -1565,6 +1569,10 @@ def out(s): sys.settrace(save_trace) linecache.getlines = self.save_linecache_getlines sys.displayhook = save_displayhook + traceback._can_colorize = saved_can_colorize + for key, value in color_variables.items(): + if value is not None: + os.environ[key] = value if clear_globs: test.globs.clear() import builtins diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index a38e43dcb9d1c4..83ecbffa2a197e 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -8,6 +8,7 @@ from unittest import mock import idlelib from idlelib.idle_test.mock_idle import Func +from test.support import force_not_colorized idlelib.testing = True # Use {} for executing test user code. @@ -46,6 +47,7 @@ def __eq__(self, other): "Did you mean: 'real'?\n"), ) + @force_not_colorized def test_get_message(self): for code, exc, msg in self.data: with self.subTest(code=code): @@ -57,6 +59,7 @@ def test_get_message(self): expect = f'{exc.__name__}: {msg}' self.assertEqual(actual, expect) + @force_not_colorized @mock.patch.object(run, 'cleanup_traceback', new_callable=lambda: (lambda t, e: None)) def test_get_multiple_message(self, mock): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 6eb0f84b02ea22..ea4945466cac82 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -59,6 +59,7 @@ "Py_DEBUG", "exceeds_recursion_limit", "get_c_recursion_limit", "skip_on_s390x", "without_optimizer", + "force_not_colorized" ] @@ -2557,3 +2558,22 @@ def copy_python_src_ignore(path, names): 'build', } return ignored + +def force_not_colorized(func): + """Force the terminal not to be colorized.""" + @functools.wraps(func) + def wrapper(*args, **kwargs): + import traceback + original_fn = traceback._can_colorize + variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None} + try: + for key in variables: + variables[key] = os.environ.pop(key, None) + traceback._can_colorize = lambda: False + return func(*args, **kwargs) + finally: + traceback._can_colorize = original_fn + for key, value in variables.items(): + if value is not None: + os.environ[key] = value + return wrapper diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index fb832aed3152ff..9624d35d0c3948 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -10,6 +10,7 @@ import unittest from test import support from test.support import os_helper +from test.support import force_not_colorized from test.support.script_helper import ( spawn_python, kill_python, assert_python_ok, assert_python_failure, interpreter_requires_environment @@ -1027,6 +1028,7 @@ def test_sys_flags_not_set(self): class SyntaxErrorTests(unittest.TestCase): + @force_not_colorized def check_string(self, code): proc = subprocess.run([sys.executable, "-"], input=code, stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 1224f143b5441f..3138f50076f1df 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -12,7 +12,8 @@ from test.support import (captured_stderr, check_impl_detail, cpython_only, gc_collect, no_tracing, script_helper, - SuppressCrashReport) + SuppressCrashReport, + force_not_colorized) from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink from test.support.warnings_helper import check_warnings @@ -41,6 +42,7 @@ def __str__(self): # XXX This is not really enough, each *operation* should be tested! + class ExceptionTests(unittest.TestCase): def raise_catch(self, exc, excname): @@ -1994,6 +1996,7 @@ def write_source(self, source): _rc, _out, err = script_helper.assert_python_failure('-Wd', '-X', 'utf8', TESTFN) return err.decode('utf-8').splitlines() + @force_not_colorized def test_assertion_error_location(self): cases = [ ('assert None', @@ -2070,6 +2073,7 @@ def test_assertion_error_location(self): result = self.write_source(source) self.assertEqual(result[-3:], expected) + @force_not_colorized def test_multiline_not_highlighted(self): cases = [ (""" @@ -2102,6 +2106,7 @@ def test_multiline_not_highlighted(self): class SyntaxErrorTests(unittest.TestCase): + @force_not_colorized def test_range_of_offsets(self): cases = [ # Basic range from 2->7 diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py index 0039fa46496c53..719c1c721cad7c 100644 --- a/Lib/test/test_interpreters/test_api.py +++ b/Lib/test/test_interpreters/test_api.py @@ -12,6 +12,7 @@ _interpreters = import_helper.import_module('_interpreters') from test.support import Py_GIL_DISABLED from test.support import interpreters +from test.support import force_not_colorized from test.support.interpreters import ( InterpreterError, InterpreterNotFoundError, ExecutionFailed, ) @@ -735,6 +736,7 @@ def test_failure(self): with self.assertRaises(ExecutionFailed): interp.exec('raise Exception') + @force_not_colorized def test_display_preserved_exception(self): tempdir = self.temp_dir() modfile = self.make_module('spam', tempdir, text=""" diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ab26bf56d9ced9..14ec51eb757e00 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -16,6 +16,7 @@ from test.support.script_helper import assert_python_ok, assert_python_failure from test.support import threading_helper from test.support import import_helper +from test.support import force_not_colorized try: from test.support import interpreters except ImportError: @@ -145,6 +146,7 @@ def f(): class ExceptHookTest(unittest.TestCase): + @force_not_colorized def test_original_excepthook(self): try: raise ValueError(42) @@ -156,6 +158,7 @@ def test_original_excepthook(self): self.assertRaises(TypeError, sys.__excepthook__) + @force_not_colorized def test_excepthook_bytes_filename(self): # bpo-37467: sys.excepthook() must not crash if a filename # is a bytes string @@ -793,6 +796,7 @@ def test_sys_getwindowsversion_no_instantiation(self): def test_clear_type_cache(self): sys._clear_type_cache() + @force_not_colorized @support.requires_subprocess() def test_ioencoding(self): env = dict(os.environ) @@ -1108,6 +1112,7 @@ def test_getandroidapilevel(self): self.assertIsInstance(level, int) self.assertGreater(level, 0) + @force_not_colorized @support.requires_subprocess() def test_sys_tracebacklimit(self): code = """if 1: diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index a712ed10f022d6..362a3f9c4a01d1 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -7,6 +7,7 @@ from test.support import verbose, cpython_only, os_helper from test.support.import_helper import import_module from test.support.script_helper import assert_python_ok, assert_python_failure +from test.support import force_not_colorized import random import sys @@ -1793,6 +1794,7 @@ def setUp(self): restore_default_excepthook(self) super().setUp() + @force_not_colorized def test_excepthook(self): with support.captured_output("stderr") as stderr: thread = ThreadRunFail(name="excepthook thread") @@ -1806,6 +1808,7 @@ def test_excepthook(self): self.assertIn('ValueError: run failed', stderr) @support.cpython_only + @force_not_colorized def test_excepthook_thread_None(self): # threading.excepthook called with thread=None: log the thread # identifier in this case. diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index dd9b1850adf086..19611937fc278b 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -21,6 +21,7 @@ from test.support.os_helper import TESTFN, unlink from test.support.script_helper import assert_python_ok, assert_python_failure from test.support.import_helper import forget +from test.support import force_not_colorized import json import textwrap @@ -39,6 +40,13 @@ LEVENSHTEIN_DATA_FILE = Path(__file__).parent / 'levenshtein_examples.json' +ORIGINAL_CAN_COLORIZE = traceback._can_colorize + +def setUpModule(): + traceback._can_colorize = lambda: False + +def tearDownModule(): + traceback._can_colorize = ORIGINAL_CAN_COLORIZE class TracebackCases(unittest.TestCase): # For now, a very minimal set of tests. I want to be sure that @@ -124,6 +132,7 @@ def test_nocaret(self): self.assertEqual(len(err), 3) self.assertEqual(err[1].strip(), "bad syntax") + @force_not_colorized def test_no_caret_with_no_debug_ranges_flag(self): # Make sure that if `-X no_debug_ranges` is used, there are no carets # in the traceback. @@ -401,7 +410,7 @@ def do_test(firstlines, message, charset, lineno): """.format(firstlines, message)) process = subprocess.Popen([sys.executable, TESTFN], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env={}) stdout, stderr = process.communicate() stdout = stdout.decode(output_encoding).splitlines() finally: @@ -4354,13 +4363,18 @@ def foo(): f'{boldm}ZeroDivisionError{reset}: {magenta}division by zero{reset}'] self.assertEqual(actual, expected) + @force_not_colorized def test_colorized_detection_checks_for_environment_variables(self): if sys.platform == "win32": virtual_patching = unittest.mock.patch("nt._supports_virtual_terminal", return_value=True) else: virtual_patching = contextlib.nullcontext() with virtual_patching: - with unittest.mock.patch("os.isatty") as isatty_mock: + + flags = unittest.mock.MagicMock(ignore_environment=False) + with (unittest.mock.patch("os.isatty") as isatty_mock, + unittest.mock.patch("sys.flags", flags), + unittest.mock.patch("traceback._can_colorize", ORIGINAL_CAN_COLORIZE)): isatty_mock.return_value = True with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): self.assertEqual(traceback._can_colorize(), False) @@ -4379,7 +4393,8 @@ def test_colorized_detection_checks_for_environment_variables(self): with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): self.assertEqual(traceback._can_colorize(), False) isatty_mock.return_value = False - self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {}): + self.assertEqual(traceback._can_colorize(), False) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index bea124521032d1..f685430a7d36ad 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -942,7 +942,7 @@ def check_env_var_invalid(self, nframe): with support.SuppressCrashReport(): ok, stdout, stderr = assert_python_failure( '-c', 'pass', - PYTHONTRACEMALLOC=str(nframe)) + PYTHONTRACEMALLOC=str(nframe), __cleanenv=True) if b'ValueError: the number of frames must be in range' in stderr: return diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index b768631846e240..4416ed0f3ed3ef 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -12,6 +12,7 @@ from test.support import import_helper from test.support import os_helper from test.support import warnings_helper +from test.support import force_not_colorized from test.support.script_helper import assert_python_ok, assert_python_failure from test.test_warnings.data import package_helper @@ -1239,6 +1240,7 @@ def test_comma_separated_warnings(self): self.assertEqual(stdout, b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']") + @force_not_colorized def test_envvar_and_command_line(self): rc, stdout, stderr = assert_python_ok("-Wignore::UnicodeWarning", "-c", "import sys; sys.stdout.write(str(sys.warnoptions))", @@ -1247,6 +1249,7 @@ def test_envvar_and_command_line(self): self.assertEqual(stdout, b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']") + @force_not_colorized def test_conflicting_envvar_and_command_line(self): rc, stdout, stderr = assert_python_failure("-Werror::DeprecationWarning", "-c", "import sys, warnings; sys.stdout.write(str(sys.warnoptions)); " diff --git a/Lib/traceback.py b/Lib/traceback.py index 054def57c21482..fccec0c71c3695 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -141,24 +141,30 @@ def _can_colorize(): return False except (ImportError, AttributeError): return False - - if os.environ.get("PYTHON_COLORS") == "0": - return False - if os.environ.get("PYTHON_COLORS") == "1": - return True - if "NO_COLOR" in os.environ: - return False + if not sys.flags.ignore_environment: + if os.environ.get("PYTHON_COLORS") == "0": + return False + if os.environ.get("PYTHON_COLORS") == "1": + return True + if "NO_COLOR" in os.environ: + return False if not _COLORIZE: return False - if "FORCE_COLOR" in os.environ: - return True - if os.environ.get("TERM") == "dumb": + if not sys.flags.ignore_environment: + if "FORCE_COLOR" in os.environ: + return True + if os.environ.get("TERM") == "dumb": + return False + + if not hasattr(sys.stderr, "fileno"): return False + try: return os.isatty(sys.stderr.fileno()) except io.UnsupportedOperation: return sys.stderr.isatty() + def _print_exception_bltin(exc, /): file = sys.stderr if sys.stderr is not None else sys.__stderr__ colorize = _can_colorize() From 85ec1c2dc67c2a506e847dbe2c3c740e81c3ab9b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 24 Apr 2024 15:23:45 -0600 Subject: [PATCH 043/217] gh-117953: Fix Refleaks Introduced by gh-118194 (gh-118250) A couple of refleaks slipped through in gh-118194. This takes care of them. (AKA _Py_ext_module_loader_info_init() does not steal references.) --- Python/importdl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Python/importdl.c b/Python/importdl.c index 65370249493325..f2ad95fbbb507d 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -169,9 +169,13 @@ _Py_ext_module_loader_info_init_from_spec( } PyObject *filename = PyObject_GetAttrString(spec, "origin"); if (filename == NULL) { + Py_DECREF(name); return -1; } - return _Py_ext_module_loader_info_init(p_info, name, filename); + int err = _Py_ext_module_loader_info_init(p_info, name, filename); + Py_DECREF(name); + Py_DECREF(filename); + return err; } From 93b7ed7c6b1494f41818fa571b1843ca3dfe1bd1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 25 Apr 2024 00:39:54 +0300 Subject: [PATCH 044/217] gh-108191: Add support of positional argument in SimpleNamespace constructor (GH-108195) SimpleNamespace({'a': 1, 'b': 2}) and SimpleNamespace([('a', 1), ('b', 2)]) are now the same as SimpleNamespace(a=1, b=2). --- Doc/library/types.rst | 22 ++++++-- Doc/whatsnew/3.13.rst | 8 +++ Lib/test/test_types.py | 55 ++++++++++++++----- ...-08-21-10-34-43.gh-issue-108191.GZM3mv.rst | 3 + Objects/namespaceobject.c | 24 +++++++- 5 files changed, 92 insertions(+), 20 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-21-10-34-43.gh-issue-108191.GZM3mv.rst diff --git a/Doc/library/types.rst b/Doc/library/types.rst index b856544e44207c..89bc0a600c0af8 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -481,14 +481,25 @@ Additional Utility Classes and Functions A simple :class:`object` subclass that provides attribute access to its namespace, as well as a meaningful repr. - Unlike :class:`object`, with ``SimpleNamespace`` you can add and remove - attributes. If a ``SimpleNamespace`` object is initialized with keyword - arguments, those are directly added to the underlying namespace. + Unlike :class:`object`, with :class:`!SimpleNamespace` you can add and remove + attributes. + + :py:class:`SimpleNamespace` objects may be initialized + in the same way as :class:`dict`: either with keyword arguments, + with a single positional argument, or with both. + When initialized with keyword arguments, + those are directly added to the underlying namespace. + Alternatively, when initialized with a positional argument, + the underlying namespace will be updated with key-value pairs + from that argument (either a mapping object or + an :term:`iterable` object producing key-value pairs). + All such keys must be strings. The type is roughly equivalent to the following code:: class SimpleNamespace: - def __init__(self, /, **kwargs): + def __init__(self, mapping_or_iterable=(), /, **kwargs): + self.__dict__.update(mapping_or_iterable) self.__dict__.update(kwargs) def __repr__(self): @@ -512,6 +523,9 @@ Additional Utility Classes and Functions Attribute order in the repr changed from alphabetical to insertion (like ``dict``). + .. versionchanged:: 3.13 + Added support for an optional positional argument. + .. function:: DynamicClassAttribute(fget=None, fset=None, fdel=None, doc=None) Route attribute access on a class to __getattr__. diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 89694afdfa3fec..ad107aad5db3bd 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -804,6 +804,14 @@ traceback ``True``) to indicate whether ``exc_type`` should be saved. (Contributed by Irit Katriel in :gh:`112332`.) +types +----- + +* :class:`~types.SimpleNamespace` constructor now allows specifying initial + values of attributes as a positional argument which must be a mapping or + an iterable of key-value pairs. + (Contributed by Serhiy Storchaka in :gh:`108191`.) + typing ------ diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 16985122bc0219..fbca198aab5180 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -2,7 +2,7 @@ from test.support import run_with_locale, cpython_only, MISSING_C_DOCSTRINGS import collections.abc -from collections import namedtuple +from collections import namedtuple, UserDict import copy import _datetime import gc @@ -1755,21 +1755,50 @@ class Model(metaclass=ModelBase): class SimpleNamespaceTests(unittest.TestCase): def test_constructor(self): - ns1 = types.SimpleNamespace() - ns2 = types.SimpleNamespace(x=1, y=2) - ns3 = types.SimpleNamespace(**dict(x=1, y=2)) + def check(ns, expected): + self.assertEqual(len(ns.__dict__), len(expected)) + self.assertEqual(vars(ns), expected) + # check order + self.assertEqual(list(vars(ns).items()), list(expected.items())) + for name in expected: + self.assertEqual(getattr(ns, name), expected[name]) + + check(types.SimpleNamespace(), {}) + check(types.SimpleNamespace(x=1, y=2), {'x': 1, 'y': 2}) + check(types.SimpleNamespace(**dict(x=1, y=2)), {'x': 1, 'y': 2}) + check(types.SimpleNamespace({'x': 1, 'y': 2}, x=4, z=3), + {'x': 4, 'y': 2, 'z': 3}) + check(types.SimpleNamespace([['x', 1], ['y', 2]], x=4, z=3), + {'x': 4, 'y': 2, 'z': 3}) + check(types.SimpleNamespace(UserDict({'x': 1, 'y': 2}), x=4, z=3), + {'x': 4, 'y': 2, 'z': 3}) + check(types.SimpleNamespace({'x': 1, 'y': 2}), {'x': 1, 'y': 2}) + check(types.SimpleNamespace([['x', 1], ['y', 2]]), {'x': 1, 'y': 2}) + check(types.SimpleNamespace([], x=4, z=3), {'x': 4, 'z': 3}) + check(types.SimpleNamespace({}, x=4, z=3), {'x': 4, 'z': 3}) + check(types.SimpleNamespace([]), {}) + check(types.SimpleNamespace({}), {}) with self.assertRaises(TypeError): - types.SimpleNamespace(1, 2, 3) + types.SimpleNamespace([], []) # too many positional arguments with self.assertRaises(TypeError): - types.SimpleNamespace(**{1: 2}) - - self.assertEqual(len(ns1.__dict__), 0) - self.assertEqual(vars(ns1), {}) - self.assertEqual(len(ns2.__dict__), 2) - self.assertEqual(vars(ns2), {'y': 2, 'x': 1}) - self.assertEqual(len(ns3.__dict__), 2) - self.assertEqual(vars(ns3), {'y': 2, 'x': 1}) + types.SimpleNamespace(1) # not a mapping or iterable + with self.assertRaises(TypeError): + types.SimpleNamespace([1]) # non-iterable + with self.assertRaises(ValueError): + types.SimpleNamespace([['x']]) # not a pair + with self.assertRaises(ValueError): + types.SimpleNamespace([['x', 'y', 'z']]) + with self.assertRaises(TypeError): + types.SimpleNamespace(**{1: 2}) # non-string key + with self.assertRaises(TypeError): + types.SimpleNamespace({1: 2}) + with self.assertRaises(TypeError): + types.SimpleNamespace([[1, 2]]) + with self.assertRaises(TypeError): + types.SimpleNamespace(UserDict({1: 2})) + with self.assertRaises(TypeError): + types.SimpleNamespace([[[], 2]]) # non-hashable key def test_unbound(self): ns1 = vars(types.SimpleNamespace()) diff --git a/Misc/NEWS.d/next/Library/2023-08-21-10-34-43.gh-issue-108191.GZM3mv.rst b/Misc/NEWS.d/next/Library/2023-08-21-10-34-43.gh-issue-108191.GZM3mv.rst new file mode 100644 index 00000000000000..da4ce5742549e6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-21-10-34-43.gh-issue-108191.GZM3mv.rst @@ -0,0 +1,3 @@ +The :class:`types.SimpleNamespace` now accepts an optional positional +argument which specifies initial values of attributes as a dict or an +iterable of key-value pairs. diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c index b2a224b9b2bda5..5b7547103a2b3f 100644 --- a/Objects/namespaceobject.c +++ b/Objects/namespaceobject.c @@ -43,10 +43,28 @@ namespace_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static int namespace_init(_PyNamespaceObject *ns, PyObject *args, PyObject *kwds) { - if (PyTuple_GET_SIZE(args) != 0) { - PyErr_Format(PyExc_TypeError, "no positional arguments expected"); + PyObject *arg = NULL; + if (!PyArg_UnpackTuple(args, _PyType_Name(Py_TYPE(ns)), 0, 1, &arg)) { return -1; } + if (arg != NULL) { + PyObject *dict; + if (PyDict_CheckExact(arg)) { + dict = Py_NewRef(arg); + } + else { + dict = PyObject_CallOneArg((PyObject *)&PyDict_Type, arg); + if (dict == NULL) { + return -1; + } + } + int err = (!PyArg_ValidateKeywordArguments(dict) || + PyDict_Update(ns->ns_dict, dict) < 0); + Py_DECREF(dict); + if (err) { + return -1; + } + } if (kwds == NULL) { return 0; } @@ -227,7 +245,7 @@ static PyMethodDef namespace_methods[] = { PyDoc_STRVAR(namespace_doc, -"SimpleNamespace(**kwargs)\n\ +"SimpleNamespace(mapping_or_iterable=(), /, **kwargs)\n\ --\n\n\ A simple attribute-based namespace."); From 8942bf41dac49149a77f5396ab086d340de9c009 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 24 Apr 2024 14:43:50 -0700 Subject: [PATCH 045/217] GH-118246: Exclude test_pathlib and test_posixpath from emulated JIT CI (GH-118247) --- .github/workflows/jit.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 490005b7170e95..b37eb727955909 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -76,13 +76,13 @@ jobs: runner: ubuntu-latest compiler: gcc # These fail because of emulation, not because of the JIT: - exclude: test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv test_external_inspection + exclude: test_pathlib test_posixpath test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv test_external_inspection - target: aarch64-unknown-linux-gnu/clang architecture: aarch64 runner: ubuntu-latest compiler: clang # These fail because of emulation, not because of the JIT: - exclude: test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv test_external_inspection + exclude: test_pathlib test_posixpath test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv test_external_inspection env: CC: ${{ matrix.compiler }} steps: From 4b10e209c76f9f36f8ae2e4d713b3a01591c1856 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 24 Apr 2024 23:00:55 +0100 Subject: [PATCH 046/217] gh-117786: Fix venv created from Windows Store install by restoring __PYVENV_LAUNCHER__ smuggling (GH-117814) --- Lib/test/test_embed.py | 3 +++ ...-04-12-13-18-42.gh-issue-117786.LpI01s.rst | 2 ++ Modules/getpath.py | 24 +++++++++++-------- PC/venvlauncher.c | 4 ++-- 4 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-04-12-13-18-42.gh-issue-117786.LpI01s.rst diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index ec928f935655f9..d94c63a13b8ea4 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -747,6 +747,9 @@ def check_config(self, configs, expected): if value is self.IGNORE_CONFIG: config.pop(key, None) del expected[key] + # Resolve bool/int mismatches to reduce noise in diffs + if isinstance(value, (bool, int)) and isinstance(config.get(key), (bool, int)): + expected[key] = type(config[key])(expected[key]) self.assertEqual(config, expected) def check_global_config(self, configs): diff --git a/Misc/NEWS.d/next/Windows/2024-04-12-13-18-42.gh-issue-117786.LpI01s.rst b/Misc/NEWS.d/next/Windows/2024-04-12-13-18-42.gh-issue-117786.LpI01s.rst new file mode 100644 index 00000000000000..a4cd9a9adb3e59 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-04-12-13-18-42.gh-issue-117786.LpI01s.rst @@ -0,0 +1,2 @@ +Fixes virtual environments not correctly launching when created from a Store +install. diff --git a/Modules/getpath.py b/Modules/getpath.py index 1410ffdbed8c70..bc7053224aaf16 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -310,7 +310,10 @@ def search_up(prefix, *landmarks, test=isfile): # and should not affect base_executable. base_executable = f"{dirname(library)}/bin/python{VERSION_MAJOR}.{VERSION_MINOR}" else: - base_executable = executable + # Use the real executable as our base, or argv[0] otherwise + # (on Windows, argv[0] is likely to be ENV___PYVENV_LAUNCHER__; on + # other platforms, real_executable is likely to be empty) + base_executable = real_executable or executable if not real_executable: real_executable = base_executable @@ -408,13 +411,14 @@ def search_up(prefix, *landmarks, test=isfile): if not real_executable: real_executable = base_executable -try: - real_executable = realpath(real_executable) -except OSError as ex: - # Only warn if the file actually exists and was unresolvable - # Otherwise users who specify a fake executable may get spurious warnings. - if isfile(real_executable): - warn(f'Failed to find real location of {base_executable}') +if real_executable: + try: + real_executable = realpath(real_executable) + except OSError as ex: + # Only warn if the file actually exists and was unresolvable + # Otherwise users who specify a fake executable may get spurious warnings. + if isfile(real_executable): + warn(f'Failed to find real location of {base_executable}') if not executable_dir and os_name == 'darwin' and library: # QUIRK: macOS checks adjacent to its library early @@ -427,12 +431,12 @@ def search_up(prefix, *landmarks, test=isfile): # If we do not have the executable's directory, we can calculate it. # This is the directory used to find prefix/exec_prefix if necessary. -if not executable_dir: +if not executable_dir and real_executable: executable_dir = real_executable_dir = dirname(real_executable) # If we do not have the real executable's directory, we calculate it. # This is the directory used to detect build layouts. -if not real_executable_dir: +if not real_executable_dir and real_executable: real_executable_dir = dirname(real_executable) # ****************************************************************************** diff --git a/PC/venvlauncher.c b/PC/venvlauncher.c index fe97d32e93b5f6..b1c8d0763d8c76 100644 --- a/PC/venvlauncher.c +++ b/PC/venvlauncher.c @@ -484,8 +484,8 @@ process(int argc, wchar_t ** argv) // We do not update argv[0] to point at the target runtime, and so we do not // pass through our original argv[0] in an environment variable. - //exitCode = smuggle_path(); - //if (exitCode) return exitCode; + exitCode = smuggle_path(); + if (exitCode) return exitCode; exitCode = launch(home_path, GetCommandLineW()); return exitCode; From 546cbcfa0eeeb533950bd49e30423f3d3bbd5ebe Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 25 Apr 2024 08:00:42 +0300 Subject: [PATCH 047/217] gh-117968: Make the test for closed file more safe in the C API tests (GH-118230) The behavior of fileno() after fclose() is undefined, but it is the only practical way to check whether the file was closed. Only test this on the known platforms (Linux, Windows, macOS), where we already tested that it works. --- Modules/_testcapi/run.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/_testcapi/run.c b/Modules/_testcapi/run.c index fa3251c4b5b310..4fd98b82d762ff 100644 --- a/Modules/_testcapi/run.c +++ b/Modules/_testcapi/run.c @@ -74,8 +74,10 @@ run_fileexflags(PyObject *mod, PyObject *pos_args) result = PyRun_FileExFlags(fp, filename, start, globals, locals, closeit, pflags); -#if !defined(__wasi__) - /* The behavior of fileno() after fclose() is undefined. */ +#if defined(__linux__) || defined(MS_WINDOWS) || defined(__APPLE__) + /* The behavior of fileno() after fclose() is undefined, but it is + * the only practical way to check whether the file was closed. + * Only test this on the known platforms. */ if (closeit && result && fileno(fp) >= 0) { PyErr_SetString(PyExc_AssertionError, "File was not closed after excution"); Py_DECREF(result); From 796b3fb28057948ea5b98f7eb0c0f3af6a1e276e Mon Sep 17 00:00:00 2001 From: Itamar Oren Date: Wed, 24 Apr 2024 23:49:15 -0700 Subject: [PATCH 048/217] gh-118207: Rename the COMMON_FIELDS macro in funcobject.h and undef it after use (GH-118208) --- Include/cpython/funcobject.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index de2013323d2c72..5433ba48eefc69 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -8,7 +8,7 @@ extern "C" { #endif -#define COMMON_FIELDS(PREFIX) \ +#define _Py_COMMON_FIELDS(PREFIX) \ PyObject *PREFIX ## globals; \ PyObject *PREFIX ## builtins; \ PyObject *PREFIX ## name; \ @@ -19,7 +19,7 @@ extern "C" { PyObject *PREFIX ## closure; /* NULL or a tuple of cell objects */ typedef struct { - COMMON_FIELDS(fc_) + _Py_COMMON_FIELDS(fc_) } PyFrameConstructor; /* Function objects and code objects should not be confused with each other: @@ -35,7 +35,7 @@ typedef struct { typedef struct { PyObject_HEAD - COMMON_FIELDS(func_) + _Py_COMMON_FIELDS(func_) PyObject *func_doc; /* The __doc__ attribute, can be anything */ PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */ PyObject *func_weakreflist; /* List of weak references */ @@ -60,6 +60,8 @@ typedef struct { */ } PyFunctionObject; +#undef _Py_COMMON_FIELDS + PyAPI_DATA(PyTypeObject) PyFunction_Type; #define PyFunction_Check(op) Py_IS_TYPE((op), &PyFunction_Type) From e38b43c213a8ab2ad9748bac2732af9b58c816ae Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 25 Apr 2024 10:11:45 +0200 Subject: [PATCH 049/217] gh-118221: Always use the default row factory in sqlite3.iterdump() (#118223) sqlite3.iterdump() depends on the row factory returning resulting rows as tuples; it will fail with custom row factories like for example a dict factory. With this commit, we explicitly reset the row factory of the cursor used by iterdump(), so we always get predictable results. This does not affect the row factory of the parent connection. Co-authored-by: Mariusz Felisiak Co-authored-by: Serhiy Storchaka --- Lib/sqlite3/dump.py | 1 + Lib/test/test_sqlite3/test_dump.py | 15 +++++++++++++++ ...2024-04-24-12-29-33.gh-issue-118221.2k_bac.rst | 2 ++ 3 files changed, 18 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-04-24-12-29-33.gh-issue-118221.2k_bac.rst diff --git a/Lib/sqlite3/dump.py b/Lib/sqlite3/dump.py index 9dcce7dc76ced4..57e6a3b4f1e6eb 100644 --- a/Lib/sqlite3/dump.py +++ b/Lib/sqlite3/dump.py @@ -26,6 +26,7 @@ def _iterdump(connection, *, filter=None): writeable_schema = False cu = connection.cursor() + cu.row_factory = None # Make sure we get predictable results. # Disable foreign key constraints, if there is any foreign key violation. violations = cu.execute("PRAGMA foreign_key_check").fetchall() if violations: diff --git a/Lib/test/test_sqlite3/test_dump.py b/Lib/test/test_sqlite3/test_dump.py index 7261b7f0dc93d0..d508f238f84fb5 100644 --- a/Lib/test/test_sqlite3/test_dump.py +++ b/Lib/test/test_sqlite3/test_dump.py @@ -190,6 +190,21 @@ def __getitem__(self, index): got = list(self.cx.iterdump()) self.assertEqual(expected, got) + def test_dump_custom_row_factory(self): + # gh-118221: iterdump should be able to cope with custom row factories. + def dict_factory(cu, row): + fields = [col[0] for col in cu.description] + return dict(zip(fields, row)) + + self.cx.row_factory = dict_factory + CREATE_TABLE = "CREATE TABLE test(t);" + expected = ["BEGIN TRANSACTION;", CREATE_TABLE, "COMMIT;"] + + self.cu.execute(CREATE_TABLE) + actual = list(self.cx.iterdump()) + self.assertEqual(expected, actual) + self.assertEqual(self.cx.row_factory, dict_factory) + def test_dump_virtual_tables(self): # gh-64662 expected = [ diff --git a/Misc/NEWS.d/next/Library/2024-04-24-12-29-33.gh-issue-118221.2k_bac.rst b/Misc/NEWS.d/next/Library/2024-04-24-12-29-33.gh-issue-118221.2k_bac.rst new file mode 100644 index 00000000000000..9b0ea9978a195e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-24-12-29-33.gh-issue-118221.2k_bac.rst @@ -0,0 +1,2 @@ +Fix a bug where :func:`sqlite3.iterdump` could fail if a custom :attr:`row +factory ` was used. Patch by Erlend Aasland. From 10bb90ed49a81a525b126ce8e4d8564c1616d0b3 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Thu, 25 Apr 2024 11:07:38 +0200 Subject: [PATCH 050/217] gh-102511: Speed up os.path.splitroot() with native helpers (GH-118089) --- Include/internal/pycore_fileutils.h | 2 + Lib/ntpath.py | 116 ++++++++------ Lib/posixpath.py | 74 +++++---- Lib/test/test_ntpath.py | 1 + ...-04-19-08-50-48.gh-issue-102511.qDEB66.rst | 1 + Modules/clinic/posixmodule.c.h | 60 ++++++- Modules/posixmodule.c | 44 ++++++ Python/fileutils.c | 147 ++++++++++++++---- 8 files changed, 337 insertions(+), 108 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-19-08-50-48.gh-issue-102511.qDEB66.rst diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index 5c55282fa39e6f..bc8100b58e8ea3 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -290,6 +290,8 @@ extern wchar_t *_Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t extern HRESULT PathCchSkipRoot(const wchar_t *pszPath, const wchar_t **ppszRootEnd); #endif /* defined(MS_WINDOWS_GAMES) && !defined(MS_WINDOWS_DESKTOP) */ +extern void _Py_skiproot(const wchar_t *path, Py_ssize_t size, Py_ssize_t *drvsize, Py_ssize_t *rootsize); + // Macros to protect CRT calls against instant termination when passed an // invalid parameter (bpo-23524). IPH stands for Invalid Parameter Handler. // Usage: diff --git a/Lib/ntpath.py b/Lib/ntpath.py index aba18bfe407abf..e810b655e5ac85 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -167,56 +167,76 @@ def splitdrive(p): return drive, root + tail -def splitroot(p): - """Split a pathname into drive, root and tail. The drive is defined - exactly as in splitdrive(). On Windows, the root may be a single path - separator or an empty string. The tail contains anything after the root. - For example: - - splitroot('//server/share/') == ('//server/share', '/', '') - splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney') - splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham') - splitroot('Windows/notepad') == ('', '', 'Windows/notepad') - """ - p = os.fspath(p) - if isinstance(p, bytes): - sep = b'\\' - altsep = b'/' - colon = b':' - unc_prefix = b'\\\\?\\UNC\\' - empty = b'' - else: - sep = '\\' - altsep = '/' - colon = ':' - unc_prefix = '\\\\?\\UNC\\' - empty = '' - normp = p.replace(altsep, sep) - if normp[:1] == sep: - if normp[1:2] == sep: - # UNC drives, e.g. \\server\share or \\?\UNC\server\share - # Device drives, e.g. \\.\device or \\?\device - start = 8 if normp[:8].upper() == unc_prefix else 2 - index = normp.find(sep, start) - if index == -1: - return p, empty, empty - index2 = normp.find(sep, index + 1) - if index2 == -1: - return p, empty, empty - return p[:index2], p[index2:index2 + 1], p[index2 + 1:] +try: + from nt import _path_splitroot_ex +except ImportError: + def splitroot(p): + """Split a pathname into drive, root and tail. The drive is defined + exactly as in splitdrive(). On Windows, the root may be a single path + separator or an empty string. The tail contains anything after the root. + For example: + + splitroot('//server/share/') == ('//server/share', '/', '') + splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney') + splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham') + splitroot('Windows/notepad') == ('', '', 'Windows/notepad') + """ + p = os.fspath(p) + if isinstance(p, bytes): + sep = b'\\' + altsep = b'/' + colon = b':' + unc_prefix = b'\\\\?\\UNC\\' + empty = b'' else: - # Relative path with root, e.g. \Windows - return empty, p[:1], p[1:] - elif normp[1:2] == colon: - if normp[2:3] == sep: - # Absolute drive-letter path, e.g. X:\Windows - return p[:2], p[2:3], p[3:] + sep = '\\' + altsep = '/' + colon = ':' + unc_prefix = '\\\\?\\UNC\\' + empty = '' + normp = p.replace(altsep, sep) + if normp[:1] == sep: + if normp[1:2] == sep: + # UNC drives, e.g. \\server\share or \\?\UNC\server\share + # Device drives, e.g. \\.\device or \\?\device + start = 8 if normp[:8].upper() == unc_prefix else 2 + index = normp.find(sep, start) + if index == -1: + return p, empty, empty + index2 = normp.find(sep, index + 1) + if index2 == -1: + return p, empty, empty + return p[:index2], p[index2:index2 + 1], p[index2 + 1:] + else: + # Relative path with root, e.g. \Windows + return empty, p[:1], p[1:] + elif normp[1:2] == colon: + if normp[2:3] == sep: + # Absolute drive-letter path, e.g. X:\Windows + return p[:2], p[2:3], p[3:] + else: + # Relative path with drive, e.g. X:Windows + return p[:2], empty, p[2:] else: - # Relative path with drive, e.g. X:Windows - return p[:2], empty, p[2:] - else: - # Relative path, e.g. Windows - return empty, empty, p + # Relative path, e.g. Windows + return empty, empty, p +else: + def splitroot(p): + """Split a pathname into drive, root and tail. The drive is defined + exactly as in splitdrive(). On Windows, the root may be a single path + separator or an empty string. The tail contains anything after the root. + For example: + + splitroot('//server/share/') == ('//server/share', '/', '') + splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney') + splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham') + splitroot('Windows/notepad') == ('', '', 'Windows/notepad') + """ + p = os.fspath(p) + if isinstance(p, bytes): + drive, root, tail = _path_splitroot_ex(os.fsdecode(p)) + return os.fsencode(drive), os.fsencode(root), os.fsencode(tail) + return _path_splitroot_ex(p) # Split a path in head (everything up to the last '/') and tail (the diff --git a/Lib/posixpath.py b/Lib/posixpath.py index f1960ddb88e590..56b7915826daf4 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -134,33 +134,53 @@ def splitdrive(p): return p[:0], p -def splitroot(p): - """Split a pathname into drive, root and tail. On Posix, drive is always - empty; the root may be empty, a single slash, or two slashes. The tail - contains anything after the root. For example: - - splitroot('foo/bar') == ('', '', 'foo/bar') - splitroot('/foo/bar') == ('', '/', 'foo/bar') - splitroot('//foo/bar') == ('', '//', 'foo/bar') - splitroot('///foo/bar') == ('', '/', '//foo/bar') - """ - p = os.fspath(p) - if isinstance(p, bytes): - sep = b'/' - empty = b'' - else: - sep = '/' - empty = '' - if p[:1] != sep: - # Relative path, e.g.: 'foo' - return empty, empty, p - elif p[1:2] != sep or p[2:3] == sep: - # Absolute path, e.g.: '/foo', '///foo', '////foo', etc. - return empty, sep, p[1:] - else: - # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see - # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 - return empty, p[:2], p[2:] +try: + from posix import _path_splitroot_ex +except ImportError: + def splitroot(p): + """Split a pathname into drive, root and tail. On Posix, drive is always + empty; the root may be empty, a single slash, or two slashes. The tail + contains anything after the root. For example: + + splitroot('foo/bar') == ('', '', 'foo/bar') + splitroot('/foo/bar') == ('', '/', 'foo/bar') + splitroot('//foo/bar') == ('', '//', 'foo/bar') + splitroot('///foo/bar') == ('', '/', '//foo/bar') + """ + p = os.fspath(p) + if isinstance(p, bytes): + sep = b'/' + empty = b'' + else: + sep = '/' + empty = '' + if p[:1] != sep: + # Relative path, e.g.: 'foo' + return empty, empty, p + elif p[1:2] != sep or p[2:3] == sep: + # Absolute path, e.g.: '/foo', '///foo', '////foo', etc. + return empty, sep, p[1:] + else: + # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see + # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + return empty, p[:2], p[2:] +else: + def splitroot(p): + """Split a pathname into drive, root and tail. On Posix, drive is always + empty; the root may be empty, a single slash, or two slashes. The tail + contains anything after the root. For example: + + splitroot('foo/bar') == ('', '', 'foo/bar') + splitroot('/foo/bar') == ('', '/', 'foo/bar') + splitroot('//foo/bar') == ('', '//', 'foo/bar') + splitroot('///foo/bar') == ('', '/', '//foo/bar') + """ + p = os.fspath(p) + if isinstance(p, bytes): + # Optimisation: the drive is always empty + _, root, tail = _path_splitroot_ex(os.fsdecode(p)) + return b'', os.fsencode(root), os.fsencode(tail) + return _path_splitroot_ex(p) # Return the tail (basename) part of a path, same as split(path)[1]. diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 31156130fcc747..7f91bf1c2b837a 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -374,6 +374,7 @@ def test_normpath(self): tester("ntpath.normpath('\\\\foo\\')", '\\\\foo\\') tester("ntpath.normpath('\\\\foo')", '\\\\foo') tester("ntpath.normpath('\\\\')", '\\\\') + tester("ntpath.normpath('//?/UNC/server/share/..')", '\\\\?\\UNC\\server\\share\\') def test_realpath_curdir(self): expected = ntpath.normpath(os.getcwd()) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-19-08-50-48.gh-issue-102511.qDEB66.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-19-08-50-48.gh-issue-102511.qDEB66.rst new file mode 100644 index 00000000000000..dfdf250710778e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-19-08-50-48.gh-issue-102511.qDEB66.rst @@ -0,0 +1 @@ +Speed up :func:`os.path.splitroot` with a native implementation. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 0398629e3c10ce..a0d1f3238a6733 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -2248,6 +2248,64 @@ os__path_islink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj #endif /* defined(MS_WINDOWS) */ +PyDoc_STRVAR(os__path_splitroot_ex__doc__, +"_path_splitroot_ex($module, /, path)\n" +"--\n" +"\n"); + +#define OS__PATH_SPLITROOT_EX_METHODDEF \ + {"_path_splitroot_ex", _PyCFunction_CAST(os__path_splitroot_ex), METH_FASTCALL|METH_KEYWORDS, os__path_splitroot_ex__doc__}, + +static PyObject * +os__path_splitroot_ex_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_splitroot_ex(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_splitroot_ex", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("_path_splitroot_ex", "argument 'path'", "str", args[0]); + goto exit; + } + path = args[0]; + return_value = os__path_splitroot_ex_impl(module, path); + +exit: + return return_value; +} + PyDoc_STRVAR(os__path_normpath__doc__, "_path_normpath($module, /, path)\n" "--\n" @@ -12602,4 +12660,4 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ -/*[clinic end generated code: output=511f0788a6b90db0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c4698b47007cd6eb input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5e54cf64cd563e..c9d67ccbb8c908 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5467,6 +5467,49 @@ os__path_islink_impl(PyObject *module, PyObject *path) #endif /* MS_WINDOWS */ +/*[clinic input] +os._path_splitroot_ex + + path: unicode + +[clinic start generated code]*/ + +static PyObject * +os__path_splitroot_ex_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=de97403d3dfebc40 input=f1470e12d899f9ac]*/ +{ + Py_ssize_t len, drvsize, rootsize; + PyObject *drv = NULL, *root = NULL, *tail = NULL, *result = NULL; + + wchar_t *buffer = PyUnicode_AsWideCharString(path, &len); + if (!buffer) { + goto exit; + } + + _Py_skiproot(buffer, len, &drvsize, &rootsize); + drv = PyUnicode_FromWideChar(buffer, drvsize); + if (drv == NULL) { + goto exit; + } + root = PyUnicode_FromWideChar(&buffer[drvsize], rootsize); + if (root == NULL) { + goto exit; + } + tail = PyUnicode_FromWideChar(&buffer[drvsize + rootsize], + len - drvsize - rootsize); + if (tail == NULL) { + goto exit; + } + result = Py_BuildValue("(OOO)", drv, root, tail); +exit: + PyMem_Free(buffer); + Py_XDECREF(drv); + Py_XDECREF(root); + Py_XDECREF(tail); + return result; +} + + /*[clinic input] os._path_normpath @@ -16799,6 +16842,7 @@ static PyMethodDef posix_methods[] = { OS__FINDFIRSTFILE_METHODDEF OS__GETVOLUMEPATHNAME_METHODDEF OS__PATH_SPLITROOT_METHODDEF + OS__PATH_SPLITROOT_EX_METHODDEF OS__PATH_NORMPATH_METHODDEF OS_GETLOADAVG_METHODDEF OS_URANDOM_METHODDEF diff --git a/Python/fileutils.c b/Python/fileutils.c index 882d3299575cf3..54853ba2f75d9d 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2295,6 +2295,99 @@ PathCchCombineEx(wchar_t *buffer, size_t bufsize, const wchar_t *dirname, #endif /* defined(MS_WINDOWS_GAMES) && !defined(MS_WINDOWS_DESKTOP) */ +void +_Py_skiproot(const wchar_t *path, Py_ssize_t size, Py_ssize_t *drvsize, + Py_ssize_t *rootsize) +{ + assert(drvsize); + assert(rootsize); +#ifndef MS_WINDOWS +#define IS_SEP(x) (*(x) == SEP) + *drvsize = 0; + if (!IS_SEP(&path[0])) { + // Relative path, e.g.: 'foo' + *rootsize = 0; + } + else if (!IS_SEP(&path[1]) || IS_SEP(&path[2])) { + // Absolute path, e.g.: '/foo', '///foo', '////foo', etc. + *rootsize = 1; + } + else { + // Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + *rootsize = 2; + } +#undef IS_SEP +#else + const wchar_t *pEnd = size >= 0 ? &path[size] : NULL; +#define IS_END(x) (pEnd ? (x) == pEnd : !*(x)) +#define IS_SEP(x) (*(x) == SEP || *(x) == ALTSEP) +#define SEP_OR_END(x) (IS_SEP(x) || IS_END(x)) + if (IS_SEP(&path[0])) { + if (IS_SEP(&path[1])) { + // Device drives, e.g. \\.\device or \\?\device + // UNC drives, e.g. \\server\share or \\?\UNC\server\share + Py_ssize_t idx; + if (path[2] == L'?' && IS_SEP(&path[3]) && + (path[4] == L'U' || path[4] == L'u') && + (path[5] == L'N' || path[5] == L'n') && + (path[6] == L'C' || path[6] == L'c') && + IS_SEP(&path[7])) + { + idx = 8; + } + else { + idx = 2; + } + while (!SEP_OR_END(&path[idx])) { + idx++; + } + if (IS_END(&path[idx])) { + *drvsize = idx; + *rootsize = 0; + } + else { + idx++; + while (!SEP_OR_END(&path[idx])) { + idx++; + } + *drvsize = idx; + if (IS_END(&path[idx])) { + *rootsize = 0; + } + else { + *rootsize = 1; + } + } + } + else { + // Relative path with root, e.g. \Windows + *drvsize = 0; + *rootsize = 1; + } + } + else if (!IS_END(&path[0]) && path[1] == L':') { + *drvsize = 2; + if (IS_SEP(&path[2])) { + // Absolute drive-letter path, e.g. X:\Windows + *rootsize = 1; + } + else { + // Relative path with drive, e.g. X:Windows + *rootsize = 0; + } + } + else { + // Relative path, e.g. Windows + *drvsize = 0; + *rootsize = 0; + } +#undef SEP_OR_END +#undef IS_SEP +#undef IS_END +#endif +} + // The caller must ensure "buffer" is big enough. static int join_relfile(wchar_t *buffer, size_t bufsize, @@ -2411,49 +2504,39 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) #endif #define SEP_OR_END(x) (IS_SEP(x) || IS_END(x)) - // Skip leading '.\' if (p1[0] == L'.' && IS_SEP(&p1[1])) { + // Skip leading '.\' path = &path[2]; - while (IS_SEP(path) && !IS_END(path)) { + while (IS_SEP(path)) { path++; } p1 = p2 = minP2 = path; lastC = SEP; } + else { + Py_ssize_t drvsize, rootsize; + _Py_skiproot(path, size, &drvsize, &rootsize); + if (drvsize || rootsize) { + // Skip past root and update minP2 + p1 = &path[drvsize + rootsize]; +#ifndef ALTSEP + p2 = p1; +#else + for (; p2 < p1; ++p2) { + if (*p2 == ALTSEP) { + *p2 = SEP; + } + } +#endif + minP2 = p2 - 1; + lastC = *minP2; #ifdef MS_WINDOWS - // Skip past drive segment and update minP2 - else if (p1[0] && p1[1] == L':') { - *p2++ = *p1++; - *p2++ = *p1++; - minP2 = p2; - lastC = L':'; - } - // Skip past all \\-prefixed paths, including \\?\, \\.\, - // and network paths, including the first segment. - else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1])) { - int sepCount = 2; - *p2++ = SEP; - *p2++ = SEP; - p1 += 2; - for (; !IS_END(p1) && sepCount; ++p1) { - if (IS_SEP(p1)) { - --sepCount; - *p2++ = lastC = SEP; - } else { - *p2++ = lastC = *p1; + if (lastC != SEP) { + minP2++; } +#endif } - minP2 = p2 - 1; - } -#else - // Skip past two leading SEPs - else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1]) && !IS_SEP(&p1[2])) { - *p2++ = *p1++; - *p2++ = *p1++; - minP2 = p2 - 1; // Absolute path has SEP at minP2 - lastC = SEP; } -#endif /* MS_WINDOWS */ /* if pEnd is specified, check that. Else, check for null terminator */ for (; !IS_END(p1); ++p1) { From f180b31e7629d36265fa36f1560365358b4fd47c Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 25 Apr 2024 11:32:47 +0100 Subject: [PATCH 051/217] GH-118095: Handle `RETURN_GENERATOR` in tier 2 (GH-118180) --- Include/internal/pycore_ceval.h | 2 +- Include/internal/pycore_frame.h | 14 ++++++-- Include/internal/pycore_opcode_metadata.h | 3 +- Include/internal/pycore_uop_ids.h | 1 + Include/internal/pycore_uop_metadata.h | 4 +++ Lib/test/test_capi/test_opt.py | 12 +++++++ Objects/frameobject.c | 5 --- Python/bytecodes.c | 24 ++++--------- Python/ceval_macros.h | 12 +++++++ Python/executor_cases.c.h | 43 ++++++++++++++++------- Python/frame.c | 12 ------- Python/generated_cases.c.h | 40 +++++++-------------- Python/optimizer.c | 5 +-- Python/optimizer_analysis.c | 2 +- Python/optimizer_bytecodes.c | 22 ++++++++++++ Python/optimizer_cases.c.h | 23 ++++++++++++ 16 files changed, 143 insertions(+), 81 deletions(-) diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 946f82ae3c20e3..8d88b5c1d15cb8 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -182,7 +182,7 @@ static inline void _Py_LeaveRecursiveCall(void) { extern struct _PyInterpreterFrame* _PyEval_GetFrame(void); -extern PyObject* _Py_MakeCoro(PyFunctionObject *func); +PyAPI_FUNC(PyObject *)_Py_MakeCoro(PyFunctionObject *func); /* Handle signals, pending calls, GIL drop request and asynchronous exception */ diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 74d9e4cac72c0e..f913928f38bd05 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -110,7 +110,17 @@ _PyFrame_NumSlotsForCodeObject(PyCodeObject *code) return code->co_framesize - FRAME_SPECIALS_SIZE; } -void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest); +static inline void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest) +{ + assert(src->stacktop >= _PyFrame_GetCode(src)->co_nlocalsplus); + *dest = *src; + for (int i = 1; i < src->stacktop; i++) { + dest->localsplus[i] = src->localsplus[i]; + } + // Don't leave a dangling pointer to the old frame when creating generators + // and coroutines: + dest->previous = NULL; +} /* Consumes reference to func and locals. Does not initialize frame->previous, which happens @@ -256,7 +266,7 @@ _PyThreadState_HasStackSpace(PyThreadState *tstate, int size) extern _PyInterpreterFrame * _PyThreadState_PushFrame(PyThreadState *tstate, size_t size); -void _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame *frame); +PyAPI_FUNC(void) _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame *frame); /* Pushes a frame without checking for space. * Must be guarded by _PyThreadState_HasStackSpace() diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 5636debbf4a7f2..400d7c334db8e7 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -805,7 +805,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { case RETURN_CONST: return 0; case RETURN_GENERATOR: - return 0; + return 1; case RETURN_VALUE: return 0; case SEND: @@ -1310,6 +1310,7 @@ _PyOpcode_macro_expansion[256] = { [PUSH_NULL] = { .nuops = 1, .uops = { { _PUSH_NULL, 0, 0 } } }, [RESUME_CHECK] = { .nuops = 1, .uops = { { _RESUME_CHECK, 0, 0 } } }, [RETURN_CONST] = { .nuops = 2, .uops = { { _LOAD_CONST, 0, 0 }, { _POP_FRAME, 0, 0 } } }, + [RETURN_GENERATOR] = { .nuops = 1, .uops = { { _RETURN_GENERATOR, 0, 0 } } }, [RETURN_VALUE] = { .nuops = 1, .uops = { { _POP_FRAME, 0, 0 } } }, [SETUP_ANNOTATIONS] = { .nuops = 1, .uops = { { _SETUP_ANNOTATIONS, 0, 0 } } }, [SET_ADD] = { .nuops = 1, .uops = { { _SET_ADD, 0, 0 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index f0558743b32f5e..bb49d6e77d2562 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -231,6 +231,7 @@ extern "C" { #define _PUSH_NULL PUSH_NULL #define _REPLACE_WITH_TRUE 424 #define _RESUME_CHECK RESUME_CHECK +#define _RETURN_GENERATOR RETURN_GENERATOR #define _SAVE_RETURN_OFFSET 425 #define _SEND 426 #define _SEND_GEN SEND_GEN diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 2da4c4d4e21e93..b8cdfae8391460 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -219,6 +219,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CALL_METHOD_DESCRIPTOR_FAST] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_MAKE_FUNCTION] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_SET_FUNCTION_ATTRIBUTE] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, + [_RETURN_GENERATOR] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_BUILD_SLICE] = HAS_ARG_FLAG | HAS_ERROR_FLAG, [_CONVERT_VALUE] = HAS_ARG_FLAG | HAS_ERROR_FLAG, [_FORMAT_SIMPLE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -445,6 +446,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_PUSH_NULL] = "_PUSH_NULL", [_REPLACE_WITH_TRUE] = "_REPLACE_WITH_TRUE", [_RESUME_CHECK] = "_RESUME_CHECK", + [_RETURN_GENERATOR] = "_RETURN_GENERATOR", [_SAVE_RETURN_OFFSET] = "_SAVE_RETURN_OFFSET", [_SETUP_ANNOTATIONS] = "_SETUP_ANNOTATIONS", [_SET_ADD] = "_SET_ADD", @@ -894,6 +896,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _SET_FUNCTION_ATTRIBUTE: return 2; + case _RETURN_GENERATOR: + return 0; case _BUILD_SLICE: return 2 + ((oparg == 3) ? 1 : 0); case _CONVERT_VALUE: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index c004f463770019..e2e772a52d764e 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1286,5 +1286,17 @@ def testfunc(n): self.assertEqual(res, 32 * 32) self.assertIsNone(ex) + def test_return_generator(self): + def gen(): + yield None + def testfunc(n): + for i in range(n): + gen() + return i + res, ex = self._run_with_optimizer(testfunc, 20) + self.assertEqual(res, 19) + self.assertIsNotNone(ex) + self.assertIn("_RETURN_GENERATOR", get_opnames(ex)) + if __name__ == "__main__": unittest.main() diff --git a/Objects/frameobject.c b/Objects/frameobject.c index d55c246d80dd6a..07b7ef3df46a5c 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -304,11 +304,6 @@ mark_stacks(PyCodeObject *code_obj, int len) stacks[i] = UNINITIALIZED; } stacks[0] = EMPTY_STACK; - if (code_obj->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) - { - // Generators get sent None while starting: - stacks[0] = push_value(stacks[0], Object); - } int todo = 1; while (todo) { todo = 0; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c31617d35b02f5..485504914912f9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -837,12 +837,7 @@ dummy_func( _PyFrame_StackPush(frame, retval); LOAD_SP(); LOAD_IP(frame->return_offset); -#if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; - } -#endif + LLTRACE_RESUME_FRAME(); } macro(RETURN_VALUE) = @@ -3186,12 +3181,7 @@ dummy_func( tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); -#if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; - } -#endif + LLTRACE_RESUME_FRAME(); } macro(CALL_BOUND_METHOD_EXACT_ARGS) = @@ -3877,7 +3867,7 @@ dummy_func( } } - tier1 inst(RETURN_GENERATOR, (--)) { + inst(RETURN_GENERATOR, (-- res)) { assert(PyFunction_Check(frame->f_funcobj)); PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); @@ -3887,19 +3877,19 @@ dummy_func( assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - frame->instr_ptr = next_instr; + frame->instr_ptr++; _PyFrame_Copy(frame, gen_frame); assert(frame->frame_obj == NULL); gen->gi_frame_state = FRAME_CREATED; gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &entry_frame); + res = (PyObject *)gen; _PyInterpreterFrame *prev = frame->previous; _PyThreadState_PopFrame(tstate, frame); frame = tstate->current_frame = prev; - _PyFrame_StackPush(frame, (PyObject *)gen); LOAD_IP(frame->return_offset); - goto resume_frame; + LOAD_SP(); + LLTRACE_RESUME_FRAME(); } inst(BUILD_SLICE, (start, stop, step if (oparg == 3) -- slice)) { diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 224cd1da7d4a0e..871d1747e2bb8d 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -86,6 +86,18 @@ #define PRE_DISPATCH_GOTO() ((void)0) #endif +#if LLTRACE +#define LLTRACE_RESUME_FRAME() \ +do { \ + lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); \ + if (lltrace < 0) { \ + goto exit_unwind; \ + } \ +} while (0) +#else +#define LLTRACE_RESUME_FRAME() ((void)0) +#endif + #ifdef Py_GIL_DISABLED #define QSBR_QUIESCENT_STATE(tstate) _Py_qsbr_quiescent_state(((_PyThreadStateImpl *)tstate)->qsbr) #else diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 7403d6fdaf0e2b..1eb3da9b70002c 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -988,12 +988,7 @@ _PyFrame_StackPush(frame, retval); LOAD_SP(); LOAD_IP(frame->return_offset); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; - } - #endif + LLTRACE_RESUME_FRAME(); break; } @@ -3213,12 +3208,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; - } - #endif + LLTRACE_RESUME_FRAME(); break; } @@ -3833,6 +3823,35 @@ break; } + case _RETURN_GENERATOR: { + PyObject *res; + assert(PyFunction_Check(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; + PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); + if (gen == NULL) { + JUMP_TO_ERROR(); + } + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + frame->instr_ptr++; + _PyFrame_Copy(frame, gen_frame); + assert(frame->frame_obj == NULL); + gen->gi_frame_state = FRAME_CREATED; + gen_frame->owner = FRAME_OWNED_BY_GENERATOR; + _Py_LeaveRecursiveCallPy(tstate); + res = (PyObject *)gen; + _PyInterpreterFrame *prev = frame->previous; + _PyThreadState_PopFrame(tstate, frame); + frame = tstate->current_frame = prev; + LOAD_IP(frame->return_offset); + LOAD_SP(); + LLTRACE_RESUME_FRAME(); + stack_pointer[0] = res; + stack_pointer += 1; + break; + } + case _BUILD_SLICE: { PyObject *step = NULL; PyObject *stop; diff --git a/Python/frame.c b/Python/frame.c index f88a8f0d73d3f8..db9d13359a23ca 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -53,18 +53,6 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame) return f; } -void -_PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest) -{ - assert(src->stacktop >= _PyFrame_GetCode(src)->co_nlocalsplus); - Py_ssize_t size = ((char*)&src->localsplus[src->stacktop]) - (char *)src; - memcpy(dest, src, size); - // Don't leave a dangling pointer to the old frame when creating generators - // and coroutines: - dest->previous = NULL; -} - - static void take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 058cac8bedd917..0c58f3f87d4041 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -997,12 +997,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; - } - #endif + LLTRACE_RESUME_FRAME(); } DISPATCH(); } @@ -1786,12 +1781,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; - } - #endif + LLTRACE_RESUME_FRAME(); } DISPATCH(); } @@ -4992,12 +4982,7 @@ _PyFrame_StackPush(frame, retval); LOAD_SP(); LOAD_IP(frame->return_offset); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; - } - #endif + LLTRACE_RESUME_FRAME(); } DISPATCH(); } @@ -5006,6 +4991,7 @@ frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RETURN_GENERATOR); + PyObject *res; assert(PyFunction_Check(frame->f_funcobj)); PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); @@ -5015,19 +5001,22 @@ assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - frame->instr_ptr = next_instr; + frame->instr_ptr++; _PyFrame_Copy(frame, gen_frame); assert(frame->frame_obj == NULL); gen->gi_frame_state = FRAME_CREATED; gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &entry_frame); + res = (PyObject *)gen; _PyInterpreterFrame *prev = frame->previous; _PyThreadState_PopFrame(tstate, frame); frame = tstate->current_frame = prev; - _PyFrame_StackPush(frame, (PyObject *)gen); LOAD_IP(frame->return_offset); - goto resume_frame; + LOAD_SP(); + LLTRACE_RESUME_FRAME(); + stack_pointer[0] = res; + stack_pointer += 1; + DISPATCH(); } TARGET(RETURN_VALUE) { @@ -5050,12 +5039,7 @@ _PyFrame_StackPush(frame, retval); LOAD_SP(); LOAD_IP(frame->return_offset); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; - } - #endif + LLTRACE_RESUME_FRAME(); DISPATCH(); } diff --git a/Python/optimizer.c b/Python/optimizer.c index b17c2998e2504b..e5c70f72f9c324 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -697,7 +697,8 @@ translate_bytecode_to_trace( // Reserve space for nuops (+ _SET_IP + _EXIT_TRACE) int nuops = expansion->nuops; RESERVE(nuops + 1); /* One extra for exit */ - if (expansion->uops[nuops-1].uop == _POP_FRAME) { + int16_t last_op = expansion->uops[nuops-1].uop; + if (last_op == _POP_FRAME || last_op == _RETURN_GENERATOR) { // Check for trace stack underflow now: // We can't bail e.g. in the middle of // LOAD_CONST + _POP_FRAME. @@ -756,7 +757,7 @@ translate_bytecode_to_trace( Py_FatalError("garbled expansion"); } - if (uop == _POP_FRAME) { + if (uop == _POP_FRAME || uop == _RETURN_GENERATOR) { TRACE_STACK_POP(); /* Set the operand to the function or code object returned to, * to assist optimization passes. (See _PUSH_FRAME below.) diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index a76edd62c94c13..9315d7228b5732 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -369,7 +369,7 @@ eliminate_pop_guard(_PyUOpInstruction *this_instr, bool exit) static PyCodeObject * get_code(_PyUOpInstruction *op) { - assert(op->opcode == _PUSH_FRAME || op->opcode == _POP_FRAME); + assert(op->opcode == _PUSH_FRAME || op->opcode == _POP_FRAME || op->opcode == _RETURN_GENERATOR); PyCodeObject *co = NULL; uint64_t operand = op->operand; if (operand == 0) { diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 481fb8387af416..8bc56342774790 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -651,6 +651,28 @@ dummy_func(void) { } } + op(_RETURN_GENERATOR, ( -- res)) { + SYNC_SP(); + ctx->frame->stack_pointer = stack_pointer; + frame_pop(ctx); + stack_pointer = ctx->frame->stack_pointer; + OUT_OF_SPACE_IF_NULL(res = sym_new_unknown(ctx)); + + /* Stack space handling */ + assert(corresponding_check_stack == NULL); + assert(co != NULL); + int framesize = co->co_framesize; + assert(framesize > 0); + assert(framesize <= curr_space); + curr_space -= framesize; + + co = get_code(this_instr); + if (co == NULL) { + // might be impossible, but bailing is still safe + goto done; + } + } + op(_CHECK_STACK_SPACE, ( --)) { assert(corresponding_check_stack == NULL); corresponding_check_stack = this_instr; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 0a7d96d30ad3e8..4f0941a3cc3e09 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1840,6 +1840,29 @@ break; } + case _RETURN_GENERATOR: { + _Py_UopsSymbol *res; + ctx->frame->stack_pointer = stack_pointer; + frame_pop(ctx); + stack_pointer = ctx->frame->stack_pointer; + OUT_OF_SPACE_IF_NULL(res = sym_new_unknown(ctx)); + /* Stack space handling */ + assert(corresponding_check_stack == NULL); + assert(co != NULL); + int framesize = co->co_framesize; + assert(framesize > 0); + assert(framesize <= curr_space); + curr_space -= framesize; + co = get_code(this_instr); + if (co == NULL) { + // might be impossible, but bailing is still safe + goto done; + } + stack_pointer[0] = res; + stack_pointer += 1; + break; + } + case _BUILD_SLICE: { _Py_UopsSymbol *slice; slice = sym_new_not_null(ctx); From 2c451489122d539080c8d674b391dedc1dedcb53 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 25 Apr 2024 20:51:31 +0900 Subject: [PATCH 052/217] gh-117578: Introduce _PyType_GetModuleByDef2 private function (GH-117661) Co-authored-by: Erlend E. Aasland Co-authored-by: Petr Viktorin --- Include/internal/pycore_typeobject.h | 1 + Modules/_decimal/_decimal.c | 8 ++--- Objects/typeobject.c | 52 ++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 09c4501c38c935..7e533bd138469b 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -164,6 +164,7 @@ extern PyObject * _PyType_GetBases(PyTypeObject *type); extern PyObject * _PyType_GetMRO(PyTypeObject *type); extern PyObject* _PyType_GetSubclasses(PyTypeObject *); extern int _PyType_HasSubclasses(PyTypeObject *); +PyAPI_FUNC(PyObject *) _PyType_GetModuleByDef2(PyTypeObject *, PyTypeObject *, PyModuleDef *); // PyType_Ready() must be called if _PyType_IsReady() is false. // See also the Py_TPFLAGS_READY flag. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 2481455ac0d143..fa425f4f740d31 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -32,6 +32,7 @@ #include #include "pycore_long.h" // _PyLong_IsZero() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_typeobject.h" #include "complexobject.h" #include "mpdecimal.h" @@ -120,11 +121,8 @@ get_module_state_by_def(PyTypeObject *tp) static inline decimal_state * find_state_left_or_right(PyObject *left, PyObject *right) { - PyObject *mod = PyType_GetModuleByDef(Py_TYPE(left), &_decimal_module); - if (mod == NULL) { - PyErr_Clear(); - mod = PyType_GetModuleByDef(Py_TYPE(right), &_decimal_module); - } + PyObject *mod = _PyType_GetModuleByDef2(Py_TYPE(left), Py_TYPE(right), + &_decimal_module); assert(mod != NULL); return get_module_state(mod); } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 808e11fcbaf1ff..07e0a5a02da87f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4825,11 +4825,24 @@ PyType_GetModuleState(PyTypeObject *type) /* Get the module of the first superclass where the module has the * given PyModuleDef. */ -PyObject * -PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def) +static inline PyObject * +get_module_by_def(PyTypeObject *type, PyModuleDef *def) { assert(PyType_Check(type)); + if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { + // type_ready_mro() ensures that no heap type is + // contained in a static type MRO. + return NULL; + } + else { + PyHeapTypeObject *ht = (PyHeapTypeObject*)type; + PyObject *module = ht->ht_module; + if (module && _PyModule_GetDef(module) == def) { + return module; + } + } + PyObject *res = NULL; BEGIN_TYPE_LOCK() @@ -4837,12 +4850,14 @@ PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def) // The type must be ready assert(mro != NULL); assert(PyTuple_Check(mro)); - // mro_invoke() ensures that the type MRO cannot be empty, so we don't have - // to check i < PyTuple_GET_SIZE(mro) at the first loop iteration. + // mro_invoke() ensures that the type MRO cannot be empty. assert(PyTuple_GET_SIZE(mro) >= 1); + // Also, the first item in the MRO is the type itself, which + // we already checked above. We skip it in the loop. + assert(PyTuple_GET_ITEM(mro, 0) == (PyObject *)type); Py_ssize_t n = PyTuple_GET_SIZE(mro); - for (Py_ssize_t i = 0; i < n; i++) { + for (Py_ssize_t i = 1; i < n; i++) { PyObject *super = PyTuple_GET_ITEM(mro, i); if(!_PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) { // Static types in the MRO need to be skipped @@ -4857,14 +4872,37 @@ PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def) } } END_TYPE_LOCK() + return res; +} - if (res == NULL) { +PyObject * +PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def) +{ + PyObject *module = get_module_by_def(type, def); + if (module == NULL) { PyErr_Format( PyExc_TypeError, "PyType_GetModuleByDef: No superclass of '%s' has the given module", type->tp_name); } - return res; + return module; +} + +PyObject * +_PyType_GetModuleByDef2(PyTypeObject *left, PyTypeObject *right, + PyModuleDef *def) +{ + PyObject *module = get_module_by_def(left, def); + if (module == NULL) { + module = get_module_by_def(right, def); + if (module == NULL) { + PyErr_Format( + PyExc_TypeError, + "PyType_GetModuleByDef: No superclass of '%s' nor '%s' has " + "the given module", left->tp_name, right->tp_name); + } + } + return module; } void * From 1723c76d794bef23b518bc79d83281c7309d4832 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Thu, 25 Apr 2024 18:11:59 +0300 Subject: [PATCH 053/217] Fix incorrect usage of ``support.requires_gil_enabled`` (#118170) --- Lib/test/test_capi/test_mem.py | 2 +- Lib/test/test_gdb/test_backtrace.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_mem.py b/Lib/test/test_capi/test_mem.py index 296601e8ee4f5f..6ab7b685c2e18b 100644 --- a/Lib/test/test_capi/test_mem.py +++ b/Lib/test/test_capi/test_mem.py @@ -153,7 +153,7 @@ class C(): pass # free-threading requires mimalloc (not malloc) -@support.requires_gil_enabled +@support.requires_gil_enabled() class PyMemMallocDebugTests(PyMemDebugTests): PYTHONMALLOC = 'malloc_debug' diff --git a/Lib/test/test_gdb/test_backtrace.py b/Lib/test/test_gdb/test_backtrace.py index fe67bf9ecc8880..714853c7b4732d 100644 --- a/Lib/test/test_gdb/test_backtrace.py +++ b/Lib/test/test_gdb/test_backtrace.py @@ -49,7 +49,7 @@ def test_bt_full(self): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") - @support.requires_gil_enabled + @support.requires_gil_enabled() @support.requires_resource('cpu') def test_threads(self): 'Verify that "py-bt" indicates threads that are waiting for the GIL' From eb20a7d12c4b2ab7931074843f8602a48b5b07bd Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 26 Apr 2024 00:13:57 +0900 Subject: [PATCH 054/217] gh-112069: Do not require lock if the set has never been exposed. (gh-118069) --- Objects/setobject.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Objects/setobject.c b/Objects/setobject.c index 0d88f4ff922d24..19975e3d4d18e2 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -2333,6 +2333,13 @@ set_init(PySetObject *self, PyObject *args, PyObject *kwds) if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable)) return -1; + if (Py_REFCNT(self) == 1 && self->fill == 0) { + self->hash = -1; + if (iterable == NULL) { + return 0; + } + return set_update_local(self, iterable); + } Py_BEGIN_CRITICAL_SECTION(self); if (self->fill) set_clear_internal(self); From fb7f79b4da35b75cdc82ff3cf20816d2bf93d416 Mon Sep 17 00:00:00 2001 From: Faidon Liambotis Date: Thu, 25 Apr 2024 18:17:40 +0300 Subject: [PATCH 055/217] gh-117566: fix IPv6Address.is_loopback for IPv4-mapped loopbacks (GH-117567) While properties like IPv6Address.is_private account for IPv4-mapped IPv6 addresses, such as for example: >>> ipaddress.ip_address("192.168.0.1").is_private True >>> ipaddress.ip_address("::ffff:192.168.0.1").is_private True ...the same doesn't currently apply to the is_loopback property: >>> ipaddress.ip_address("127.0.0.1").is_loopback True >>> ipaddress.ip_address("::ffff:127.0.0.1").is_loopback False At minimum, this inconsistency between different properties is counter-intuitive. Moreover, ::ffff:127.0.0.0/104 is for all intents and purposes a loopback address, and should be treated as such. --- Lib/ipaddress.py | 5 ++++- Lib/test/test_ipaddress.py | 16 ++++++++++++++++ ...024-04-05-15-51-01.gh-issue-117566.54nABf.rst | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index 22cdfc93d8ad32..8e4d49c859534d 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -2142,6 +2142,9 @@ def is_loopback(self): RFC 2373 2.5.3. """ + ipv4_mapped = self.ipv4_mapped + if ipv4_mapped is not None: + return ipv4_mapped.is_loopback return self._ip == 1 @property @@ -2258,7 +2261,7 @@ def is_unspecified(self): @property def is_loopback(self): - return self._ip == 1 and self.network.is_loopback + return super().is_loopback and self.network.is_loopback class IPv6Network(_BaseV6, _BaseNetwork): diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index f1519df673747a..c3ecf2a742941a 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -2446,6 +2446,22 @@ def testIpv4MappedPrivateCheck(self): self.assertEqual( False, ipaddress.ip_address('::ffff:172.32.0.0').is_private) + def testIpv4MappedLoopbackCheck(self): + # test networks + self.assertEqual(True, ipaddress.ip_network( + '::ffff:127.100.200.254/128').is_loopback) + self.assertEqual(True, ipaddress.ip_network( + '::ffff:127.42.0.0/112').is_loopback) + self.assertEqual(False, ipaddress.ip_network( + '::ffff:128.0.0.0').is_loopback) + # test addresses + self.assertEqual(True, ipaddress.ip_address( + '::ffff:127.100.200.254').is_loopback) + self.assertEqual(True, ipaddress.ip_address( + '::ffff:127.42.0.0').is_loopback) + self.assertEqual(False, ipaddress.ip_address( + '::ffff:128.0.0.0').is_loopback) + def testAddrExclude(self): addr1 = ipaddress.ip_network('10.1.1.0/24') addr2 = ipaddress.ip_network('10.1.1.0/26') diff --git a/Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst b/Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst new file mode 100644 index 00000000000000..56c2fb0e25d494 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-05-15-51-01.gh-issue-117566.54nABf.rst @@ -0,0 +1,3 @@ +:meth:`ipaddress.IPv6Address.is_loopback` will now return ``True`` for +IPv4-mapped loopback addresses, i.e. addresses in the +``::ffff:127.0.0.0/104`` address space. From f14e9f91544b526a920724dd2b3e2d88d1e28463 Mon Sep 17 00:00:00 2001 From: mpage Date: Thu, 25 Apr 2024 08:31:57 -0700 Subject: [PATCH 056/217] gh-117657: Fix data race in `_Py_IsImmortal` (#118261) The load of `ob_ref_local races with stores. Using a relaxed load is sufficient; stores to the field are relaxed. --- Include/object.h | 3 ++- Tools/tsan/suppressions_free_threading.txt | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/object.h b/Include/object.h index ffcacf1a3ef4ed..5aaf11c5194f0e 100644 --- a/Include/object.h +++ b/Include/object.h @@ -349,7 +349,8 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) { static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op) { #if defined(Py_GIL_DISABLED) - return (op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL); + return (_Py_atomic_load_uint32_relaxed(&op->ob_ref_local) == + _Py_IMMORTAL_REFCNT_LOCAL); #elif SIZEOF_VOID_P > 4 return (_Py_CAST(PY_INT32_T, op->ob_refcnt) < 0); #else diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 1408103ba80f96..6ceb275925c5e1 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -14,7 +14,6 @@ race:set_allocator_unlocked race:_add_to_weak_set race:_in_weak_set race:_mi_heap_delayed_free_partial -race:_Py_IsImmortal race:_Py_IsOwnedByCurrentThread race:_PyEval_EvalFrameDefault race:_PyFunction_SetVersion From cce5ae60821ceb7f72cec0ed48f7350c6a63fdc9 Mon Sep 17 00:00:00 2001 From: mpage Date: Thu, 25 Apr 2024 08:48:16 -0700 Subject: [PATCH 057/217] gh-117657: Add a couple more TSAN suppressions (#118256) --- Tools/tsan/suppressions_free_threading.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 6ceb275925c5e1..aa954ca9b6c09c 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -24,6 +24,8 @@ race:_PyInterpreterState_IsRunningMain race:_PyObject_GC_IS_SHARED race:_PyObject_GC_SET_SHARED race:_PyObject_GC_TRACK +# https://gist.github.com/mpage/0a24eb2dd458441ededb498e9b0e5de8 +race:_PyParkingLot_Park race:_PyType_HasFeature race:assign_version_tag race:compare_unicode_unicode @@ -43,3 +45,6 @@ race:set_inheritable race:start_the_world race:tstate_set_detached race:unicode_hash + +# https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40 +thread:pthread_create From 5da0280648b5f1c5142dad39dcd1e7fd99fdf37a Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Thu, 25 Apr 2024 08:53:29 -0700 Subject: [PATCH 058/217] gh-117657: Fixes a few small TSAN issues in dictobject (#118200) Fixup TSAN errors for dict --- Include/cpython/dictobject.h | 4 ++++ Include/internal/pycore_object.h | 2 +- Objects/dictobject.c | 25 ++++++++++++---------- Tools/tsan/suppressions_free_threading.txt | 3 --- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index 35b6a822a0dfff..3fd23b9313c453 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -56,7 +56,11 @@ static inline Py_ssize_t PyDict_GET_SIZE(PyObject *op) { PyDictObject *mp; assert(PyDict_Check(op)); mp = _Py_CAST(PyDictObject*, op); +#ifdef Py_GIL_DISABLED + return _Py_atomic_load_ssize_relaxed(&mp->ma_used); +#else return mp->ma_used; +#endif } #define PyDict_GET_SIZE(op) PyDict_GET_SIZE(_PyObject_CAST(op)) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 88b052f4544b15..7df8003196d8cc 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -688,7 +688,7 @@ static inline PyDictObject * _PyObject_GetManagedDict(PyObject *obj) { PyManagedDictPointer *dorv = _PyObject_ManagedDictPointer(obj); - return (PyDictObject *)FT_ATOMIC_LOAD_PTR_RELAXED(dorv->dict); + return (PyDictObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(dorv->dict); } static inline PyDictValues * diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 2644516bc30770..afcf535f8c0a78 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1097,10 +1097,11 @@ compare_unicode_unicode(PyDictObject *mp, PyDictKeysObject *dk, void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash) { PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix]; - assert(ep->me_key != NULL); - assert(PyUnicode_CheckExact(ep->me_key)); - if (ep->me_key == key || - (unicode_get_hash(ep->me_key) == hash && unicode_eq(ep->me_key, key))) { + PyObject *ep_key = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_key); + assert(ep_key != NULL); + assert(PyUnicode_CheckExact(ep_key)); + if (ep_key == key || + (unicode_get_hash(ep_key) == hash && unicode_eq(ep_key, key))) { return 1; } return 0; @@ -1761,10 +1762,12 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, else { assert(old_value != NULL); if (DK_IS_UNICODE(mp->ma_keys)) { - DK_UNICODE_ENTRIES(mp->ma_keys)[ix].me_value = value; + PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[ix]; + STORE_VALUE(ep, value); } else { - DK_ENTRIES(mp->ma_keys)[ix].me_value = value; + PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix]; + STORE_VALUE(ep, value); } } mp->ma_version_tag = new_version; @@ -1810,15 +1813,15 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp, if (unicode) { PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(newkeys); ep->me_key = key; - ep->me_value = value; + STORE_VALUE(ep, value); } else { PyDictKeyEntry *ep = DK_ENTRIES(newkeys); ep->me_key = key; ep->me_hash = hash; - ep->me_value = value; + STORE_VALUE(ep, value); } - mp->ma_used++; + FT_ATOMIC_STORE_SSIZE_RELAXED(mp->ma_used, FT_ATOMIC_LOAD_SSIZE_RELAXED(mp->ma_used) + 1); mp->ma_version_tag = new_version; newkeys->dk_usable--; newkeys->dk_nentries++; @@ -2510,7 +2513,7 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, Py_ssize_t hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); - mp->ma_used--; + FT_ATOMIC_STORE_SSIZE_RELAXED(mp->ma_used, FT_ATOMIC_LOAD_SSIZE(mp->ma_used) - 1); mp->ma_version_tag = new_version; if (_PyDict_HasSplitTable(mp)) { assert(old_value == mp->ma_values->values[ix]); @@ -6895,7 +6898,7 @@ _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, PyObject **attr } #ifdef Py_GIL_DISABLED - PyObject *value = _Py_atomic_load_ptr_relaxed(&values->values[ix]); + PyObject *value = _Py_atomic_load_ptr_acquire(&values->values[ix]); if (value == NULL || _Py_TryIncrefCompare(&values->values[ix], value)) { *attr = value; return true; diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index aa954ca9b6c09c..e4ca32bebc5a22 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -28,9 +28,6 @@ race:_PyObject_GC_TRACK race:_PyParkingLot_Park race:_PyType_HasFeature race:assign_version_tag -race:compare_unicode_unicode -race:delitem_common -race:dictresize race:gc_collect_main race:gc_restore_tid race:initialize_new_array From c379de224cea2458b15c03cfeffc5506419a62eb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 25 Apr 2024 19:27:51 +0300 Subject: [PATCH 059/217] Remove tests_gui variables from Tkinter tests (GH-118280) They were only used in runtktests.py which was removed in f59ed3c310a7ceebf2a56a84ea969a7f75d95b64 (bpo-45229). --- Lib/test/test_tkinter/test_geometry_managers.py | 4 ---- Lib/test/test_tkinter/test_widgets.py | 8 -------- Lib/test/test_ttk/test_widgets.py | 8 -------- 3 files changed, 20 deletions(-) diff --git a/Lib/test/test_tkinter/test_geometry_managers.py b/Lib/test/test_tkinter/test_geometry_managers.py index 59fe592b492adc..f8f1c895c56340 100644 --- a/Lib/test/test_tkinter/test_geometry_managers.py +++ b/Lib/test/test_tkinter/test_geometry_managers.py @@ -893,9 +893,5 @@ def test_grid_slaves(self): self.assertEqual(self.root.grid_slaves(row=1, column=1), [d, c]) -tests_gui = ( - PackTest, PlaceTest, GridTest, -) - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index d3f942db7baf9a..85bf5ff7652b69 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -1479,13 +1479,5 @@ def test_label(self): self._test_widget(tkinter.Label) -tests_gui = ( - ButtonTest, CanvasTest, CheckbuttonTest, EntryTest, - FrameTest, LabelFrameTest,LabelTest, ListboxTest, - MenubuttonTest, MenuTest, MessageTest, OptionMenuTest, - PanedWindowTest, RadiobuttonTest, ScaleTest, ScrollbarTest, - SpinboxTest, TextTest, ToplevelTest, DefaultRootTest, -) - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_ttk/test_widgets.py b/Lib/test/test_ttk/test_widgets.py index e3e440c45859f7..ca7402b276013d 100644 --- a/Lib/test/test_ttk/test_widgets.py +++ b/Lib/test/test_ttk/test_widgets.py @@ -1879,13 +1879,5 @@ def test_label(self): self._test_widget(ttk.Label) -tests_gui = ( - ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest, - FrameTest, LabelFrameTest, LabelTest, MenubuttonTest, - NotebookTest, PanedWindowTest, ProgressbarTest, - RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest, - SizegripTest, SpinboxTest, TreeviewTest, WidgetTest, DefaultRootTest, - ) - if __name__ == "__main__": unittest.main() From d5df25268b037c2f1f4a1184fad5274d0e72f8d6 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Thu, 25 Apr 2024 15:34:05 -0700 Subject: [PATCH 060/217] gh-112075: _Py_dict_lookup needs to lock shared keys (#117528) Lock shared keys in `Py_dict_lookup` and use thread-safe lookup in `insertdict` Co-authored-by: Sam Gross --- Objects/dictobject.c | 285 ++++++++++++++++++++++++------------------- 1 file changed, 158 insertions(+), 127 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index afcf535f8c0a78..43cb2350b7fc71 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -162,6 +162,16 @@ ASSERT_DICT_LOCKED(PyObject *op) assert(_Py_IsOwnedByCurrentThread((PyObject *)mp) || IS_DICT_SHARED(mp)); #define LOAD_KEYS_NENTRIES(d) +#define LOCK_KEYS_IF_SPLIT(keys, kind) \ + if (kind == DICT_KEYS_SPLIT) { \ + LOCK_KEYS(dk); \ + } + +#define UNLOCK_KEYS_IF_SPLIT(keys, kind) \ + if (kind == DICT_KEYS_SPLIT) { \ + UNLOCK_KEYS(dk); \ + } + static inline Py_ssize_t load_keys_nentries(PyDictObject *mp) { @@ -195,6 +205,9 @@ set_values(PyDictObject *mp, PyDictValues *values) #define DECREF_KEYS(dk) _Py_atomic_add_ssize(&dk->dk_refcnt, -1) #define LOAD_KEYS_NENTIRES(keys) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries) +#define INCREF_KEYS_FT(dk) dictkeys_incref(dk) +#define DECREF_KEYS_FT(dk, shared) dictkeys_decref(_PyInterpreterState_GET(), dk, shared) + static inline void split_keys_entry_added(PyDictKeysObject *keys) { ASSERT_KEYS_LOCKED(keys); @@ -216,6 +229,10 @@ static inline void split_keys_entry_added(PyDictKeysObject *keys) #define INCREF_KEYS(dk) dk->dk_refcnt++ #define DECREF_KEYS(dk) dk->dk_refcnt-- #define LOAD_KEYS_NENTIRES(keys) keys->dk_nentries +#define INCREF_KEYS_FT(dk) +#define DECREF_KEYS_FT(dk, shared) +#define LOCK_KEYS_IF_SPLIT(keys, kind) +#define UNLOCK_KEYS_IF_SPLIT(keys, kind) #define IS_DICT_SHARED(mp) (false) #define SET_DICT_SHARED(mp) #define LOAD_INDEX(keys, size, idx) ((const int##size##_t*)(keys->dk_indices))[idx] @@ -1171,6 +1188,14 @@ _PyDictKeys_StringLookup(PyDictKeysObject* dk, PyObject *key) return unicodekeys_lookup_unicode(dk, key, hash); } +#ifdef Py_GIL_DISABLED + +static Py_ssize_t +unicodekeys_lookup_unicode_threadsafe(PyDictKeysObject* dk, PyObject *key, + Py_hash_t hash); + +#endif + /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. @@ -1200,10 +1225,33 @@ _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **valu if (kind != DICT_KEYS_GENERAL) { if (PyUnicode_CheckExact(key)) { +#ifdef Py_GIL_DISABLED + if (kind == DICT_KEYS_SPLIT) { + // A split dictionaries keys can be mutated by other + // dictionaries but if we have a unicode key we can avoid + // locking the shared keys. + ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash); + if (ix == DKIX_KEY_CHANGED) { + LOCK_KEYS(dk); + ix = unicodekeys_lookup_unicode(dk, key, hash); + UNLOCK_KEYS(dk); + } + } + else { + ix = unicodekeys_lookup_unicode(dk, key, hash); + } +#else ix = unicodekeys_lookup_unicode(dk, key, hash); +#endif } else { + INCREF_KEYS_FT(dk); + LOCK_KEYS_IF_SPLIT(dk, kind); + ix = unicodekeys_lookup_generic(mp, dk, key, hash); + + UNLOCK_KEYS_IF_SPLIT(dk, kind); + DECREF_KEYS_FT(dk, IS_DICT_SHARED(mp)); if (ix == DKIX_KEY_CHANGED) { goto start; } @@ -1607,31 +1655,6 @@ insertion_resize(PyInterpreterState *interp, PyDictObject *mp, int unicode) return dictresize(interp, mp, calculate_log2_keysize(GROWTH_RATE(mp)), unicode); } -static Py_ssize_t -insert_into_splitdictkeys(PyDictKeysObject *keys, PyObject *name, Py_hash_t hash) -{ - assert(PyUnicode_CheckExact(name)); - ASSERT_KEYS_LOCKED(keys); - - Py_ssize_t ix = unicodekeys_lookup_unicode(keys, name, hash); - if (ix == DKIX_EMPTY) { - if (keys->dk_usable <= 0) { - return DKIX_EMPTY; - } - /* Insert into new slot. */ - keys->dk_version = 0; - Py_ssize_t hashpos = find_empty_slot(keys, hash); - ix = keys->dk_nentries; - PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(keys)[ix]; - dictkeys_set_index(keys, hashpos, ix); - assert(ep->me_key == NULL); - ep->me_key = Py_NewRef(name); - split_keys_entry_added(keys); - } - assert (ix < SHARED_KEYS_MAX_SIZE); - return ix; -} - static inline int insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp, Py_hash_t hash, PyObject *key, PyObject *value) @@ -1665,39 +1688,57 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp, return 0; } -static int -insert_split_dict(PyInterpreterState *interp, PyDictObject *mp, - Py_hash_t hash, PyObject *key, PyObject *value) +static Py_ssize_t +insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash) { - PyDictKeysObject *keys = mp->ma_keys; - LOCK_KEYS(keys); - if (keys->dk_usable <= 0) { - /* Need to resize. */ - UNLOCK_KEYS(keys); - int ins = insertion_resize(interp, mp, 1); - if (ins < 0) { - return -1; - } - assert(!_PyDict_HasSplitTable(mp)); - return insert_combined_dict(interp, mp, hash, key, value); - } - - Py_ssize_t hashpos = find_empty_slot(keys, hash); - dictkeys_set_index(keys, hashpos, keys->dk_nentries); + assert(PyUnicode_CheckExact(key)); + Py_ssize_t ix; - PyDictUnicodeEntry *ep; - ep = &DK_UNICODE_ENTRIES(keys)[keys->dk_nentries]; - STORE_SHARED_KEY(ep->me_key, key); - Py_ssize_t index = keys->dk_nentries; - _PyDictValues_AddToInsertionOrder(mp->ma_values, index); - assert (mp->ma_values->values[index] == NULL); - STORE_SPLIT_VALUE(mp, index, value); +#ifdef Py_GIL_DISABLED + ix = unicodekeys_lookup_unicode_threadsafe(keys, key, hash); + if (ix >= 0) { + return ix; + } +#endif - split_keys_entry_added(keys); - assert(keys->dk_usable >= 0); + LOCK_KEYS(keys); + ix = unicodekeys_lookup_unicode(keys, key, hash); + if (ix == DKIX_EMPTY && keys->dk_usable > 0) { + // Insert into new slot + keys->dk_version = 0; + Py_ssize_t hashpos = find_empty_slot(keys, hash); + ix = keys->dk_nentries; + dictkeys_set_index(keys, hashpos, ix); + PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(keys)[ix]; + STORE_SHARED_KEY(ep->me_key, Py_NewRef(key)); + split_keys_entry_added(keys); + } + assert (ix < SHARED_KEYS_MAX_SIZE); UNLOCK_KEYS(keys); - return 0; + return ix; +} + +static void +insert_split_value(PyInterpreterState *interp, PyDictObject *mp, PyObject *key, PyObject *value, Py_ssize_t ix) +{ + assert(PyUnicode_CheckExact(key)); + MAINTAIN_TRACKING(mp, key, value); + PyObject *old_value = mp->ma_values->values[ix]; + if (old_value == NULL) { + uint64_t new_version = _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value); + STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value)); + _PyDictValues_AddToInsertionOrder(mp->ma_values, ix); + mp->ma_used++; + mp->ma_version_tag = new_version; + } + else { + uint64_t new_version = _PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value); + STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value)); + mp->ma_version_tag = new_version; + Py_DECREF(old_value); + } + ASSERT_CONSISTENT(mp); } /* @@ -1720,6 +1761,21 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL); } + if (_PyDict_HasSplitTable(mp)) { + Py_ssize_t ix = insert_split_key(mp->ma_keys, key, hash); + if (ix != DKIX_EMPTY) { + insert_split_value(interp, mp, key, value, ix); + Py_DECREF(key); + Py_DECREF(value); + return 0; + } + + /* No space in shared keys. Resize and continue below. */ + if (insertion_resize(interp, mp, 1) < 0) { + goto Fail; + } + } + Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) goto Fail; @@ -1727,24 +1783,17 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, MAINTAIN_TRACKING(mp, key, value); if (ix == DKIX_EMPTY) { + assert(!_PyDict_HasSplitTable(mp)); uint64_t new_version = _PyDict_NotifyEvent( interp, PyDict_EVENT_ADDED, mp, key, value); /* Insert into new slot. */ mp->ma_keys->dk_version = 0; assert(old_value == NULL); - - if (!_PyDict_HasSplitTable(mp)) { - if (insert_combined_dict(interp, mp, hash, key, value) < 0) { - goto Fail; - } - } - else { - if (insert_split_dict(interp, mp, hash, key, value) < 0) - goto Fail; + if (insert_combined_dict(interp, mp, hash, key, value) < 0) { + goto Fail; } - - mp->ma_used++; mp->ma_version_tag = new_version; + mp->ma_used++; ASSERT_CONSISTENT(mp); return 0; } @@ -1752,23 +1801,15 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, if (old_value != value) { uint64_t new_version = _PyDict_NotifyEvent( interp, PyDict_EVENT_MODIFIED, mp, key, value); - if (_PyDict_HasSplitTable(mp)) { - STORE_SPLIT_VALUE(mp, ix, value); - if (old_value == NULL) { - _PyDictValues_AddToInsertionOrder(mp->ma_values, ix); - mp->ma_used++; - } + assert(old_value != NULL); + assert(!_PyDict_HasSplitTable(mp)); + if (DK_IS_UNICODE(mp->ma_keys)) { + PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[ix]; + STORE_VALUE(ep, value); } else { - assert(old_value != NULL); - if (DK_IS_UNICODE(mp->ma_keys)) { - PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[ix]; - STORE_VALUE(ep, value); - } - else { - PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix]; - STORE_VALUE(ep, value); - } + PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix]; + STORE_VALUE(ep, value); } mp->ma_version_tag = new_version; } @@ -4177,6 +4218,29 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu } } + if (_PyDict_HasSplitTable(mp)) { + Py_ssize_t ix = insert_split_key(mp->ma_keys, key, hash); + if (ix != DKIX_EMPTY) { + PyObject *value = mp->ma_values->values[ix]; + int already_present = value != NULL; + if (!already_present) { + insert_split_value(interp, mp, key, default_value, ix); + value = default_value; + } + if (result) { + *result = incref_result ? Py_NewRef(value) : value; + } + return already_present; + } + + /* No space in shared keys. Resize and continue below. */ + if (insertion_resize(interp, mp, 1) < 0) { + goto error; + } + } + + assert(!_PyDict_HasSplitTable(mp)); + Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &value); if (ix == DKIX_ERROR) { if (result) { @@ -4186,29 +4250,17 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu } if (ix == DKIX_EMPTY) { + assert(!_PyDict_HasSplitTable(mp)); uint64_t new_version = _PyDict_NotifyEvent( - interp, PyDict_EVENT_ADDED, mp, key, default_value); + interp, PyDict_EVENT_ADDED, mp, key, default_value); mp->ma_keys->dk_version = 0; value = default_value; - if (!_PyDict_HasSplitTable(mp)) { - if (insert_combined_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) { - Py_DECREF(key); - Py_DECREF(value); - if (result) { - *result = NULL; - } - return -1; - } - } - else { - if (insert_split_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) { - Py_DECREF(key); - Py_DECREF(value); - if (result) { - *result = NULL; - } - return -1; + if (insert_combined_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) { + Py_DECREF(key); + Py_DECREF(value); + if (result) { + *result = NULL; } } @@ -4222,29 +4274,19 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu } return 0; } - else if (value == NULL) { - uint64_t new_version = _PyDict_NotifyEvent( - interp, PyDict_EVENT_ADDED, mp, key, default_value); - value = default_value; - assert(_PyDict_HasSplitTable(mp)); - assert(mp->ma_values->values[ix] == NULL); - MAINTAIN_TRACKING(mp, key, value); - STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value)); - _PyDictValues_AddToInsertionOrder(mp->ma_values, ix); - mp->ma_used++; - mp->ma_version_tag = new_version; - ASSERT_CONSISTENT(mp); - if (result) { - *result = incref_result ? Py_NewRef(value) : value; - } - return 0; - } + assert(value != NULL); ASSERT_CONSISTENT(mp); if (result) { *result = incref_result ? Py_NewRef(value) : value; } return 1; + +error: + if (result) { + *result = NULL; + } + return -1; } int @@ -6699,18 +6741,7 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values, assert(hash != -1); } -#ifdef Py_GIL_DISABLED - // Try a thread-safe lookup to see if the index is already allocated - ix = unicodekeys_lookup_unicode_threadsafe(keys, name, hash); - if (ix == DKIX_EMPTY || ix == DKIX_KEY_CHANGED) { - // Lock keys and do insert - LOCK_KEYS(keys); - ix = insert_into_splitdictkeys(keys, name, hash); - UNLOCK_KEYS(keys); - } -#else - ix = insert_into_splitdictkeys(keys, name, hash); -#endif + ix = insert_split_key(keys, name, hash); #ifdef Py_STATS if (ix == DKIX_EMPTY) { From 09c29475813ff2a763931fc0b45aaaef57cd2ac7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 25 Apr 2024 19:05:51 -0600 Subject: [PATCH 061/217] gh-110693: Pending Calls Machinery Cleanups (gh-118296) This does some cleanup in preparation for later changes. --- Include/internal/pycore_ceval.h | 6 +- Include/internal/pycore_ceval_state.h | 52 ++++++-- Include/internal/pycore_runtime_init.h | 8 ++ Lib/test/test_capi/test_misc.py | 85 ++++++++++--- Modules/_testcapimodule.c | 53 +++++++-- Modules/_testinternalcapi.c | 63 ++++++---- Python/ceval_gil.c | 159 ++++++++++++++++--------- 7 files changed, 314 insertions(+), 112 deletions(-) diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 8d88b5c1d15cb8..cfb88c3f4c8e15 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -48,8 +48,12 @@ extern void _PyEval_SignalReceived(void); #define _Py_PENDING_MAINTHREADONLY 1 #define _Py_PENDING_RAWFREE 2 +typedef int _Py_add_pending_call_result; +#define _Py_ADD_PENDING_SUCCESS 0 +#define _Py_ADD_PENDING_FULL -1 + // Export for '_testinternalcapi' shared extension -PyAPI_FUNC(int) _PyEval_AddPendingCall( +PyAPI_FUNC(_Py_add_pending_call_result) _PyEval_AddPendingCall( PyInterpreterState *interp, _Py_pending_call_func func, void *arg, diff --git a/Include/internal/pycore_ceval_state.h b/Include/internal/pycore_ceval_state.h index 168295534e036c..1831f58899b745 100644 --- a/Include/internal/pycore_ceval_state.h +++ b/Include/internal/pycore_ceval_state.h @@ -14,28 +14,56 @@ extern "C" { typedef int (*_Py_pending_call_func)(void *); +struct _pending_call { + _Py_pending_call_func func; + void *arg; + int flags; +}; + +#define PENDINGCALLSARRAYSIZE 32 + +#define MAXPENDINGCALLS PENDINGCALLSARRAYSIZE +/* For interpreter-level pending calls, we want to avoid spending too + much time on pending calls in any one thread, so we apply a limit. */ +#if MAXPENDINGCALLS > 100 +# define MAXPENDINGCALLSLOOP 100 +#else +# define MAXPENDINGCALLSLOOP MAXPENDINGCALLS +#endif + +#define MAXPENDINGCALLS_MAIN PENDINGCALLSARRAYSIZE +/* For the main thread, we want to make sure all pending calls are + run at once, for the sake of prompt signal handling. This is + unlikely to cause any problems since there should be very few + pending calls for the main thread. */ +#define MAXPENDINGCALLSLOOP_MAIN 0 + struct _pending_calls { int busy; PyMutex mutex; /* Request for running pending calls. */ - int32_t calls_to_do; -#define NPENDINGCALLS 32 - struct _pending_call { - _Py_pending_call_func func; - void *arg; - int flags; - } calls[NPENDINGCALLS]; + int32_t npending; + /* The maximum allowed number of pending calls. + If the queue fills up to this point then _PyEval_AddPendingCall() + will return _Py_ADD_PENDING_FULL. */ + int32_t max; + /* We don't want a flood of pending calls to interrupt any one thread + for too long, so we keep a limit on the number handled per pass. + A value of 0 means there is no limit (other than the maximum + size of the list of pending calls). */ + int32_t maxloop; + struct _pending_call calls[PENDINGCALLSARRAYSIZE]; int first; - int last; + int next; }; + typedef enum { PERF_STATUS_FAILED = -1, // Perf trampoline is in an invalid state PERF_STATUS_NO_INIT = 0, // Perf trampoline is not initialized PERF_STATUS_OK = 1, // Perf trampoline is ready to be executed } perf_status_t; - #ifdef PY_HAVE_PERF_TRAMPOLINE struct code_arena_st; @@ -48,6 +76,7 @@ struct trampoline_api_st { }; #endif + struct _ceval_runtime_state { struct { #ifdef PY_HAVE_PERF_TRAMPOLINE @@ -62,10 +91,15 @@ struct _ceval_runtime_state { #endif } perf; /* Pending calls to be made only on the main thread. */ + // The signal machinery falls back on this + // so it must be especially stable and efficient. + // For example, we use a preallocated array + // for the list of pending calls. struct _pending_calls pending_mainthread; PyMutex sys_trace_profile_mutex; }; + #ifdef PY_HAVE_PERF_TRAMPOLINE # define _PyEval_RUNTIME_PERF_INIT \ { \ diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 33c7a9dadfd2a1..41331df8320a9c 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -114,6 +114,10 @@ extern PyTypeObject _PyExc_MemoryError; .autoTSSkey = Py_tss_NEEDS_INIT, \ .parser = _parser_runtime_state_INIT, \ .ceval = { \ + .pending_mainthread = { \ + .max = MAXPENDINGCALLS_MAIN, \ + .maxloop = MAXPENDINGCALLSLOOP_MAIN, \ + }, \ .perf = _PyEval_RUNTIME_PERF_INIT, \ }, \ .gilstate = { \ @@ -166,6 +170,10 @@ extern PyTypeObject _PyExc_MemoryError; .imports = IMPORTS_INIT, \ .ceval = { \ .recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \ + .pending = { \ + .max = MAXPENDINGCALLS, \ + .maxloop = MAXPENDINGCALLSLOOP, \ + }, \ }, \ .gc = { \ .enabled = 1, \ diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 0701eafb7c36e0..49d1056f050467 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1172,6 +1172,12 @@ class MyType: self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname') + def test_gen_get_code(self): + def genf(): yield + gen = genf() + self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code) + + @requires_limited_api class TestHeapTypeRelative(unittest.TestCase): """Test API for extending opaque types (PEP 697)""" @@ -1452,7 +1458,7 @@ class TestPendingCalls(unittest.TestCase): # about when pending calls get run. This is especially relevant # here for creating deterministic tests. - def pendingcalls_submit(self, l, n): + def main_pendingcalls_submit(self, l, n): def callback(): #this function can be interrupted by thread switching so let's #use an atomic operation @@ -1467,12 +1473,27 @@ def callback(): if _testcapi._pending_threadfunc(callback): break - def pendingcalls_wait(self, l, n, context = None): + def pendingcalls_submit(self, l, n, *, main=True, ensure=False): + def callback(): + #this function can be interrupted by thread switching so let's + #use an atomic operation + l.append(None) + + if main: + return _testcapi._pending_threadfunc(callback, n, + blocking=False, + ensure_added=ensure) + else: + return _testinternalcapi.pending_threadfunc(callback, n, + blocking=False, + ensure_added=ensure) + + def pendingcalls_wait(self, l, numadded, context = None): #now, stick around until l[0] has grown to 10 count = 0 - while len(l) != n: + while len(l) != numadded: #this busy loop is where we expect to be interrupted to - #run our callbacks. Note that callbacks are only run on the + #run our callbacks. Note that some callbacks are only run on the #main thread if False and support.verbose: print("(%i)"%(len(l),),) @@ -1482,12 +1503,12 @@ def pendingcalls_wait(self, l, n, context = None): continue count += 1 self.assertTrue(count < 10000, - "timeout waiting for %i callbacks, got %i"%(n, len(l))) + "timeout waiting for %i callbacks, got %i"%(numadded, len(l))) if False and support.verbose: print("(%i)"%(len(l),)) @threading_helper.requires_working_threading() - def test_pendingcalls_threaded(self): + def test_main_pendingcalls_threaded(self): #do every callback on a separate thread n = 32 #total callbacks @@ -1501,15 +1522,15 @@ class foo(object):pass context.lock = threading.Lock() context.event = threading.Event() - threads = [threading.Thread(target=self.pendingcalls_thread, + threads = [threading.Thread(target=self.main_pendingcalls_thread, args=(context,)) for i in range(context.nThreads)] with threading_helper.start_threads(threads): self.pendingcalls_wait(context.l, n, context) - def pendingcalls_thread(self, context): + def main_pendingcalls_thread(self, context): try: - self.pendingcalls_submit(context.l, context.n) + self.main_pendingcalls_submit(context.l, context.n) finally: with context.lock: context.nFinished += 1 @@ -1519,20 +1540,54 @@ def pendingcalls_thread(self, context): if nFinished == context.nThreads: context.event.set() - def test_pendingcalls_non_threaded(self): + def test_main_pendingcalls_non_threaded(self): #again, just using the main thread, likely they will all be dispatched at #once. It is ok to ask for too many, because we loop until we find a slot. #the loop can be interrupted to dispatch. #there are only 32 dispatch slots, so we go for twice that! l = [] n = 64 - self.pendingcalls_submit(l, n) + self.main_pendingcalls_submit(l, n) self.pendingcalls_wait(l, n) - def test_gen_get_code(self): - def genf(): yield - gen = genf() - self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code) + def test_max_pending(self): + with self.subTest('main-only'): + maxpending = 32 + + l = [] + added = self.pendingcalls_submit(l, 1, main=True) + self.pendingcalls_wait(l, added) + self.assertEqual(added, 1) + + l = [] + added = self.pendingcalls_submit(l, maxpending, main=True) + self.pendingcalls_wait(l, added) + self.assertEqual(added, maxpending) + + l = [] + added = self.pendingcalls_submit(l, maxpending+1, main=True) + self.pendingcalls_wait(l, added) + self.assertEqual(added, maxpending) + + with self.subTest('not main-only'): + # Per-interpreter pending calls has the same low limit + # on how many may be pending at a time. + maxpending = 32 + + l = [] + added = self.pendingcalls_submit(l, 1, main=False) + self.pendingcalls_wait(l, added) + self.assertEqual(added, 1) + + l = [] + added = self.pendingcalls_submit(l, maxpending, main=False) + self.pendingcalls_wait(l, added) + self.assertEqual(added, maxpending) + + l = [] + added = self.pendingcalls_submit(l, maxpending+1, main=False) + self.pendingcalls_wait(l, added) + self.assertEqual(added, maxpending) class PendingTask(types.SimpleNamespace): diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 034a30fa47ed30..0bdd252efdabc7 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -819,25 +819,55 @@ static int _pending_callback(void *arg) * run from any python thread. */ static PyObject * -pending_threadfunc(PyObject *self, PyObject *arg) +pending_threadfunc(PyObject *self, PyObject *arg, PyObject *kwargs) { + static char *kwlist[] = {"callback", "num", + "blocking", "ensure_added", NULL}; PyObject *callable; - int r; - if (PyArg_ParseTuple(arg, "O", &callable) == 0) + unsigned int num = 1; + int blocking = 0; + int ensure_added = 0; + if (!PyArg_ParseTupleAndKeywords(arg, kwargs, + "O|I$pp:_pending_threadfunc", kwlist, + &callable, &num, &blocking, &ensure_added)) + { return NULL; + } /* create the reference for the callbackwhile we hold the lock */ - Py_INCREF(callable); + for (unsigned int i = 0; i < num; i++) { + Py_INCREF(callable); + } - Py_BEGIN_ALLOW_THREADS - r = Py_AddPendingCall(&_pending_callback, callable); - Py_END_ALLOW_THREADS + PyThreadState *save_tstate = NULL; + if (!blocking) { + save_tstate = PyEval_SaveThread(); + } + + unsigned int num_added = 0; + for (; num_added < num; num_added++) { + if (ensure_added) { + int r; + do { + r = Py_AddPendingCall(&_pending_callback, callable); + } while (r < 0); + } + else { + if (Py_AddPendingCall(&_pending_callback, callable) < 0) { + break; + } + } + } + + if (!blocking) { + PyEval_RestoreThread(save_tstate); + } - if (r<0) { + for (unsigned int i = num_added; i < num; i++) { Py_DECREF(callable); /* unsuccessful add, destroy the extra reference */ - Py_RETURN_FALSE; } - Py_RETURN_TRUE; + /* The callable is decref'ed above in each added _pending_callback(). */ + return PyLong_FromUnsignedLong((unsigned long)num_added); } /* Test PyOS_string_to_double. */ @@ -3232,7 +3262,8 @@ static PyMethodDef TestMethods[] = { {"_spawn_pthread_waiter", spawn_pthread_waiter, METH_NOARGS}, {"_end_spawned_pthread", end_spawned_pthread, METH_NOARGS}, #endif - {"_pending_threadfunc", pending_threadfunc, METH_VARARGS}, + {"_pending_threadfunc", _PyCFunction_CAST(pending_threadfunc), + METH_VARARGS|METH_KEYWORDS}, #ifdef HAVE_GETTIMEOFDAY {"profile_int", profile_int, METH_NOARGS}, #endif diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index cc9e1403f87ecd..b0bba3422a50a0 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1062,37 +1062,56 @@ static PyObject * pending_threadfunc(PyObject *self, PyObject *args, PyObject *kwargs) { PyObject *callable; + unsigned int num = 1; + int blocking = 0; int ensure_added = 0; - static char *kwlist[] = {"", "ensure_added", NULL}; + static char *kwlist[] = {"callback", "num", + "blocking", "ensure_added", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "O|$p:pending_threadfunc", kwlist, - &callable, &ensure_added)) + "O|I$pp:pending_threadfunc", kwlist, + &callable, &num, &blocking, &ensure_added)) { return NULL; } PyInterpreterState *interp = _PyInterpreterState_GET(); /* create the reference for the callbackwhile we hold the lock */ - Py_INCREF(callable); + for (unsigned int i = 0; i < num; i++) { + Py_INCREF(callable); + } - int r; - Py_BEGIN_ALLOW_THREADS - r = _PyEval_AddPendingCall(interp, &_pending_callback, callable, 0); - Py_END_ALLOW_THREADS - if (r < 0) { - /* unsuccessful add */ - if (!ensure_added) { - Py_DECREF(callable); - Py_RETURN_FALSE; + PyThreadState *save_tstate = NULL; + if (!blocking) { + save_tstate = PyEval_SaveThread(); + } + + unsigned int num_added = 0; + for (; num_added < num; num_added++) { + if (ensure_added) { + _Py_add_pending_call_result r; + do { + r = _PyEval_AddPendingCall(interp, &_pending_callback, callable, 0); + assert(r == _Py_ADD_PENDING_SUCCESS + || r == _Py_ADD_PENDING_FULL); + } while (r == _Py_ADD_PENDING_FULL); + } + else { + if (_PyEval_AddPendingCall(interp, &_pending_callback, callable, 0) < 0) { + break; + } } - do { - Py_BEGIN_ALLOW_THREADS - r = _PyEval_AddPendingCall(interp, &_pending_callback, callable, 0); - Py_END_ALLOW_THREADS - } while (r < 0); } - Py_RETURN_TRUE; + if (!blocking) { + PyEval_RestoreThread(save_tstate); + } + + for (unsigned int i = num_added; i < num; i++) { + Py_DECREF(callable); /* unsuccessful add, destroy the extra reference */ + } + + /* The callable is decref'ed in _pending_callback() above. */ + return PyLong_FromUnsignedLong((unsigned long)num_added); } @@ -1135,14 +1154,16 @@ pending_identify(PyObject *self, PyObject *args) PyThread_acquire_lock(mutex, WAIT_LOCK); /* It gets released in _pending_identify_callback(). */ - int r; + _Py_add_pending_call_result r; do { Py_BEGIN_ALLOW_THREADS r = _PyEval_AddPendingCall(interp, &_pending_identify_callback, (void *)mutex, 0); Py_END_ALLOW_THREADS - } while (r < 0); + assert(r == _Py_ADD_PENDING_SUCCESS + || r == _Py_ADD_PENDING_FULL); + } while (r == _Py_ADD_PENDING_FULL); /* Wait for the pending call to complete. */ PyThread_acquire_lock(mutex, WAIT_LOCK); diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index c0819d8ab1d8d0..c3c2c54b199c59 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -84,15 +84,15 @@ update_eval_breaker_for_thread(PyInterpreterState *interp, PyThreadState *tstate return; #endif - int32_t calls_to_do = _Py_atomic_load_int32_relaxed( - &interp->ceval.pending.calls_to_do); - if (calls_to_do) { + int32_t npending = _Py_atomic_load_int32_relaxed( + &interp->ceval.pending.npending); + if (npending) { _Py_set_eval_breaker_bit(tstate, _PY_CALLS_TO_DO_BIT); } else if (_Py_IsMainThread()) { - calls_to_do = _Py_atomic_load_int32_relaxed( - &_PyRuntime.ceval.pending_mainthread.calls_to_do); - if (calls_to_do) { + npending = _Py_atomic_load_int32_relaxed( + &_PyRuntime.ceval.pending_mainthread.npending); + if (npending) { _Py_set_eval_breaker_bit(tstate, _PY_CALLS_TO_DO_BIT); } } @@ -624,6 +624,34 @@ PyEval_RestoreThread(PyThreadState *tstate) } +void +_PyEval_SignalReceived(void) +{ + _Py_set_eval_breaker_bit(_PyRuntime.main_tstate, _PY_SIGNALS_PENDING_BIT); +} + + +#ifndef Py_GIL_DISABLED +static void +signal_active_thread(PyInterpreterState *interp, uintptr_t bit) +{ + struct _gil_runtime_state *gil = interp->ceval.gil; + + // If a thread from the targeted interpreter is holding the GIL, signal + // that thread. Otherwise, the next thread to run from the targeted + // interpreter will have its bit set as part of taking the GIL. + MUTEX_LOCK(gil->mutex); + if (_Py_atomic_load_int_relaxed(&gil->locked)) { + PyThreadState *holder = (PyThreadState*)_Py_atomic_load_ptr_relaxed(&gil->last_holder); + if (holder->interp == interp) { + _Py_set_eval_breaker_bit(holder, bit); + } + } + MUTEX_UNLOCK(gil->mutex); +} +#endif + + /* Mechanism whereby asynchronously executing callbacks (e.g. UNIX signal handlers or Mac I/O completion routines) can schedule calls to a function to be called synchronously. @@ -646,29 +674,31 @@ PyEval_RestoreThread(PyThreadState *tstate) threadstate. */ -void -_PyEval_SignalReceived(void) -{ - _Py_set_eval_breaker_bit(_PyRuntime.main_tstate, _PY_SIGNALS_PENDING_BIT); -} - /* Push one item onto the queue while holding the lock. */ static int _push_pending_call(struct _pending_calls *pending, _Py_pending_call_func func, void *arg, int flags) { - int i = pending->last; - int j = (i + 1) % NPENDINGCALLS; - if (j == pending->first) { - return -1; /* Queue full */ + if (pending->npending == pending->max) { + return _Py_ADD_PENDING_FULL; } + assert(pending->npending < pending->max); + + int i = pending->next; + assert(pending->calls[i].func == NULL); + pending->calls[i].func = func; pending->calls[i].arg = arg; pending->calls[i].flags = flags; - pending->last = j; - assert(pending->calls_to_do < NPENDINGCALLS); - _Py_atomic_add_int32(&pending->calls_to_do, 1); - return 0; + + assert(pending->npending < PENDINGCALLSARRAYSIZE); + _Py_atomic_add_int32(&pending->npending, 1); + + pending->next = (i + 1) % PENDINGCALLSARRAYSIZE; + assert(pending->next != pending->first + || pending->npending == pending->max); + + return _Py_ADD_PENDING_SUCCESS; } static int @@ -676,8 +706,9 @@ _next_pending_call(struct _pending_calls *pending, int (**func)(void *), void **arg, int *flags) { int i = pending->first; - if (i == pending->last) { + if (pending->npending == 0) { /* Queue empty */ + assert(i == pending->next); assert(pending->calls[i].func == NULL); return -1; } @@ -695,38 +726,18 @@ _pop_pending_call(struct _pending_calls *pending, int i = _next_pending_call(pending, func, arg, flags); if (i >= 0) { pending->calls[i] = (struct _pending_call){0}; - pending->first = (i + 1) % NPENDINGCALLS; - assert(pending->calls_to_do > 0); - _Py_atomic_add_int32(&pending->calls_to_do, -1); + pending->first = (i + 1) % PENDINGCALLSARRAYSIZE; + assert(pending->npending > 0); + _Py_atomic_add_int32(&pending->npending, -1); } } -#ifndef Py_GIL_DISABLED -static void -signal_active_thread(PyInterpreterState *interp, uintptr_t bit) -{ - struct _gil_runtime_state *gil = interp->ceval.gil; - - // If a thread from the targeted interpreter is holding the GIL, signal - // that thread. Otherwise, the next thread to run from the targeted - // interpreter will have its bit set as part of taking the GIL. - MUTEX_LOCK(gil->mutex); - if (_Py_atomic_load_int_relaxed(&gil->locked)) { - PyThreadState *holder = (PyThreadState*)_Py_atomic_load_ptr_relaxed(&gil->last_holder); - if (holder->interp == interp) { - _Py_set_eval_breaker_bit(holder, bit); - } - } - MUTEX_UNLOCK(gil->mutex); -} -#endif - /* This implementation is thread-safe. It allows scheduling to be made from any thread, and even from an executing callback. */ -int +_Py_add_pending_call_result _PyEval_AddPendingCall(PyInterpreterState *interp, _Py_pending_call_func func, void *arg, int flags) { @@ -739,7 +750,8 @@ _PyEval_AddPendingCall(PyInterpreterState *interp, } PyMutex_Lock(&pending->mutex); - int result = _push_pending_call(pending, func, arg, flags); + _Py_add_pending_call_result result = + _push_pending_call(pending, func, arg, flags); PyMutex_Unlock(&pending->mutex); if (main_only) { @@ -762,7 +774,15 @@ Py_AddPendingCall(_Py_pending_call_func func, void *arg) /* Legacy users of this API will continue to target the main thread (of the main interpreter). */ PyInterpreterState *interp = _PyInterpreterState_Main(); - return _PyEval_AddPendingCall(interp, func, arg, _Py_PENDING_MAINTHREADONLY); + _Py_add_pending_call_result r = + _PyEval_AddPendingCall(interp, func, arg, _Py_PENDING_MAINTHREADONLY); + if (r == _Py_ADD_PENDING_FULL) { + return -1; + } + else { + assert(r == _Py_ADD_PENDING_SUCCESS); + return 0; + } } static int @@ -782,10 +802,21 @@ handle_signals(PyThreadState *tstate) } static int -_make_pending_calls(struct _pending_calls *pending) +_make_pending_calls(struct _pending_calls *pending, int32_t *p_npending) { + int res = 0; + int32_t npending = -1; + + assert(sizeof(pending->max) <= sizeof(size_t) + && ((size_t)pending->max) <= Py_ARRAY_LENGTH(pending->calls)); + int32_t maxloop = pending->maxloop; + if (maxloop == 0) { + maxloop = pending->max; + } + assert(maxloop > 0 && maxloop <= pending->max); + /* perform a bounded number of calls, in case of recursion */ - for (int i=0; imutex); _pop_pending_call(pending, &func, &arg, &flags); + npending = pending->npending; PyMutex_Unlock(&pending->mutex); - /* having released the lock, perform the callback */ + /* Check if there are any more pending calls. */ if (func == NULL) { + assert(npending == 0); break; } - int res = func(arg); + + /* having released the lock, perform the callback */ + res = func(arg); if ((flags & _Py_PENDING_RAWFREE) && arg != NULL) { PyMem_RawFree(arg); } if (res != 0) { - return -1; + res = -1; + goto finally; } } - return 0; + +finally: + *p_npending = npending; + return res; } static void @@ -861,26 +900,36 @@ make_pending_calls(PyThreadState *tstate) added in-between re-signals */ unsignal_pending_calls(tstate, interp); - if (_make_pending_calls(pending) != 0) { + int32_t npending; + if (_make_pending_calls(pending, &npending) != 0) { pending->busy = 0; /* There might not be more calls to make, but we play it safe. */ signal_pending_calls(tstate, interp); return -1; } + if (npending > 0) { + /* We hit pending->maxloop. */ + signal_pending_calls(tstate, interp); + } if (_Py_IsMainThread() && _Py_IsMainInterpreter(interp)) { - if (_make_pending_calls(pending_main) != 0) { + if (_make_pending_calls(pending_main, &npending) != 0) { pending->busy = 0; /* There might not be more calls to make, but we play it safe. */ signal_pending_calls(tstate, interp); return -1; } + if (npending > 0) { + /* We hit pending_main->maxloop. */ + signal_pending_calls(tstate, interp); + } } pending->busy = 0; return 0; } + void _Py_set_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit) { From ef940dec409f0a9e4f353c6188990aeb3ad4ffb4 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 26 Apr 2024 11:01:30 +0200 Subject: [PATCH 062/217] gh-118235: Skip RAISE_SYNTAX_ERROR rules in the grammar spec (GH-118237) --- Doc/tools/extensions/peg_highlight.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/tools/extensions/peg_highlight.py b/Doc/tools/extensions/peg_highlight.py index 4bdc2ee1861334..5ab5530d269901 100644 --- a/Doc/tools/extensions/peg_highlight.py +++ b/Doc/tools/extensions/peg_highlight.py @@ -16,6 +16,7 @@ class PEGLexer(RegexLexer): - Rule types - Rule options - Rules named `invalid_*` or `incorrect_*` + - Rules with `RAISE_SYNTAX_ERROR` """ name = "PEG" @@ -59,6 +60,7 @@ class PEGLexer(RegexLexer): (r"^(\s+\|\s+.*invalid_\w+.*\n)", bygroups(None)), (r"^(\s+\|\s+.*incorrect_\w+.*\n)", bygroups(None)), (r"^(#.*invalid syntax.*(?:.|\n)*)", bygroups(None),), + (r"^(\s+\|\s+.*\{[^}]*RAISE_SYNTAX_ERROR[^}]*\})\n", bygroups(None)), ], "root": [ include("invalids"), From 463c20dae9ac91d6c44028177b90f43238b819cf Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Fri, 26 Apr 2024 13:10:16 +0300 Subject: [PATCH 063/217] gh-117928: Bump the minimum Sphinx version to 6.2.1 (#117853) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .github/workflows/reusable-docs.yml | 2 +- Doc/conf.py | 4 +- Doc/requirements-oldest-sphinx.txt | 38 +++++++++---------- Doc/tools/extensions/c_annotations.py | 11 ------ Doc/tools/extensions/pyspecific.py | 8 +--- ...-04-25-22-12-20.gh-issue-117928.LKdTno.rst | 1 + 6 files changed, 24 insertions(+), 40 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2024-04-25-22-12-20.gh-issue-117928.LKdTno.rst diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index cea8f93d67b29c..9e26d7847d2bd3 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -74,7 +74,7 @@ jobs: - name: 'Set up Python' uses: actions/setup-python@v5 with: - python-version: '3.11' # known to work with Sphinx 4.2 + python-version: '3.12' # known to work with Sphinx 6.2.1 cache: 'pip' cache-dependency-path: 'Doc/requirements-oldest-sphinx.txt' - name: 'Install build dependencies' diff --git a/Doc/conf.py b/Doc/conf.py index e7b688e9e6e0a8..73abe8276f29fe 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -298,8 +298,8 @@ 'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'], } -# Avoid a warning with Sphinx >= 2.0 -master_doc = 'contents' +# Avoid a warning with Sphinx >= 4.0 +root_doc = 'contents' # Allow translation of index directives gettext_additional_targets = [ diff --git a/Doc/requirements-oldest-sphinx.txt b/Doc/requirements-oldest-sphinx.txt index 597341d99ffeaa..9a5f4f3676e089 100644 --- a/Doc/requirements-oldest-sphinx.txt +++ b/Doc/requirements-oldest-sphinx.txt @@ -7,29 +7,29 @@ blurb python-docs-theme>=2022.1 # Generated from: -# pip install "Sphinx~=4.2.0" +# pip install "Sphinx~=6.2.1" # pip freeze # -# Sphinx 4.2 comes from ``needs_sphinx = '4.2'`` in ``Doc/conf.py``. +# Sphinx 6.2.1 comes from ``needs_sphinx = '6.2.1'`` in ``Doc/conf.py``. -alabaster==0.7.13 -Babel==2.13.0 -certifi==2023.7.22 -charset-normalizer==3.3.0 -docutils==0.17.1 -idna==3.4 +alabaster==0.7.16 +Babel==2.14.0 +certifi==2024.2.2 +charset-normalizer==3.3.2 +docutils==0.19 +idna==3.7 imagesize==1.4.1 -Jinja2==3.1.2 -MarkupSafe==2.1.3 -packaging==23.2 -Pygments==2.16.1 +Jinja2==3.1.3 +MarkupSafe==2.1.5 +packaging==24.0 +Pygments==2.17.2 requests==2.31.0 snowballstemmer==2.2.0 -Sphinx==4.2.0 -sphinxcontrib-applehelp==1.0.4 -sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==2.0.1 +Sphinx==6.2.1 +sphinxcontrib-applehelp==1.0.8 +sphinxcontrib-devhelp==1.0.6 +sphinxcontrib-htmlhelp==2.0.5 sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.5 -urllib3==2.0.7 +sphinxcontrib-qthelp==1.0.7 +sphinxcontrib-serializinghtml==1.1.10 +urllib3==2.2.1 diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index abd0a8c817f154..7916b178f1c0f1 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -19,7 +19,6 @@ """ from os import path -import docutils from docutils import nodes from docutils.parsers.rst import directives from docutils.parsers.rst import Directive @@ -40,16 +39,6 @@ } -# Monkeypatch nodes.Node.findall for forwards compatibility -# This patch can be dropped when the minimum Sphinx version is 4.4.0 -# or the minimum Docutils version is 0.18.1. -if docutils.__version_info__ < (0, 18, 1): - def findall(self, *args, **kwargs): - return iter(self.traverse(*args, **kwargs)) - - nodes.Node.findall = findall - - class RCEntry: def __init__(self, name): self.name = name diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index 8c88612cf68180..44db77af5d24d3 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -27,13 +27,7 @@ from sphinx.util import logging from sphinx.util.docutils import SphinxDirective from sphinx.writers.text import TextWriter, TextTranslator - -try: - # Sphinx 6+ - from sphinx.util.display import status_iterator -except ImportError: - # Deprecated in Sphinx 6.1, will be removed in Sphinx 8 - from sphinx.util import status_iterator +from sphinx.util.display import status_iterator ISSUE_URI = 'https://bugs.python.org/issue?@action=redirect&bpo=%s' diff --git a/Misc/NEWS.d/next/Documentation/2024-04-25-22-12-20.gh-issue-117928.LKdTno.rst b/Misc/NEWS.d/next/Documentation/2024-04-25-22-12-20.gh-issue-117928.LKdTno.rst new file mode 100644 index 00000000000000..c8a2a6b759bae9 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2024-04-25-22-12-20.gh-issue-117928.LKdTno.rst @@ -0,0 +1 @@ +The minimum Sphinx version required for the documentation is now 6.2.1. From 5a4d3df2fa02409ffd2a90cd75b67370206e9891 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 26 Apr 2024 16:06:53 +0200 Subject: [PATCH 064/217] Fix note in Enum.__new__ docs (#118284) --- Doc/library/enum.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index d84d9d9b4161b1..00f617e5ffc5e7 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -279,7 +279,7 @@ Data Types >>> Color.RED.value 1 - Value of the member, can be set in :meth:`~object.__new__`. + Value of the member, can be set in :meth:`~Enum.__new__`. .. note:: Enum member values @@ -299,7 +299,7 @@ Data Types .. attribute:: Enum._value_ - Value of the member, can be set in :meth:`~object.__new__`. + Value of the member, can be set in :meth:`~Enum.__new__`. .. attribute:: Enum._order_ @@ -407,8 +407,8 @@ Data Types results in the call ``int('1a', 16)`` and a value of ``17`` for the member. - ..note:: When writing a custom ``__new__``, do not use ``super().__new__`` -- - call the appropriate ``__new__`` instead. + .. note:: When writing a custom ``__new__``, do not use ``super().__new__`` -- + call the appropriate ``__new__`` instead. .. method:: Enum.__repr__(self) @@ -827,7 +827,7 @@ Supported ``__dunder__`` names :attr:`~EnumType.__members__` is a read-only ordered mapping of ``member_name``:``member`` items. It is only available on the class. -:meth:`~object.__new__`, if specified, must create and return the enum members; +:meth:`~Enum.__new__`, if specified, must create and return the enum members; it is also a very good idea to set the member's :attr:`!_value_` appropriately. Once all the members are created it is no longer used. From a5eeb832c2bbbd6ce1e9d545a553de926af468d5 Mon Sep 17 00:00:00 2001 From: mpage Date: Fri, 26 Apr 2024 07:39:08 -0700 Subject: [PATCH 065/217] gh-117657: Fix race data race in `_Py_IsOwnedByCurrentThread()` (#118258) --- Include/object.h | 4 ++++ Tools/tsan/suppressions_free_threading.txt | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Include/object.h b/Include/object.h index 5aaf11c5194f0e..9132784628a501 100644 --- a/Include/object.h +++ b/Include/object.h @@ -303,7 +303,11 @@ _Py_ThreadId(void) static inline Py_ALWAYS_INLINE int _Py_IsOwnedByCurrentThread(PyObject *ob) { +#ifdef _Py_THREAD_SANITIZER + return _Py_atomic_load_uintptr_relaxed(&ob->ob_tid) == _Py_ThreadId(); +#else return ob->ob_tid == _Py_ThreadId(); +#endif } #endif diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index e4ca32bebc5a22..4b1a2fdf6dd43a 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -14,7 +14,6 @@ race:set_allocator_unlocked race:_add_to_weak_set race:_in_weak_set race:_mi_heap_delayed_free_partial -race:_Py_IsOwnedByCurrentThread race:_PyEval_EvalFrameDefault race:_PyFunction_SetVersion race:_PyImport_AcquireLock From 0f998613324bcb6fa1cd9a3a2fc7e46f67358df7 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Fri, 26 Apr 2024 09:01:44 -0700 Subject: [PATCH 066/217] gh-117385: Remove unhooked events on sys.settrace (GH-117386) --- .../2024-03-30-00-37-53.gh-issue-117385.h0OJti.rst | 1 + Python/legacy_tracing.c | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-03-30-00-37-53.gh-issue-117385.h0OJti.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-03-30-00-37-53.gh-issue-117385.h0OJti.rst b/Misc/NEWS.d/next/Core and Builtins/2024-03-30-00-37-53.gh-issue-117385.h0OJti.rst new file mode 100644 index 00000000000000..2e385df3938347 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-03-30-00-37-53.gh-issue-117385.h0OJti.rst @@ -0,0 +1 @@ +Remove unhandled ``PY_MONITORING_EVENT_BRANCH`` and ``PY_MONITORING_EVENT_EXCEPTION_HANDLED`` events from :func:`sys.settrace`. diff --git a/Python/legacy_tracing.c b/Python/legacy_tracing.c index d7aae7d2343ac2..b5a17405931825 100644 --- a/Python/legacy_tracing.c +++ b/Python/legacy_tracing.c @@ -599,10 +599,9 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg) (1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) | (1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) | (1 << PY_MONITORING_EVENT_RAISE) | (1 << PY_MONITORING_EVENT_LINE) | - (1 << PY_MONITORING_EVENT_JUMP) | (1 << PY_MONITORING_EVENT_BRANCH) | + (1 << PY_MONITORING_EVENT_JUMP) | (1 << PY_MONITORING_EVENT_PY_UNWIND) | (1 << PY_MONITORING_EVENT_PY_THROW) | - (1 << PY_MONITORING_EVENT_STOP_ITERATION) | - (1 << PY_MONITORING_EVENT_EXCEPTION_HANDLED); + (1 << PY_MONITORING_EVENT_STOP_ITERATION); PyFrameObject* frame = PyEval_GetFrame(); if (frame->f_trace_opcodes) { From 63add11704078390909a485b57a7de6a0358fc2b Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Sat, 27 Apr 2024 01:20:30 +0900 Subject: [PATCH 067/217] gh-117680: Fix msvc warning in instruction_sequence.c (#118326) --- Python/instruction_sequence.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/instruction_sequence.c b/Python/instruction_sequence.c index 3a254b2c9def4e..a3f85f754d71bb 100644 --- a/Python/instruction_sequence.c +++ b/Python/instruction_sequence.c @@ -20,6 +20,8 @@ typedef _Py_SourceLocation location; #define INITIAL_INSTR_SEQUENCE_SIZE 100 #define INITIAL_INSTR_SEQUENCE_LABELS_MAP_SIZE 10 +#include "clinic/instruction_sequence.c.h" + #undef SUCCESS #undef ERROR #define SUCCESS 0 @@ -172,8 +174,6 @@ class InstructionSequenceType "_PyInstructionSequence *" "&_PyInstructionSequenc [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=589963e07480390f]*/ -#include "clinic/instruction_sequence.c.h" - static _PyInstructionSequence* inst_seq_create(void) { From 3e06c7f719b99cc7f5e8889319cff4980e41d3e8 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 26 Apr 2024 18:08:50 +0100 Subject: [PATCH 068/217] GH-118095: Add dynamic exit support and FOR_ITER_GEN support to tier 2 (GH-118279) --- Include/internal/pycore_opcode_metadata.h | 3 +- Include/internal/pycore_uop_ids.h | 207 +++++++++++----------- Include/internal/pycore_uop_metadata.h | 8 + Lib/test/test_capi/test_opt.py | 17 +- Python/bytecodes.c | 60 +++++-- Python/ceval.c | 4 + Python/ceval_macros.h | 1 + Python/executor_cases.c.h | 61 ++++++- Python/generated_cases.c.h | 57 ++++-- Python/optimizer.c | 19 +- Python/optimizer_cases.c.h | 13 +- Tools/jit/template.c | 4 + 12 files changed, 315 insertions(+), 139 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 400d7c334db8e7..4b1f43cf2af06e 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -625,7 +625,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { case FOR_ITER: return 2; case FOR_ITER_GEN: - return 2; + return 1; case FOR_ITER_LIST: return 2; case FOR_ITER_RANGE: @@ -1253,6 +1253,7 @@ _PyOpcode_macro_expansion[256] = { [FORMAT_SIMPLE] = { .nuops = 1, .uops = { { _FORMAT_SIMPLE, 0, 0 } } }, [FORMAT_WITH_SPEC] = { .nuops = 1, .uops = { { _FORMAT_WITH_SPEC, 0, 0 } } }, [FOR_ITER] = { .nuops = 1, .uops = { { _FOR_ITER, 9, 0 } } }, + [FOR_ITER_GEN] = { .nuops = 3, .uops = { { _CHECK_PEP_523, 0, 0 }, { _FOR_ITER_GEN_FRAME, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, [FOR_ITER_LIST] = { .nuops = 3, .uops = { { _ITER_CHECK_LIST, 0, 0 }, { _ITER_JUMP_LIST, 9, 1 }, { _ITER_NEXT_LIST, 0, 0 } } }, [FOR_ITER_RANGE] = { .nuops = 3, .uops = { { _ITER_CHECK_RANGE, 0, 0 }, { _ITER_JUMP_RANGE, 9, 1 }, { _ITER_NEXT_RANGE, 0, 0 } } }, [FOR_ITER_TUPLE] = { .nuops = 3, .uops = { { _ITER_CHECK_TUPLE, 0, 0 }, { _ITER_JUMP_TUPLE, 9, 1 }, { _ITER_NEXT_TUPLE, 0, 0 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index bb49d6e77d2562..beb182c436d52a 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -91,48 +91,49 @@ extern "C" { #define _DEOPT 342 #define _DICT_MERGE DICT_MERGE #define _DICT_UPDATE DICT_UPDATE +#define _DYNAMIC_EXIT 343 #define _END_SEND END_SEND -#define _ERROR_POP_N 343 +#define _ERROR_POP_N 344 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK -#define _FATAL_ERROR 344 +#define _FATAL_ERROR 345 #define _FORMAT_SIMPLE FORMAT_SIMPLE #define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC -#define _FOR_ITER 345 -#define _FOR_ITER_GEN FOR_ITER_GEN -#define _FOR_ITER_TIER_TWO 346 +#define _FOR_ITER 346 +#define _FOR_ITER_GEN_FRAME 347 +#define _FOR_ITER_TIER_TWO 348 #define _GET_AITER GET_AITER #define _GET_ANEXT GET_ANEXT #define _GET_AWAITABLE GET_AWAITABLE #define _GET_ITER GET_ITER #define _GET_LEN GET_LEN #define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER -#define _GUARD_BOTH_FLOAT 347 -#define _GUARD_BOTH_INT 348 -#define _GUARD_BOTH_UNICODE 349 -#define _GUARD_BUILTINS_VERSION 350 -#define _GUARD_DORV_NO_DICT 351 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 352 -#define _GUARD_GLOBALS_VERSION 353 -#define _GUARD_IS_FALSE_POP 354 -#define _GUARD_IS_NONE_POP 355 -#define _GUARD_IS_NOT_NONE_POP 356 -#define _GUARD_IS_TRUE_POP 357 -#define _GUARD_KEYS_VERSION 358 -#define _GUARD_NOS_FLOAT 359 -#define _GUARD_NOS_INT 360 -#define _GUARD_NOT_EXHAUSTED_LIST 361 -#define _GUARD_NOT_EXHAUSTED_RANGE 362 -#define _GUARD_NOT_EXHAUSTED_TUPLE 363 -#define _GUARD_TOS_FLOAT 364 -#define _GUARD_TOS_INT 365 -#define _GUARD_TYPE_VERSION 366 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 367 -#define _INIT_CALL_PY_EXACT_ARGS 368 -#define _INIT_CALL_PY_EXACT_ARGS_0 369 -#define _INIT_CALL_PY_EXACT_ARGS_1 370 -#define _INIT_CALL_PY_EXACT_ARGS_2 371 -#define _INIT_CALL_PY_EXACT_ARGS_3 372 -#define _INIT_CALL_PY_EXACT_ARGS_4 373 +#define _GUARD_BOTH_FLOAT 349 +#define _GUARD_BOTH_INT 350 +#define _GUARD_BOTH_UNICODE 351 +#define _GUARD_BUILTINS_VERSION 352 +#define _GUARD_DORV_NO_DICT 353 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 354 +#define _GUARD_GLOBALS_VERSION 355 +#define _GUARD_IS_FALSE_POP 356 +#define _GUARD_IS_NONE_POP 357 +#define _GUARD_IS_NOT_NONE_POP 358 +#define _GUARD_IS_TRUE_POP 359 +#define _GUARD_KEYS_VERSION 360 +#define _GUARD_NOS_FLOAT 361 +#define _GUARD_NOS_INT 362 +#define _GUARD_NOT_EXHAUSTED_LIST 363 +#define _GUARD_NOT_EXHAUSTED_RANGE 364 +#define _GUARD_NOT_EXHAUSTED_TUPLE 365 +#define _GUARD_TOS_FLOAT 366 +#define _GUARD_TOS_INT 367 +#define _GUARD_TYPE_VERSION 368 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 369 +#define _INIT_CALL_PY_EXACT_ARGS 370 +#define _INIT_CALL_PY_EXACT_ARGS_0 371 +#define _INIT_CALL_PY_EXACT_ARGS_1 372 +#define _INIT_CALL_PY_EXACT_ARGS_2 373 +#define _INIT_CALL_PY_EXACT_ARGS_3 374 +#define _INIT_CALL_PY_EXACT_ARGS_4 375 #define _INSTRUMENTED_CALL INSTRUMENTED_CALL #define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX #define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW @@ -149,65 +150,65 @@ extern "C" { #define _INSTRUMENTED_RETURN_CONST INSTRUMENTED_RETURN_CONST #define _INSTRUMENTED_RETURN_VALUE INSTRUMENTED_RETURN_VALUE #define _INSTRUMENTED_YIELD_VALUE INSTRUMENTED_YIELD_VALUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 374 -#define _IS_NONE 375 +#define _INTERNAL_INCREMENT_OPT_COUNTER 376 +#define _IS_NONE 377 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 376 -#define _ITER_CHECK_RANGE 377 -#define _ITER_CHECK_TUPLE 378 -#define _ITER_JUMP_LIST 379 -#define _ITER_JUMP_RANGE 380 -#define _ITER_JUMP_TUPLE 381 -#define _ITER_NEXT_LIST 382 -#define _ITER_NEXT_RANGE 383 -#define _ITER_NEXT_TUPLE 384 -#define _JUMP_TO_TOP 385 +#define _ITER_CHECK_LIST 378 +#define _ITER_CHECK_RANGE 379 +#define _ITER_CHECK_TUPLE 380 +#define _ITER_JUMP_LIST 381 +#define _ITER_JUMP_RANGE 382 +#define _ITER_JUMP_TUPLE 383 +#define _ITER_NEXT_LIST 384 +#define _ITER_NEXT_RANGE 385 +#define _ITER_NEXT_TUPLE 386 +#define _JUMP_TO_TOP 387 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND #define _LOAD_ASSERTION_ERROR LOAD_ASSERTION_ERROR -#define _LOAD_ATTR 386 -#define _LOAD_ATTR_CLASS 387 -#define _LOAD_ATTR_CLASS_0 388 -#define _LOAD_ATTR_CLASS_1 389 +#define _LOAD_ATTR 388 +#define _LOAD_ATTR_CLASS 389 +#define _LOAD_ATTR_CLASS_0 390 +#define _LOAD_ATTR_CLASS_1 391 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 390 -#define _LOAD_ATTR_INSTANCE_VALUE_0 391 -#define _LOAD_ATTR_INSTANCE_VALUE_1 392 -#define _LOAD_ATTR_METHOD_LAZY_DICT 393 -#define _LOAD_ATTR_METHOD_NO_DICT 394 -#define _LOAD_ATTR_METHOD_WITH_VALUES 395 -#define _LOAD_ATTR_MODULE 396 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 397 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 398 +#define _LOAD_ATTR_INSTANCE_VALUE 392 +#define _LOAD_ATTR_INSTANCE_VALUE_0 393 +#define _LOAD_ATTR_INSTANCE_VALUE_1 394 +#define _LOAD_ATTR_METHOD_LAZY_DICT 395 +#define _LOAD_ATTR_METHOD_NO_DICT 396 +#define _LOAD_ATTR_METHOD_WITH_VALUES 397 +#define _LOAD_ATTR_MODULE 398 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 399 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 400 #define _LOAD_ATTR_PROPERTY LOAD_ATTR_PROPERTY -#define _LOAD_ATTR_SLOT 399 -#define _LOAD_ATTR_SLOT_0 400 -#define _LOAD_ATTR_SLOT_1 401 -#define _LOAD_ATTR_WITH_HINT 402 +#define _LOAD_ATTR_SLOT 401 +#define _LOAD_ATTR_SLOT_0 402 +#define _LOAD_ATTR_SLOT_1 403 +#define _LOAD_ATTR_WITH_HINT 404 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS #define _LOAD_CONST LOAD_CONST -#define _LOAD_CONST_INLINE 403 -#define _LOAD_CONST_INLINE_BORROW 404 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 405 -#define _LOAD_CONST_INLINE_WITH_NULL 406 +#define _LOAD_CONST_INLINE 405 +#define _LOAD_CONST_INLINE_BORROW 406 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 407 +#define _LOAD_CONST_INLINE_WITH_NULL 408 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 407 -#define _LOAD_FAST_0 408 -#define _LOAD_FAST_1 409 -#define _LOAD_FAST_2 410 -#define _LOAD_FAST_3 411 -#define _LOAD_FAST_4 412 -#define _LOAD_FAST_5 413 -#define _LOAD_FAST_6 414 -#define _LOAD_FAST_7 415 +#define _LOAD_FAST 409 +#define _LOAD_FAST_0 410 +#define _LOAD_FAST_1 411 +#define _LOAD_FAST_2 412 +#define _LOAD_FAST_3 413 +#define _LOAD_FAST_4 414 +#define _LOAD_FAST_5 415 +#define _LOAD_FAST_6 416 +#define _LOAD_FAST_7 417 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 416 -#define _LOAD_GLOBAL_BUILTINS 417 -#define _LOAD_GLOBAL_MODULE 418 +#define _LOAD_GLOBAL 418 +#define _LOAD_GLOBAL_BUILTINS 419 +#define _LOAD_GLOBAL_MODULE 420 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR @@ -221,50 +222,50 @@ extern "C" { #define _MATCH_SEQUENCE MATCH_SEQUENCE #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_FRAME 419 -#define _POP_JUMP_IF_FALSE 420 -#define _POP_JUMP_IF_TRUE 421 +#define _POP_FRAME 421 +#define _POP_JUMP_IF_FALSE 422 +#define _POP_JUMP_IF_TRUE 423 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 422 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 424 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 423 +#define _PUSH_FRAME 425 #define _PUSH_NULL PUSH_NULL -#define _REPLACE_WITH_TRUE 424 +#define _REPLACE_WITH_TRUE 426 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR -#define _SAVE_RETURN_OFFSET 425 -#define _SEND 426 +#define _SAVE_RETURN_OFFSET 427 +#define _SEND 428 #define _SEND_GEN SEND_GEN #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _SIDE_EXIT 427 -#define _START_EXECUTOR 428 -#define _STORE_ATTR 429 -#define _STORE_ATTR_INSTANCE_VALUE 430 -#define _STORE_ATTR_SLOT 431 +#define _SIDE_EXIT 429 +#define _START_EXECUTOR 430 +#define _STORE_ATTR 431 +#define _STORE_ATTR_INSTANCE_VALUE 432 +#define _STORE_ATTR_SLOT 433 #define _STORE_ATTR_WITH_HINT STORE_ATTR_WITH_HINT #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 432 -#define _STORE_FAST_0 433 -#define _STORE_FAST_1 434 -#define _STORE_FAST_2 435 -#define _STORE_FAST_3 436 -#define _STORE_FAST_4 437 -#define _STORE_FAST_5 438 -#define _STORE_FAST_6 439 -#define _STORE_FAST_7 440 +#define _STORE_FAST 434 +#define _STORE_FAST_0 435 +#define _STORE_FAST_1 436 +#define _STORE_FAST_2 437 +#define _STORE_FAST_3 438 +#define _STORE_FAST_4 439 +#define _STORE_FAST_5 440 +#define _STORE_FAST_6 441 +#define _STORE_FAST_7 442 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME #define _STORE_SLICE STORE_SLICE -#define _STORE_SUBSCR 441 +#define _STORE_SUBSCR 443 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TO_BOOL 442 +#define _TO_BOOL 444 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -274,12 +275,12 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 443 +#define _UNPACK_SEQUENCE 445 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START -#define MAX_UOP_ID 443 +#define MAX_UOP_ID 445 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index b8cdfae8391460..776728d04bce00 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -180,6 +180,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_ITER_CHECK_RANGE] = HAS_EXIT_FLAG, [_GUARD_NOT_EXHAUSTED_RANGE] = HAS_EXIT_FLAG, [_ITER_NEXT_RANGE] = HAS_ERROR_FLAG, + [_FOR_ITER_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_WITH_EXCEPT_START] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_PUSH_EXC_INFO] = 0, [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = HAS_DEOPT_FLAG, @@ -245,6 +246,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CHECK_FUNCTION] = HAS_DEOPT_FLAG, [_INTERNAL_INCREMENT_OPT_COUNTER] = 0, [_COLD_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, + [_DYNAMIC_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_START_EXECUTOR] = HAS_DEOPT_FLAG, [_FATAL_ERROR] = HAS_ESCAPES_FLAG, [_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG, @@ -331,6 +333,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_DEOPT] = "_DEOPT", [_DICT_MERGE] = "_DICT_MERGE", [_DICT_UPDATE] = "_DICT_UPDATE", + [_DYNAMIC_EXIT] = "_DYNAMIC_EXIT", [_END_SEND] = "_END_SEND", [_ERROR_POP_N] = "_ERROR_POP_N", [_EXIT_INIT_CHECK] = "_EXIT_INIT_CHECK", @@ -338,6 +341,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_FATAL_ERROR] = "_FATAL_ERROR", [_FORMAT_SIMPLE] = "_FORMAT_SIMPLE", [_FORMAT_WITH_SPEC] = "_FORMAT_WITH_SPEC", + [_FOR_ITER_GEN_FRAME] = "_FOR_ITER_GEN_FRAME", [_FOR_ITER_TIER_TWO] = "_FOR_ITER_TIER_TWO", [_GET_AITER] = "_GET_AITER", [_GET_ANEXT] = "_GET_ANEXT", @@ -818,6 +822,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _ITER_NEXT_RANGE: return 1; + case _FOR_ITER_GEN_FRAME: + return 1; case _WITH_EXCEPT_START: return 4; case _PUSH_EXC_INFO: @@ -948,6 +954,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _COLD_EXIT: return 0; + case _DYNAMIC_EXIT: + return 0; case _START_EXECUTOR: return 0; case _FATAL_ERROR: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index e2e772a52d764e..c798b343626677 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -132,7 +132,7 @@ def iter_opnames(ex): def get_opnames(ex): - return set(iter_opnames(ex)) + return list(iter_opnames(ex)) @requires_specialization @@ -1298,5 +1298,20 @@ def testfunc(n): self.assertIsNotNone(ex) self.assertIn("_RETURN_GENERATOR", get_opnames(ex)) + def test_for_iter_gen(self): + def gen(n): + for i in range(n): + yield i + def testfunc(n): + g = gen(n) + s = 0 + for i in g: + s += i + return s + res, ex = self._run_with_optimizer(testfunc, 20) + self.assertEqual(res, 190) + self.assertIsNotNone(ex) + self.assertIn("_FOR_ITER_GEN_FRAME", get_opnames(ex)) + if __name__ == "__main__": unittest.main() diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 485504914912f9..fe3d61362e6b02 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1109,6 +1109,10 @@ dummy_func( _PyFrame_StackPush(frame, retval); /* We don't know which of these is relevant here, so keep them equal */ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + assert(_PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); goto resume_frame; } @@ -2759,24 +2763,26 @@ dummy_func( _ITER_JUMP_RANGE + _ITER_NEXT_RANGE; - inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) { - DEOPT_IF(tstate->interp->eval_frame); + op(_FOR_ITER_GEN_FRAME, (iter -- iter, gen_frame: _PyInterpreterFrame*)) { PyGenObject *gen = (PyGenObject *)iter; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING); STAT_INC(FOR_ITER, hit); - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; _PyFrame_StackPush(gen_frame, Py_None); gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; - assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - assert(next_instr - this_instr + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); - DISPATCH_INLINED(gen_frame); + // oparg is the return offset from the next instruction. + frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg); } + macro(FOR_ITER_GEN) = + unused/1 + + _CHECK_PEP_523 + + _FOR_ITER_GEN_FRAME + + _PUSH_FRAME; + inst(BEFORE_ASYNC_WITH, (mgr -- exit, res)) { PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); if (enter == NULL) { @@ -3166,10 +3172,7 @@ dummy_func( } } - // The 'unused' output effect represents the return value - // (which will be pushed when the frame returns). - // It is needed so CALL_PY_EXACT_ARGS matches its family. - op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused if (0))) { + op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- )) { // Write it out explicitly because it's subtly different. // Eventually this should be the only occurrence of this code. assert(tstate->interp->eval_frame == NULL); @@ -4189,6 +4192,38 @@ dummy_func( GOTO_TIER_TWO(executor); } + tier2 op(_DYNAMIC_EXIT, (--)) { + tstate->previous_executor = (PyObject *)current_executor; + _PyExitData *exit = (_PyExitData *)¤t_executor->exits[oparg]; + _Py_CODEUNIT *target = frame->instr_ptr; + _PyExecutorObject *executor; + if (target->op.code == ENTER_EXECUTOR) { + PyCodeObject *code = (PyCodeObject *)frame->f_executable; + executor = code->co_executors->executors[target->op.arg]; + Py_INCREF(executor); + } + else { + if (!backoff_counter_triggers(exit->temperature)) { + exit->temperature = advance_backoff_counter(exit->temperature); + GOTO_TIER_ONE(target); + } + int optimized = _PyOptimizer_Optimize(frame, target, stack_pointer, &executor); + if (optimized <= 0) { + exit->temperature = restart_backoff_counter(exit->temperature); + if (optimized < 0) { + Py_DECREF(current_executor); + tstate->previous_executor = Py_None; + GOTO_UNWIND(); + } + GOTO_TIER_ONE(target); + } + else { + exit->temperature = initial_temperature_backoff_counter(); + } + } + GOTO_TIER_TWO(executor); + } + tier2 op(_START_EXECUTOR, (executor/4 --)) { Py_DECREF(tstate->previous_executor); tstate->previous_executor = NULL; @@ -4222,6 +4257,7 @@ dummy_func( GOTO_UNWIND(); } + // END BYTECODES // } diff --git a/Python/ceval.c b/Python/ceval.c index 2f217c5f33c6ce..d130c734a67144 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1072,9 +1072,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int next_uop = current_executor->trace + target; goto tier2_dispatch; +exit_to_tier1_dynamic: + next_instr = frame->instr_ptr; + goto goto_to_tier1; exit_to_tier1: assert(next_uop[-1].format == UOP_FORMAT_TARGET); next_instr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame)); +goto_to_tier1: #ifdef Py_DEBUG if (lltrace >= 2) { printf("DEOPT: [UOp "); diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 871d1747e2bb8d..1a8554ab72269f 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -442,3 +442,4 @@ do { \ #define GOTO_UNWIND() goto error_tier_two #define EXIT_TO_TRACE() goto exit_to_trace #define EXIT_TO_TIER1() goto exit_to_tier1 +#define EXIT_TO_TIER1_DYNAMIC() goto exit_to_tier1_dynamic; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 1eb3da9b70002c..280cca1592ae18 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2769,7 +2769,32 @@ break; } - /* _FOR_ITER_GEN is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + case _FOR_ITER_GEN_FRAME: { + PyObject *iter; + _PyInterpreterFrame *gen_frame; + oparg = CURRENT_OPARG(); + iter = stack_pointer[-1]; + PyGenObject *gen = (PyGenObject *)iter; + if (Py_TYPE(gen) != &PyGen_Type) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + if (gen->gi_frame_state >= FRAME_EXECUTING) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(FOR_ITER, hit); + gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + _PyFrame_StackPush(gen_frame, Py_None); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + // oparg is the return offset from the next instruction. + frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg); + stack_pointer[0] = (PyObject *)gen_frame; + stack_pointer += 1; + break; + } /* _BEFORE_ASYNC_WITH is not a viable micro-op for tier 2 because it has both popping and not-popping errors */ @@ -4187,6 +4212,40 @@ break; } + case _DYNAMIC_EXIT: { + oparg = CURRENT_OPARG(); + tstate->previous_executor = (PyObject *)current_executor; + _PyExitData *exit = (_PyExitData *)¤t_executor->exits[oparg]; + _Py_CODEUNIT *target = frame->instr_ptr; + _PyExecutorObject *executor; + if (target->op.code == ENTER_EXECUTOR) { + PyCodeObject *code = (PyCodeObject *)frame->f_executable; + executor = code->co_executors->executors[target->op.arg]; + Py_INCREF(executor); + } + else { + if (!backoff_counter_triggers(exit->temperature)) { + exit->temperature = advance_backoff_counter(exit->temperature); + GOTO_TIER_ONE(target); + } + int optimized = _PyOptimizer_Optimize(frame, target, stack_pointer, &executor); + if (optimized <= 0) { + exit->temperature = restart_backoff_counter(exit->temperature); + if (optimized < 0) { + Py_DECREF(current_executor); + tstate->previous_executor = Py_None; + GOTO_UNWIND(); + } + GOTO_TIER_ONE(target); + } + else { + exit->temperature = initial_temperature_backoff_counter(); + } + } + GOTO_TIER_TWO(executor); + break; + } + case _START_EXECUTOR: { PyObject *executor = (PyObject *)CURRENT_OPERAND(); Py_DECREF(tstate->previous_executor); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 0c58f3f87d4041..c27505fde3d9fa 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2631,28 +2631,49 @@ } TARGET(FOR_ITER_GEN) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_GEN); static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); PyObject *iter; + _PyInterpreterFrame *gen_frame; + _PyInterpreterFrame *new_frame; /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + DEOPT_IF(tstate->interp->eval_frame, FOR_ITER); + } + // _FOR_ITER_GEN_FRAME iter = stack_pointer[-1]; - DEOPT_IF(tstate->interp->eval_frame, FOR_ITER); - PyGenObject *gen = (PyGenObject *)iter; - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); - STAT_INC(FOR_ITER, hit); - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - _PyFrame_StackPush(gen_frame, Py_None); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - assert(next_instr - this_instr + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); - DISPATCH_INLINED(gen_frame); + { + PyGenObject *gen = (PyGenObject *)iter; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); + STAT_INC(FOR_ITER, hit); + gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + _PyFrame_StackPush(gen_frame, Py_None); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + // oparg is the return offset from the next instruction. + frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg); + } + // _PUSH_FRAME + new_frame = gen_frame; + { + // Write it out explicitly because it's subtly different. + // Eventually this should be the only occurrence of this code. + assert(tstate->interp->eval_frame == NULL); + _PyFrame_SetStackPointer(frame, stack_pointer); + new_frame->previous = frame; + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = new_frame; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); } TARGET(FOR_ITER_LIST) { @@ -6011,6 +6032,10 @@ _PyFrame_StackPush(frame, retval); /* We don't know which of these is relevant here, so keep them equal */ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + assert(_PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); goto resume_frame; } diff --git a/Python/optimizer.c b/Python/optimizer.c index e5c70f72f9c324..02c9b395027791 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -567,8 +567,6 @@ translate_bytecode_to_trace( top: // Jump here after _PUSH_FRAME or likely branches for (;;) { target = INSTR_IP(instr, code); - RESERVE_RAW(2, "_CHECK_VALIDITY_AND_SET_IP"); - ADD_TO_TRACE(_CHECK_VALIDITY_AND_SET_IP, 0, (uintptr_t)instr, target); // Need space for _DEOPT max_length--; @@ -597,6 +595,8 @@ translate_bytecode_to_trace( } } assert(opcode != ENTER_EXECUTOR && opcode != EXTENDED_ARG); + RESERVE_RAW(2, "_CHECK_VALIDITY_AND_SET_IP"); + ADD_TO_TRACE(_CHECK_VALIDITY_AND_SET_IP, 0, (uintptr_t)instr, target); /* Special case the first instruction, * so that we can guarantee forward progress */ @@ -814,6 +814,12 @@ translate_bytecode_to_trace( ADD_TO_TRACE(_EXIT_TRACE, 0, 0, 0); goto done; } + if (opcode == FOR_ITER_GEN) { + DPRINTF(2, "Bailing due to dynamic target\n"); + ADD_TO_TRACE(uop, oparg, 0, target); + ADD_TO_TRACE(_DYNAMIC_EXIT, 0, 0, 0); + goto done; + } // Increment IP to the return address instr += _PyOpcode_Caches[_PyOpcode_Deopt[opcode]] + 1; TRACE_STACK_PUSH(); @@ -847,7 +853,7 @@ translate_bytecode_to_trace( } DPRINTF(2, "Bail, new_code == NULL\n"); ADD_TO_TRACE(uop, oparg, 0, target); - ADD_TO_TRACE(_EXIT_TRACE, 0, 0, 0); + ADD_TO_TRACE(_DYNAMIC_EXIT, 0, 0, 0); goto done; } @@ -917,7 +923,7 @@ count_exits(_PyUOpInstruction *buffer, int length) int exit_count = 0; for (int i = 0; i < length; i++) { int opcode = buffer[i].opcode; - if (opcode == _SIDE_EXIT) { + if (opcode == _SIDE_EXIT || opcode == _DYNAMIC_EXIT) { exit_count++; } } @@ -1114,6 +1120,11 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil dest->format = UOP_FORMAT_EXIT; next_exit--; } + if (opcode == _DYNAMIC_EXIT) { + executor->exits[next_exit].target = 0; + dest->oparg = next_exit; + next_exit--; + } } assert(next_exit == -1); assert(dest == executor->trace); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 4f0941a3cc3e09..b1965687701050 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1439,7 +1439,14 @@ break; } - /* _FOR_ITER_GEN is not a viable micro-op for tier 2 */ + case _FOR_ITER_GEN_FRAME: { + _PyInterpreterFrame *gen_frame; + gen_frame = sym_new_not_null(ctx); + if (gen_frame == NULL) goto out_of_space; + stack_pointer[0] = (_Py_UopsSymbol *)gen_frame; + stack_pointer += 1; + break; + } /* _BEFORE_ASYNC_WITH is not a viable micro-op for tier 2 */ @@ -2109,6 +2116,10 @@ break; } + case _DYNAMIC_EXIT: { + break; + } + case _START_EXECUTOR: { break; } diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 228dc83254d678..3e81fd15bb8093 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -87,6 +87,7 @@ _JIT_ENTRY(_PyInterpreterFrame *frame, PyObject **stack_pointer, PyThreadState * PATCH_VALUE(_PyExecutorObject *, current_executor, _JIT_EXECUTOR) int oparg; int uopcode = _JIT_OPCODE; + _Py_CODEUNIT *next_instr; // Other stuff we need handy: PATCH_VALUE(uint16_t, _oparg, _JIT_OPARG) #if SIZEOF_VOID_P == 8 @@ -122,6 +123,9 @@ _JIT_ENTRY(_PyInterpreterFrame *frame, PyObject **stack_pointer, PyThreadState * exit_to_tier1: tstate->previous_executor = (PyObject *)current_executor; GOTO_TIER_ONE(_PyCode_CODE(_PyFrame_GetCode(frame)) + _target); +exit_to_tier1_dynamic: + tstate->previous_executor = (PyObject *)current_executor; + GOTO_TIER_ONE(frame->instr_ptr); exit_to_trace: { _PyExitData *exit = ¤t_executor->exits[_exit_index]; From 5a90de0d4cbc151a6deea36a27eb81b192410e56 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 26 Apr 2024 14:22:29 -0400 Subject: [PATCH 069/217] gh-116749: Disable GIL by default in free-threaded build (#118295) Switch GIL to disabled by default in free-threaded build so that the free-threaded CIs catch thread-safety issues. --- Python/ceval_gil.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index c3c2c54b199c59..fdbb4882c3d711 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -451,9 +451,7 @@ init_own_gil(PyInterpreterState *interp, struct _gil_runtime_state *gil) { assert(!gil_created(gil)); #ifdef Py_GIL_DISABLED - // gh-116329: Once it is safe to do so, change this condition to - // (enable_gil == _PyConfig_GIL_ENABLE), so the GIL is disabled by default. - gil->enabled = _PyInterpreterState_GetConfig(interp)->enable_gil != _PyConfig_GIL_DISABLE; + gil->enabled = _PyInterpreterState_GetConfig(interp)->enable_gil == _PyConfig_GIL_ENABLE; #endif create_gil(gil); assert(gil_created(gil)); From b43c7e1070e515b3e94043ff777ab83074234051 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Fri, 26 Apr 2024 21:23:30 +0100 Subject: [PATCH 070/217] gh-112730: Respect tests that require environment variables with no-colorize fixes (#118288) --- Lib/test/test_traceback.py | 3 ++- Lib/test/test_tracemalloc.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 19611937fc278b..8927fccc289320 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -384,6 +384,7 @@ def f(): ]) @requires_subprocess() + @force_not_colorized def test_encoded_file(self): # Test that tracebacks are correctly printed for encoded source files: # - correct line number (Issue2384) @@ -410,7 +411,7 @@ def do_test(firstlines, message, charset, lineno): """.format(firstlines, message)) process = subprocess.Popen([sys.executable, TESTFN], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env={}) + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = process.communicate() stdout = stdout.decode(output_encoding).splitlines() finally: diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index f685430a7d36ad..5755f7697de91a 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -8,6 +8,7 @@ interpreter_requires_environment) from test import support from test.support import os_helper +from test.support import force_not_colorized try: import _testcapi @@ -938,11 +939,12 @@ def test_env_limit(self): stdout = stdout.rstrip() self.assertEqual(stdout, b'10') + @force_not_colorized def check_env_var_invalid(self, nframe): with support.SuppressCrashReport(): ok, stdout, stderr = assert_python_failure( '-c', 'pass', - PYTHONTRACEMALLOC=str(nframe), __cleanenv=True) + PYTHONTRACEMALLOC=str(nframe)) if b'ValueError: the number of frames must be in range' in stderr: return From 194fd17bc6cb73138e2fe8eb5ca34b19a6c3b25a Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Fri, 26 Apr 2024 14:27:58 -0700 Subject: [PATCH 071/217] bpo-32839: Add the after_info() method for Tkinter widgets (GH-5664) --- Doc/whatsnew/3.13.rst | 3 ++ Lib/test/test_tkinter/test_misc.py | 40 +++++++++++++++++++ Lib/tkinter/__init__.py | 15 +++++++ .../2018-02-13-10-02-54.bpo-32839.McbVz3.rst | 1 + 4 files changed, 59 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-02-13-10-02-54.bpo-32839.McbVz3.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index ad107aad5db3bd..083a70ce2405e3 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -791,6 +791,9 @@ tkinter :class:`tkinter.ttk.Style`. (Contributed by Serhiy Storchaka in :gh:`68166`.) +* Add the :meth:`!after_info` method for Tkinter widgets. + (Contributed by Cheryl Sabella in :gh:`77020`.) + traceback --------- diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index 81a20b698a72eb..6dca2a3920e06a 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -232,6 +232,46 @@ def callback(): with self.assertRaises(tkinter.TclError): root.tk.call('after', 'info', idle1) + def test_after_info(self): + root = self.root + + # No events. + self.assertEqual(root.after_info(), ()) + + # Add timer. + timer = root.after(1, lambda: 'break') + + # With no parameter, it returns a tuple of the event handler ids. + self.assertEqual(root.after_info(), (timer, )) + root.after_cancel(timer) + + timer1 = root.after(5000, lambda: 'break') + timer2 = root.after(5000, lambda: 'break') + idle1 = root.after_idle(lambda: 'break') + # Only contains new events and not 'timer'. + self.assertEqual(root.after_info(), (idle1, timer2, timer1)) + + # With a parameter returns a tuple of (script, type). + timer1_info = root.after_info(timer1) + self.assertEqual(len(timer1_info), 2) + self.assertEqual(timer1_info[1], 'timer') + idle1_info = root.after_info(idle1) + self.assertEqual(len(idle1_info), 2) + self.assertEqual(idle1_info[1], 'idle') + + root.after_cancel(timer1) + with self.assertRaises(tkinter.TclError): + root.after_info(timer1) + root.after_cancel(timer2) + with self.assertRaises(tkinter.TclError): + root.after_info(timer2) + root.after_cancel(idle1) + with self.assertRaises(tkinter.TclError): + root.after_info(idle1) + + # No events. + self.assertEqual(root.after_info(), ()) + def test_clipboard(self): root = self.root root.clipboard_clear() diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index fd7b48e3519990..70a1ed46fd0774 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -897,6 +897,21 @@ def after_cancel(self, id): pass self.tk.call('after', 'cancel', id) + def after_info(self, id=None): + """Return information about existing event handlers. + + With no argument, return a tuple of the identifiers for all existing + event handlers created by the after and after_idle commands for this + interpreter. If id is supplied, it specifies an existing handler; id + must have been the return value from some previous call to after or + after_idle and it must not have triggered yet or been canceled. If the + id doesn't exist, a TclError is raised. Otherwise, the return value is + a tuple containing (script, type) where script is a reference to the + function to be called by the event handler and type is either 'idle' + or 'timer' to indicate what kind of event handler it is. + """ + return self.tk.splitlist(self.tk.call('after', 'info', id)) + def bell(self, displayof=0): """Ring a display's bell.""" self.tk.call(('bell',) + self._displayof(displayof)) diff --git a/Misc/NEWS.d/next/Library/2018-02-13-10-02-54.bpo-32839.McbVz3.rst b/Misc/NEWS.d/next/Library/2018-02-13-10-02-54.bpo-32839.McbVz3.rst new file mode 100644 index 00000000000000..0a2e3e3c540c48 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-02-13-10-02-54.bpo-32839.McbVz3.rst @@ -0,0 +1 @@ +Add the :meth:`after_info` method for Tkinter widgets. From 1d3392517698170e270eb7d847b6a8c28bfaca0f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 26 Apr 2024 19:13:44 -0600 Subject: [PATCH 072/217] gh-110693: Use a Larger Queue for Per-Interpreter Pending Calls (gh-118302) This is an improvement over the status quo, reducing the likelihood of completely filling the pending calls queue. However, the problem won't go away completely unless we move to an unbounded linked list or add a mechanism for waiting until the queue isn't full. --- Include/internal/pycore_ceval_state.h | 6 ++++-- Lib/test/test_capi/test_misc.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_ceval_state.h b/Include/internal/pycore_ceval_state.h index 1831f58899b745..11f2a100bf531e 100644 --- a/Include/internal/pycore_ceval_state.h +++ b/Include/internal/pycore_ceval_state.h @@ -20,7 +20,7 @@ struct _pending_call { int flags; }; -#define PENDINGCALLSARRAYSIZE 32 +#define PENDINGCALLSARRAYSIZE 300 #define MAXPENDINGCALLS PENDINGCALLSARRAYSIZE /* For interpreter-level pending calls, we want to avoid spending too @@ -31,7 +31,9 @@ struct _pending_call { # define MAXPENDINGCALLSLOOP MAXPENDINGCALLS #endif -#define MAXPENDINGCALLS_MAIN PENDINGCALLSARRAYSIZE +/* We keep the number small to preserve as much compatibility + as possible with earlier versions. */ +#define MAXPENDINGCALLS_MAIN 32 /* For the main thread, we want to make sure all pending calls are run at once, for the sake of prompt signal handling. This is unlikely to cause any problems since there should be very few diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 49d1056f050467..020e8493e57c0c 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1570,9 +1570,9 @@ def test_max_pending(self): self.assertEqual(added, maxpending) with self.subTest('not main-only'): - # Per-interpreter pending calls has the same low limit + # Per-interpreter pending calls has a much higher limit # on how many may be pending at a time. - maxpending = 32 + maxpending = 300 l = [] added = self.pendingcalls_submit(l, 1, main=False) From 8397d8d3002d3d817d4fbb8f852690be8ed0d6c7 Mon Sep 17 00:00:00 2001 From: Xie Yanbo Date: Sat, 27 Apr 2024 10:06:08 +0800 Subject: [PATCH 073/217] Correct spelling error in recent NEWS entry (#118308) --- .../next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst b/Misc/NEWS.d/next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst index 8eb68ebe99ba15..daa5fe7c0f2917 100644 --- a/Misc/NEWS.d/next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst +++ b/Misc/NEWS.d/next/Library/2024-04-24-12-20-48.gh-issue-118013.TKn_kZ.rst @@ -6,4 +6,4 @@ class was dynamically created, the class held strong references to other objects which took up a significant amount of memory, and the cache contained the sole strong reference to the class. The fix for the regression leads to a slowdown in :func:`getattr_static`, but the function should still -be signficantly faster than it was in Python 3.11. Patch by Alex Waygood. +be significantly faster than it was in Python 3.11. Patch by Alex Waygood. From c57326f48729f5cd7ddf7e2b38c4fd06d0962a41 Mon Sep 17 00:00:00 2001 From: Wulian233 <71213467+Wulian233@users.noreply.github.com> Date: Sat, 27 Apr 2024 17:25:32 +0800 Subject: [PATCH 074/217] Correct typo in iOS README (#118341) --- iOS/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/README.rst b/iOS/README.rst index df429b64cec77f..a0ffa5737aae43 100644 --- a/iOS/README.rst +++ b/iOS/README.rst @@ -50,7 +50,7 @@ iOS specific arguments to configure Unless you know what you're doing, changing the name of the Python framework on iOS is not advised. If you use this option, you won't be able - to run the ``make testios`` target without making signficant manual + to run the ``make testios`` target without making significant manual alterations, and you won't be able to use any binary packages unless you compile them yourself using your own framework name. From 51aefc5bf907ddffaaf083ded0de773adcdf08c8 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 27 Apr 2024 13:36:06 +0300 Subject: [PATCH 075/217] gh-118323: Document `&&` grammar syntax (#118324) --- Grammar/python.gram | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Grammar/python.gram b/Grammar/python.gram index 3943d7fec5db03..05d7837e3aa6db 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -78,6 +78,9 @@ _PyPegen_parse(Parser *p) # Fail if e can be parsed, without consuming any input. # ~ # Commit to the current alternative, even if it fails to parse. +# &&e +# Eager parse e. The parser will not backtrack and will immediately +# fail with SyntaxError if e cannot be parsed. # # STARTING RULES From 2326d6c868e300a814179d77fc308fec8365cb8c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 28 Apr 2024 06:21:28 -0700 Subject: [PATCH 076/217] gh-109118: Make comprehensions work within annotation scopes, but without inlining (#118160) Co-authored-by: Carl Meyer --- Doc/whatsnew/3.13.rst | 4 +- Lib/test/test_type_params.py | 48 +++++++++++-------- ...-04-25-21-18-19.gh-issue-118160.GH5SMc.rst | 3 ++ Python/symtable.c | 18 ++----- 4 files changed, 39 insertions(+), 34 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-25-21-18-19.gh-issue-118160.GH5SMc.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 083a70ce2405e3..98349a5984bb7e 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -276,7 +276,9 @@ Other Language Changes (Contributed by Pedro Sousa Lacerda in :gh:`66449`.) * :ref:`annotation scope ` within class scopes can now - contain lambdas. (Contributed by Jelle Zijlstra in :gh:`109118`.) + contain lambdas and comprehensions. Comprehensions that are located within + class scopes are not inlined into their parent scope. (Contributed by + Jelle Zijlstra in :gh:`109118` and :gh:`118160`.) New Modules diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index fbb80d9aac9942..4b86395ee74f75 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -436,9 +436,11 @@ class C[T]: class Inner[U](make_base(T for _ in (1,)), make_base(T)): pass """ - with self.assertRaisesRegex(SyntaxError, - "Cannot use comprehension in annotation scope within class scope"): - run_code(code) + ns = run_code(code) + inner = ns["C"].Inner + base1, base2, _ = inner.__bases__ + self.assertEqual(list(base1.__arg__), [ns["C"].__type_params__[0]]) + self.assertEqual(base2.__arg__, "class") def test_listcomp_in_nested_class(self): code = """ @@ -464,9 +466,11 @@ class C[T]: class Inner[U](make_base([T for _ in (1,)]), make_base(T)): pass """ - with self.assertRaisesRegex(SyntaxError, - "Cannot use comprehension in annotation scope within class scope"): - run_code(code) + ns = run_code(code) + inner = ns["C"].Inner + base1, base2, _ = inner.__bases__ + self.assertEqual(base1.__arg__, [ns["C"].__type_params__[0]]) + self.assertEqual(base2.__arg__, "class") def test_gen_exp_in_generic_method(self): code = """ @@ -475,27 +479,33 @@ class C[T]: def meth[U](x: (T for _ in (1,)), y: T): pass """ - with self.assertRaisesRegex(SyntaxError, - "Cannot use comprehension in annotation scope within class scope"): - run_code(code) + ns = run_code(code) + meth = ns["C"].meth + self.assertEqual(list(meth.__annotations__["x"]), [ns["C"].__type_params__[0]]) + self.assertEqual(meth.__annotations__["y"], "class") def test_nested_scope_in_generic_alias(self): code = """ - class C[T]: + T = "global" + class C: T = "class" {} """ - error_cases = [ - "type Alias3[T] = (T for _ in (1,))", - "type Alias4 = (T for _ in (1,))", - "type Alias5[T] = [T for _ in (1,)]", - "type Alias6 = [T for _ in (1,)]", + cases = [ + "type Alias[T] = (T for _ in (1,))", + "type Alias = (T for _ in (1,))", + "type Alias[T] = [T for _ in (1,)]", + "type Alias = [T for _ in (1,)]", ] - for case in error_cases: + for case in cases: with self.subTest(case=case): - with self.assertRaisesRegex(SyntaxError, - r"Cannot use [a-z]+ in annotation scope within class scope"): - run_code(code.format(case)) + ns = run_code(code.format(case)) + alias = ns["C"].Alias + value = list(alias.__value__)[0] + if alias.__type_params__: + self.assertIs(value, alias.__type_params__[0]) + else: + self.assertEqual(value, "global") def test_lambda_in_alias_in_class(self): code = """ diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-25-21-18-19.gh-issue-118160.GH5SMc.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-25-21-18-19.gh-issue-118160.GH5SMc.rst new file mode 100644 index 00000000000000..c4e798df5de702 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-25-21-18-19.gh-issue-118160.GH5SMc.rst @@ -0,0 +1,3 @@ +:ref:`Annotation scopes ` within classes can now contain +comprehensions. However, such comprehensions are not inlined into their +parent scope at runtime. Patch by Jelle Zijlstra. diff --git a/Python/symtable.c b/Python/symtable.c index 483ef1c3c46542..eecd159b2c3f17 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1154,10 +1154,12 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, } } - // we inline all non-generator-expression comprehensions + // we inline all non-generator-expression comprehensions, + // except those in annotation scopes that are nested in classes int inline_comp = entry->ste_comprehension && - !entry->ste_generator; + !entry->ste_generator && + !ste->ste_can_see_class_scope; if (!analyze_child_block(entry, newbound, newfree, newglobal, type_params, new_class_entry, &child_free)) @@ -2589,18 +2591,6 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e, identifier scope_name, asdl_comprehension_seq *generators, expr_ty elt, expr_ty value) { - if (st->st_cur->ste_can_see_class_scope) { - // gh-109118 - PyErr_Format(PyExc_SyntaxError, - "Cannot use comprehension in annotation scope within class scope"); - PyErr_RangedSyntaxLocationObject(st->st_filename, - e->lineno, - e->col_offset + 1, - e->end_lineno, - e->end_col_offset + 1); - VISIT_QUIT(st, 0); - } - int is_generator = (e->kind == GeneratorExp_kind); comprehension_ty outermost = ((comprehension_ty) asdl_seq_GET(generators, 0)); From c618d53a3a09c4b145c54fd9da4ca367be70f1e4 Mon Sep 17 00:00:00 2001 From: Xie Yanbo Date: Mon, 29 Apr 2024 01:00:48 +0800 Subject: [PATCH 077/217] Fix typo in Tools/wasm/README.md(#118358) --- Tools/wasm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index eca113d3bfabad..bc3e4ba8bd5b76 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -275,7 +275,7 @@ Node builds use ``NODERAWFS``. ### Hosting Python WASM builds The simple REPL terminal uses SharedArrayBuffer. For security reasons -browsers only provide the feature in secure environents with cross-origin +browsers only provide the feature in secure environments with cross-origin isolation. The webserver must send cross-origin headers and correct MIME types for the JavaScript and WASM files. Otherwise the terminal will fail to load with an error message like ``Browsers disable shared array buffer``. From e0ab6424369bcd276b0dc4f537fde360bdca2f05 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 28 Apr 2024 20:31:22 +0300 Subject: [PATCH 078/217] gh-101100: Fix Sphinx warnings in `whatsnew/3.9.rst` (#118364) --- Doc/whatsnew/3.7.rst | 6 ++--- Doc/whatsnew/3.9.rst | 48 ++++++++++++++++++++-------------------- Misc/NEWS.d/3.10.0a1.rst | 4 ++-- Misc/NEWS.d/3.11.0a4.rst | 2 +- Misc/NEWS.d/3.9.0a1.rst | 2 +- Misc/NEWS.d/3.9.0a2.rst | 2 +- Misc/NEWS.d/3.9.0a6.rst | 10 ++++----- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index ad7c8b5320180e..69d043bcf7efd5 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -669,8 +669,8 @@ include: * The new :func:`asyncio.current_task` function returns the currently running :class:`~asyncio.Task` instance, and the new :func:`asyncio.all_tasks` function returns a set of all existing ``Task`` instances in a given loop. - The :meth:`Task.current_task() ` and - :meth:`Task.all_tasks() ` methods have been deprecated. + The :meth:`!Task.current_task` and + :meth:`!Task.all_tasks` methods have been deprecated. (Contributed by Andrew Svetlov in :issue:`32250`.) * The new *provisional* :class:`~asyncio.BufferedProtocol` class allows @@ -1969,7 +1969,7 @@ asynchronous context manager must be used in order to acquire and release the synchronization resource. (Contributed by Andrew Svetlov in :issue:`32253`.) -The :meth:`asyncio.Task.current_task` and :meth:`asyncio.Task.all_tasks` +The :meth:`!asyncio.Task.current_task` and :meth:`!asyncio.Task.all_tasks` methods have been deprecated. (Contributed by Andrew Svetlov in :issue:`32250`.) diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index de248bc3584d9a..e29d37ca120b76 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -81,13 +81,13 @@ Interpreter improvements: * a number of Python builtins (range, tuple, set, frozenset, list, dict) are now sped up using :pep:`590` vectorcall; * garbage collection does not block on resurrected objects; -* a number of Python modules (:mod:`_abc`, :mod:`!audioop`, :mod:`_bz2`, - :mod:`_codecs`, :mod:`_contextvars`, :mod:`!_crypt`, :mod:`_functools`, - :mod:`_json`, :mod:`_locale`, :mod:`math`, :mod:`operator`, :mod:`resource`, - :mod:`time`, :mod:`_weakref`) now use multiphase initialization as defined +* a number of Python modules (:mod:`!_abc`, :mod:`!audioop`, :mod:`!_bz2`, + :mod:`!_codecs`, :mod:`!_contextvars`, :mod:`!_crypt`, :mod:`!_functools`, + :mod:`!_json`, :mod:`!_locale`, :mod:`math`, :mod:`operator`, :mod:`resource`, + :mod:`time`, :mod:`!_weakref`) now use multiphase initialization as defined by PEP 489; * a number of standard library modules (:mod:`!audioop`, :mod:`ast`, :mod:`grp`, - :mod:`_hashlib`, :mod:`pwd`, :mod:`_posixsubprocess`, :mod:`random`, + :mod:`!_hashlib`, :mod:`pwd`, :mod:`!_posixsubprocess`, :mod:`random`, :mod:`select`, :mod:`struct`, :mod:`termios`, :mod:`zlib`) are now using the stable ABI defined by PEP 384. @@ -203,7 +203,7 @@ The :mod:`ast` module uses the new parser and produces the same AST as the old parser. In Python 3.10, the old parser will be deleted and so will all -functionality that depends on it (primarily the :mod:`parser` module, +functionality that depends on it (primarily the :mod:`!parser` module, which has long been deprecated). In Python 3.9 *only*, you can switch back to the LL(1) parser using a command line switch (``-X oldparser``) or an environment variable (``PYTHONOLDPARSER=1``). @@ -366,7 +366,7 @@ wait until the cancellation is complete also in the case when *timeout* is <= 0, like it does with positive timeouts. (Contributed by Elvis Pranskevichus in :issue:`32751`.) -:mod:`asyncio` now raises :exc:`TyperError` when calling incompatible +:mod:`asyncio` now raises :exc:`TypeError` when calling incompatible methods with an :class:`ssl.SSLSocket` socket. (Contributed by Ido Michael in :issue:`37404`.) @@ -589,7 +589,7 @@ a non-blocking socket. (Contributed by Donghee Na in :issue:`39259`.) os -- -Added :const:`~os.CLD_KILLED` and :const:`~os.CLD_STOPPED` for :attr:`si_code`. +Added :const:`~os.CLD_KILLED` and :const:`~os.CLD_STOPPED` for :attr:`!si_code`. (Contributed by Donghee Na in :issue:`38493`.) Exposed the Linux-specific :func:`os.pidfd_open` (:issue:`38692`) and @@ -861,7 +861,7 @@ Deprecated Python versions it will raise a :exc:`TypeError` for all floats. (Contributed by Serhiy Storchaka in :issue:`37315`.) -* The :mod:`parser` and :mod:`symbol` modules are deprecated and will be +* The :mod:`!parser` and :mod:`!symbol` modules are deprecated and will be removed in future versions of Python. For the majority of use cases, users can leverage the Abstract Syntax Tree (AST) generation and compilation stage, using the :mod:`ast` module. @@ -889,7 +889,7 @@ Deprecated it for writing and silencing a warning. (Contributed by Serhiy Storchaka in :issue:`28286`.) -* Deprecated the ``split()`` method of :class:`_tkinter.TkappType` in +* Deprecated the ``split()`` method of :class:`!_tkinter.TkappType` in favour of the ``splitlist()`` method which has more consistent and predicable behavior. (Contributed by Serhiy Storchaka in :issue:`38371`.) @@ -898,11 +898,11 @@ Deprecated deprecated and will be removed in version 3.11. (Contributed by Yury Selivanov and Kyle Stanley in :issue:`34790`.) -* binhex4 and hexbin4 standards are now deprecated. The :mod:`binhex` module +* binhex4 and hexbin4 standards are now deprecated. The :mod:`!binhex` module and the following :mod:`binascii` functions are now deprecated: - * :func:`~binascii.b2a_hqx`, :func:`~binascii.a2b_hqx` - * :func:`~binascii.rlecode_hqx`, :func:`~binascii.rledecode_hqx` + * :func:`!b2a_hqx`, :func:`!a2b_hqx` + * :func:`!rlecode_hqx`, :func:`!rledecode_hqx` (Contributed by Victor Stinner in :issue:`39353`.) @@ -950,7 +950,7 @@ Deprecated Removed ======= -* The erroneous version at :data:`unittest.mock.__version__` has been removed. +* The erroneous version at :data:`!unittest.mock.__version__` has been removed. * :class:`!nntplib.NNTP`: ``xpath()`` and ``xgtitle()`` methods have been removed. These methods are deprecated since Python 3.3. Generally, these extensions @@ -987,7 +987,7 @@ Removed removed. They were deprecated since Python 3.7. (Contributed by Victor Stinner in :issue:`37320`.) -* The :meth:`~threading.Thread.isAlive()` method of :class:`threading.Thread` +* The :meth:`!isAlive()` method of :class:`threading.Thread` has been removed. It was deprecated since Python 3.8. Use :meth:`~threading.Thread.is_alive()` instead. (Contributed by Donghee Na in :issue:`37804`.) @@ -1035,7 +1035,7 @@ Removed ``asyncio.Condition`` and ``asyncio.Semaphore``. (Contributed by Andrew Svetlov in :issue:`34793`.) -* The :func:`sys.getcounts` function, the ``-X showalloccount`` command line +* The :func:`!sys.getcounts` function, the ``-X showalloccount`` command line option and the ``show_alloc_count`` field of the C structure :c:type:`PyConfig` have been removed. They required a special Python build by defining ``COUNT_ALLOCS`` macro. @@ -1046,11 +1046,11 @@ Removed the ``__annotations__`` attribute instead. (Contributed by Serhiy Storchaka in :issue:`40182`.) -* The :meth:`symtable.SymbolTable.has_exec` method has been removed. It was +* The :meth:`!symtable.SymbolTable.has_exec` method has been removed. It was deprecated since 2006, and only returning ``False`` when it's called. (Contributed by Batuhan Taskaya in :issue:`40208`) -* The :meth:`asyncio.Task.current_task` and :meth:`asyncio.Task.all_tasks` +* The :meth:`!asyncio.Task.current_task` and :meth:`!asyncio.Task.all_tasks` have been removed. They were deprecated since Python 3.7 and you can use :func:`asyncio.current_task` and :func:`asyncio.all_tasks` instead. (Contributed by Rémi Lapeyre in :issue:`40967`) @@ -1230,7 +1230,7 @@ Build Changes * The ``COUNT_ALLOCS`` special build macro has been removed. (Contributed by Victor Stinner in :issue:`39489`.) -* On non-Windows platforms, the :c:func:`setenv` and :c:func:`unsetenv` +* On non-Windows platforms, the :c:func:`!setenv` and :c:func:`!unsetenv` functions are now required to build Python. (Contributed by Victor Stinner in :issue:`39395`.) @@ -1319,7 +1319,7 @@ New Features the garbage collector respectively. (Contributed by Pablo Galindo Salgado in :issue:`40241`.) -* Added :c:func:`_PyObject_FunctionStr` to get a user-friendly string +* Added :c:func:`!_PyObject_FunctionStr` to get a user-friendly string representation of a function-like object. (Patch by Jeroen Demeyer in :issue:`37645`.) @@ -1361,7 +1361,7 @@ Porting to Python 3.9 and refers to a constant string. (Contributed by Serhiy Storchaka in :issue:`38650`.) -* The :c:type:`PyGC_Head` structure is now opaque. It is only defined in the +* The :c:type:`!PyGC_Head` structure is now opaque. It is only defined in the internal C API (``pycore_gc.h``). (Contributed by Victor Stinner in :issue:`40241`.) @@ -1384,12 +1384,12 @@ Porting to Python 3.9 * :c:func:`PyObject_IS_GC` macro was converted to a function. - * The :c:func:`PyObject_NEW` macro becomes an alias to the - :c:macro:`PyObject_New` macro, and the :c:func:`PyObject_NEW_VAR` macro + * The :c:func:`!PyObject_NEW` macro becomes an alias to the + :c:macro:`PyObject_New` macro, and the :c:func:`!PyObject_NEW_VAR` macro becomes an alias to the :c:macro:`PyObject_NewVar` macro. They no longer access directly the :c:member:`PyTypeObject.tp_basicsize` member. - * :c:func:`PyObject_GET_WEAKREFS_LISTPTR` macro was converted to a function: + * :c:func:`!PyObject_GET_WEAKREFS_LISTPTR` macro was converted to a function: the macro accessed directly the :c:member:`PyTypeObject.tp_weaklistoffset` member. diff --git a/Misc/NEWS.d/3.10.0a1.rst b/Misc/NEWS.d/3.10.0a1.rst index 2e32ca9f3b26bb..9a729a45b160eb 100644 --- a/Misc/NEWS.d/3.10.0a1.rst +++ b/Misc/NEWS.d/3.10.0a1.rst @@ -1861,8 +1861,8 @@ bundled versions of ``pip`` and ``setuptools``. Patch by Krzysztof Konopko. .. nonce: _dx3OO .. section: Library -Removed :meth:`asyncio.Task.current_task` and -:meth:`asyncio.Task.all_tasks`. Patch contributed by Rémi Lapeyre. +Removed :meth:`!asyncio.Task.current_task` and +:meth:`!asyncio.Task.all_tasks`. Patch contributed by Rémi Lapeyre. .. diff --git a/Misc/NEWS.d/3.11.0a4.rst b/Misc/NEWS.d/3.11.0a4.rst index 78b682f7a22cc6..a5ce7620016cc7 100644 --- a/Misc/NEWS.d/3.11.0a4.rst +++ b/Misc/NEWS.d/3.11.0a4.rst @@ -7,7 +7,7 @@ :c:func:`Py_EndInterpreter` now explicitly untracks all objects currently tracked by the GC. Previously, if an object was used later by another interpreter, calling :c:func:`PyObject_GC_UnTrack` on the object crashed if -the previous or the next object of the :c:type:`PyGC_Head` structure became +the previous or the next object of the :c:type:`!PyGC_Head` structure became a dangling pointer. Patch by Victor Stinner. .. diff --git a/Misc/NEWS.d/3.9.0a1.rst b/Misc/NEWS.d/3.9.0a1.rst index 8f38f04eb41798..39d760cdd4fddf 100644 --- a/Misc/NEWS.d/3.9.0a1.rst +++ b/Misc/NEWS.d/3.9.0a1.rst @@ -5616,7 +5616,7 @@ heap type .. nonce: 4DcUaI .. section: C API -Add :c:func:`_PyObject_FunctionStr` to get a user-friendly string +Add :c:func:`!_PyObject_FunctionStr` to get a user-friendly string representation of a function-like object. Patch by Jeroen Demeyer. .. diff --git a/Misc/NEWS.d/3.9.0a2.rst b/Misc/NEWS.d/3.9.0a2.rst index 7d878cfe227552..39b1c308312aa4 100644 --- a/Misc/NEWS.d/3.9.0a2.rst +++ b/Misc/NEWS.d/3.9.0a2.rst @@ -844,7 +844,7 @@ test.regrtest now can receive a list of test patterns to ignore (using the .. nonce: cNsA7S .. section: Build -:mod:`asyncio` now raises :exc:`TyperError` when calling incompatible +:mod:`asyncio` now raises :exc:`TypeError` when calling incompatible methods with an :class:`ssl.SSLSocket` socket. Patch by Ido Michael. .. diff --git a/Misc/NEWS.d/3.9.0a6.rst b/Misc/NEWS.d/3.9.0a6.rst index 26a6fb98efdc36..466ff624fcbf81 100644 --- a/Misc/NEWS.d/3.9.0a6.rst +++ b/Misc/NEWS.d/3.9.0a6.rst @@ -564,7 +564,7 @@ Implement traverse and clear slots in _abc._abc_data type. .. nonce: 3rO_q7 .. section: Library -Remove deprecated :meth:`symtable.SymbolTable.has_exec`. +Remove deprecated :meth:`!symtable.SymbolTable.has_exec`. .. @@ -1118,7 +1118,7 @@ into an exit code. .. nonce: _FOf7E .. section: C API -Move the :c:type:`PyGC_Head` structure to the internal C API. +Move the :c:type:`!PyGC_Head` structure to the internal C API. .. @@ -1149,8 +1149,8 @@ the garbage collector respectively. Patch by Pablo Galindo. .. nonce: Seuh3D .. section: C API -The :c:func:`PyObject_NEW` macro becomes an alias to the -:c:func:`PyObject_New` macro, and the :c:func:`PyObject_NEW_VAR` macro +The :c:func:`!PyObject_NEW` macro becomes an alias to the +:c:func:`PyObject_New` macro, and the :c:func:`!PyObject_NEW_VAR` macro becomes an alias to the :c:func:`PyObject_NewVar` macro, to hide implementation details. They no longer access directly the :c:member:`PyTypeObject.tp_basicsize` member. @@ -1174,7 +1174,7 @@ used. .. nonce: 6nFYbY .. section: C API -Convert the :c:func:`PyObject_GET_WEAKREFS_LISTPTR` macro to a function to +Convert the :c:func:`!PyObject_GET_WEAKREFS_LISTPTR` macro to a function to hide implementation details: the macro accessed directly to the :c:member:`PyTypeObject.tp_weaklistoffset` member. From 33c6cf3148781993aa1665b1f2231d5442df8f40 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 28 Apr 2024 21:06:45 +0300 Subject: [PATCH 079/217] gh-101100: Fix Sphinx warnings in `library/faulthandler.rst` (#118353) --- Doc/library/faulthandler.rst | 16 +++++++++------- Doc/tools/.nitignore | 1 - 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst index 96593ee97a139d..c40d5e9aacb83c 100644 --- a/Doc/library/faulthandler.rst +++ b/Doc/library/faulthandler.rst @@ -10,14 +10,15 @@ This module contains functions to dump Python tracebacks explicitly, on a fault, after a timeout, or on a user signal. Call :func:`faulthandler.enable` to -install fault handlers for the :const:`SIGSEGV`, :const:`SIGFPE`, -:const:`SIGABRT`, :const:`SIGBUS`, and :const:`SIGILL` signals. You can also +install fault handlers for the :const:`~signal.SIGSEGV`, +:const:`~signal.SIGFPE`, :const:`~signal.SIGABRT`, :const:`~signal.SIGBUS`, and +:const:`~signal.SIGILL` signals. You can also enable them at startup by setting the :envvar:`PYTHONFAULTHANDLER` environment variable or by using the :option:`-X` ``faulthandler`` command line option. The fault handler is compatible with system fault handlers like Apport or the Windows fault handler. The module uses an alternative stack for signal handlers -if the :c:func:`sigaltstack` function is available. This allows it to dump the +if the :c:func:`!sigaltstack` function is available. This allows it to dump the traceback even on a stack overflow. The fault handler is called on catastrophic cases and therefore can only use @@ -70,8 +71,9 @@ Fault handler state .. function:: enable(file=sys.stderr, all_threads=True) - Enable the fault handler: install handlers for the :const:`SIGSEGV`, - :const:`SIGFPE`, :const:`SIGABRT`, :const:`SIGBUS` and :const:`SIGILL` + Enable the fault handler: install handlers for the :const:`~signal.SIGSEGV`, + :const:`~signal.SIGFPE`, :const:`~signal.SIGABRT`, :const:`~signal.SIGBUS` + and :const:`~signal.SIGILL` signals to dump the Python traceback. If *all_threads* is ``True``, produce tracebacks for every running thread. Otherwise, dump only the current thread. @@ -106,8 +108,8 @@ Dumping the tracebacks after a timeout Dump the tracebacks of all threads, after a timeout of *timeout* seconds, or every *timeout* seconds if *repeat* is ``True``. If *exit* is ``True``, call - :c:func:`_exit` with status=1 after dumping the tracebacks. (Note - :c:func:`_exit` exits the process immediately, which means it doesn't do any + :c:func:`!_exit` with status=1 after dumping the tracebacks. (Note + :c:func:`!_exit` exits the process immediately, which means it doesn't do any cleanup like flushing file buffers.) If the function is called twice, the new call replaces previous parameters and resets the timeout. The timer has a sub-second resolution. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 6f38f36d3311c0..4790136a75cba9 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -28,7 +28,6 @@ Doc/library/email.errors.rst Doc/library/email.parser.rst Doc/library/email.policy.rst Doc/library/exceptions.rst -Doc/library/faulthandler.rst Doc/library/functools.rst Doc/library/http.cookiejar.rst Doc/library/http.server.rst From f5b7e397c0a0e180257450843ab622ab8783adf6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 28 Apr 2024 21:12:25 +0300 Subject: [PATCH 080/217] gh-101100: Fix Sphinx warnings in `whatsnew/3.10.rst` (#118356) --- Doc/whatsnew/3.10.rst | 74 +++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 1a4ecdf1737303..b939ccd17903f2 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -352,7 +352,7 @@ was expecting an indentation, including the location of the statement: AttributeErrors ~~~~~~~~~~~~~~~ -When printing :exc:`AttributeError`, :c:func:`PyErr_Display` will offer +When printing :exc:`AttributeError`, :c:func:`!PyErr_Display` will offer suggestions of similar attribute names in the object that the exception was raised from: @@ -366,14 +366,14 @@ raised from: (Contributed by Pablo Galindo in :issue:`38530`.) .. warning:: - Notice this won't work if :c:func:`PyErr_Display` is not called to display the error + Notice this won't work if :c:func:`!PyErr_Display` is not called to display the error which can happen if some other custom error display function is used. This is a common scenario in some REPLs like IPython. NameErrors ~~~~~~~~~~ -When printing :exc:`NameError` raised by the interpreter, :c:func:`PyErr_Display` +When printing :exc:`NameError` raised by the interpreter, :c:func:`!PyErr_Display` will offer suggestions of similar variable names in the function that the exception was raised from: @@ -388,7 +388,7 @@ was raised from: (Contributed by Pablo Galindo in :issue:`38530`.) .. warning:: - Notice this won't work if :c:func:`PyErr_Display` is not called to display the error, + Notice this won't work if :c:func:`!PyErr_Display` is not called to display the error, which can happen if some other custom error display function is used. This is a common scenario in some REPLs like IPython. @@ -690,7 +690,7 @@ are in :pep:`635`, and a longer tutorial is in :pep:`636`. Optional ``EncodingWarning`` and ``encoding="locale"`` option ------------------------------------------------------------- -The default encoding of :class:`TextIOWrapper` and :func:`open` is +The default encoding of :class:`~io.TextIOWrapper` and :func:`open` is platform and locale dependent. Since UTF-8 is used on most Unix platforms, omitting ``encoding`` option when opening UTF-8 files (e.g. JSON, YAML, TOML, Markdown) is a very common bug. For example:: @@ -785,7 +785,7 @@ especially when forward references or invalid types were involved. Compare:: StrCache = 'Cache[str]' # a type alias LOG_PREFIX = 'LOG[DEBUG]' # a module constant -Now the :mod:`typing` module has a special value :data:`TypeAlias` +Now the :mod:`typing` module has a special value :data:`~typing.TypeAlias` which lets you declare type aliases more explicitly:: StrCache: TypeAlias = 'Cache[str]' # a type alias @@ -798,10 +798,10 @@ See :pep:`613` for more details. PEP 647: User-Defined Type Guards --------------------------------- -:data:`TypeGuard` has been added to the :mod:`typing` module to annotate +:data:`~typing.TypeGuard` has been added to the :mod:`typing` module to annotate type guard functions and improve information provided to static type checkers -during type narrowing. For more information, please see :data:`TypeGuard`\ 's -documentation, and :pep:`647`. +during type narrowing. For more information, please see +:data:`~typing.TypeGuard`\ 's documentation, and :pep:`647`. (Contributed by Ken Jin and Guido van Rossum in :issue:`43766`. PEP written by Eric Traut.) @@ -972,8 +972,8 @@ and objects representing asynchronously released resources. Add asynchronous context manager support to :func:`contextlib.nullcontext`. (Contributed by Tom Gringauz in :issue:`41543`.) -Add :class:`AsyncContextDecorator`, for supporting usage of async context managers -as decorators. +Add :class:`~contextlib.AsyncContextDecorator`, for supporting usage of async +context managers as decorators. curses ------ @@ -1089,8 +1089,8 @@ encodings enum ---- -:class:`Enum` :func:`__repr__` now returns ``enum_name.member_name`` and -:func:`__str__` now returns ``member_name``. Stdlib enums available as +:class:`~enum.Enum` :func:`~object.__repr__` now returns ``enum_name.member_name`` and +:func:`~object.__str__` now returns ``member_name``. Stdlib enums available as module constants have a :func:`repr` of ``module_name.member_name``. (Contributed by Ethan Furman in :issue:`40066`.) @@ -1104,7 +1104,7 @@ Add *encoding* and *errors* parameters in :func:`fileinput.input` and :class:`fileinput.FileInput`. (Contributed by Inada Naoki in :issue:`43712`.) -:func:`fileinput.hook_compressed` now returns :class:`TextIOWrapper` object +:func:`fileinput.hook_compressed` now returns :class:`~io.TextIOWrapper` object when *mode* is "r" and file is compressed, like uncompressed files. (Contributed by Inada Naoki in :issue:`5758`.) @@ -1202,12 +1202,12 @@ Feature parity with ``importlib_metadata`` 4.6 :ref:`importlib.metadata entry points ` now provide a nicer experience for selecting entry points by group and name through a new -:class:`importlib.metadata.EntryPoints` class. See the Compatibility +:ref:`importlib.metadata.EntryPoints ` class. See the Compatibility Note in the docs for more info on the deprecation and usage. -Added :func:`importlib.metadata.packages_distributions` for resolving -top-level Python modules and packages to their -:class:`importlib.metadata.Distribution`. +Added :ref:`importlib.metadata.packages_distributions() ` +for resolving top-level Python modules and packages to their +:ref:`importlib.metadata.Distribution `. inspect ------- @@ -1224,7 +1224,7 @@ best practice for accessing the annotations dict defined on any Python object; for more information on best practices for working with annotations, please see :ref:`annotations-howto`. Relatedly, :func:`inspect.signature`, -:func:`inspect.Signature.from_callable`, and :func:`inspect.Signature.from_function` +:func:`inspect.Signature.from_callable`, and :func:`!inspect.Signature.from_function` now call :func:`inspect.get_annotations` to retrieve annotations. This means :func:`inspect.signature` and :func:`inspect.Signature.from_callable` can also now un-stringize stringized annotations. @@ -1484,9 +1484,9 @@ is a :class:`typing.TypedDict`. Subclasses of ``typing.Protocol`` which only have data variables declared will now raise a ``TypeError`` when checked with ``isinstance`` unless they -are decorated with :func:`runtime_checkable`. Previously, these checks +are decorated with :func:`~typing.runtime_checkable`. Previously, these checks passed silently. Users should decorate their -subclasses with the :func:`runtime_checkable` decorator +subclasses with the :func:`!runtime_checkable` decorator if they want runtime protocols. (Contributed by Yurii Karabas in :issue:`38908`.) @@ -1595,8 +1595,8 @@ Optimizations :func:`map`, :func:`filter`, :func:`reversed`, :func:`bool` and :func:`float`. (Contributed by Donghee Na and Jeroen Demeyer in :issue:`43575`, :issue:`43287`, :issue:`41922`, :issue:`41873` and :issue:`41870`.) -* :class:`BZ2File` performance is improved by removing internal ``RLock``. - This makes :class:`BZ2File` thread unsafe in the face of multiple simultaneous +* :class:`~bz2.BZ2File` performance is improved by removing internal ``RLock``. + This makes :class:`!BZ2File` thread unsafe in the face of multiple simultaneous readers or writers, just like its equivalent classes in :mod:`gzip` and :mod:`lzma` have always been. (Contributed by Inada Naoki in :issue:`43785`.) @@ -1620,7 +1620,7 @@ Deprecated cleaning up old import semantics that were kept for Python 2.7 compatibility. Specifically, :meth:`!find_loader`/:meth:`!find_module` - (superseded by :meth:`~importlib.abc.Finder.find_spec`), + (superseded by :meth:`~importlib.abc.MetaPathFinder.find_spec`), :meth:`~importlib.abc.Loader.load_module` (superseded by :meth:`~importlib.abc.Loader.exec_module`), :meth:`!module_repr` (which the import system @@ -1647,7 +1647,7 @@ Deprecated :meth:`~importlib.abc.Loader.exec_module` instead. (Contributed by Brett Cannon in :issue:`26131`.) -* :meth:`zimport.zipimporter.load_module` has been deprecated in +* :meth:`!zimport.zipimporter.load_module` has been deprecated in preference for :meth:`~zipimport.zipimporter.exec_module`. (Contributed by Brett Cannon in :issue:`26131`.) @@ -1759,23 +1759,23 @@ Deprecated * The following :mod:`ssl` features have been deprecated since Python 3.6, Python 3.7, or OpenSSL 1.1.0 and will be removed in 3.11: - * :data:`~ssl.OP_NO_SSLv2`, :data:`~ssl.OP_NO_SSLv3`, :data:`~ssl.OP_NO_TLSv1`, - :data:`~ssl.OP_NO_TLSv1_1`, :data:`~ssl.OP_NO_TLSv1_2`, and - :data:`~ssl.OP_NO_TLSv1_3` are replaced by - :attr:`sslSSLContext.minimum_version` and - :attr:`sslSSLContext.maximum_version`. + * :data:`!OP_NO_SSLv2`, :data:`!OP_NO_SSLv3`, :data:`!OP_NO_TLSv1`, + :data:`!OP_NO_TLSv1_1`, :data:`!OP_NO_TLSv1_2`, and + :data:`!OP_NO_TLSv1_3` are replaced by + :attr:`~ssl.SSLContext.minimum_version` and + :attr:`~ssl.SSLContext.maximum_version`. - * :data:`~ssl.PROTOCOL_SSLv2`, :data:`~ssl.PROTOCOL_SSLv3`, - :data:`~ssl.PROTOCOL_SSLv23`, :data:`~ssl.PROTOCOL_TLSv1`, - :data:`~ssl.PROTOCOL_TLSv1_1`, :data:`~ssl.PROTOCOL_TLSv1_2`, and - :const:`~ssl.PROTOCOL_TLS` are deprecated in favor of + * :data:`!PROTOCOL_SSLv2`, :data:`!PROTOCOL_SSLv3`, + :data:`!PROTOCOL_SSLv23`, :data:`!PROTOCOL_TLSv1`, + :data:`!PROTOCOL_TLSv1_1`, :data:`!PROTOCOL_TLSv1_2`, and + :const:`!PROTOCOL_TLS` are deprecated in favor of :const:`~ssl.PROTOCOL_TLS_CLIENT` and :const:`~ssl.PROTOCOL_TLS_SERVER` - * :func:`~ssl.wrap_socket` is replaced by :meth:`ssl.SSLContext.wrap_socket` + * :func:`!wrap_socket` is replaced by :meth:`ssl.SSLContext.wrap_socket` - * :func:`~ssl.match_hostname` + * :func:`!match_hostname` - * :func:`~ssl.RAND_pseudo_bytes`, :func:`~ssl.RAND_egd` + * :func:`!RAND_pseudo_bytes`, :func:`!RAND_egd` * NPN features like :meth:`ssl.SSLSocket.selected_npn_protocol` and :meth:`ssl.SSLContext.set_npn_protocols` are replaced by ALPN. From 133c1a7cdb19dd9317e7607ecf8f4fd4fb5842f6 Mon Sep 17 00:00:00 2001 From: Henrik Tunedal Date: Sun, 28 Apr 2024 23:10:44 +0200 Subject: [PATCH 081/217] gh-118293: Suppress mouse cursor feedback when launching Windows processes with multiprocessing (GH-118315) --- Doc/library/subprocess.rst | 16 +++++++ Lib/multiprocessing/popen_spawn_win32.py | 4 +- Lib/subprocess.py | 2 + ...-04-26-14-23-07.gh-issue-118293.ohhPtW.rst | 2 + Modules/_winapi.c | 48 +++++++++++++++++++ 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-04-26-14-23-07.gh-issue-118293.ohhPtW.rst diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 1cd233173e85e1..bd35fda7d79225 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -1066,6 +1066,22 @@ The :mod:`subprocess` module exposes the following constants. Specifies that the :attr:`STARTUPINFO.wShowWindow` attribute contains additional information. +.. data:: STARTF_FORCEONFEEDBACK + + A :attr:`STARTUPINFO.dwFlags` parameter to specify that the + *Working in Background* mouse cursor will be displayed while a + process is launching. This is the default behavior for GUI + processes. + + .. versionadded:: 3.13 + +.. data:: STARTF_FORCEOFFFEEDBACK + + A :attr:`STARTUPINFO.dwFlags` parameter to specify that the mouse + cursor will not be changed when launching a process. + + .. versionadded:: 3.13 + .. data:: CREATE_NEW_CONSOLE The new process has a new console, instead of inheriting its parent's diff --git a/Lib/multiprocessing/popen_spawn_win32.py b/Lib/multiprocessing/popen_spawn_win32.py index 49d4c7eea22411..62fb0ddbf91a5d 100644 --- a/Lib/multiprocessing/popen_spawn_win32.py +++ b/Lib/multiprocessing/popen_spawn_win32.py @@ -3,6 +3,7 @@ import signal import sys import _winapi +from subprocess import STARTUPINFO, STARTF_FORCEOFFFEEDBACK from .context import reduction, get_spawning_popen, set_spawning_popen from . import spawn @@ -74,7 +75,8 @@ def __init__(self, process_obj): try: hp, ht, pid, tid = _winapi.CreateProcess( python_exe, cmd, - None, None, False, 0, env, None, None) + None, None, False, 0, env, None, + STARTUPINFO(dwFlags=STARTF_FORCEOFFFEEDBACK)) _winapi.CloseHandle(ht) except: _winapi.CloseHandle(rhandle) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index d7c7b45127104f..212fdf5b095511 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -83,6 +83,7 @@ STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE, SW_HIDE, STARTF_USESTDHANDLES, STARTF_USESHOWWINDOW, + STARTF_FORCEONFEEDBACK, STARTF_FORCEOFFFEEDBACK, ABOVE_NORMAL_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS, HIGH_PRIORITY_CLASS, IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS, @@ -93,6 +94,7 @@ "STD_INPUT_HANDLE", "STD_OUTPUT_HANDLE", "STD_ERROR_HANDLE", "SW_HIDE", "STARTF_USESTDHANDLES", "STARTF_USESHOWWINDOW", + "STARTF_FORCEONFEEDBACK", "STARTF_FORCEOFFFEEDBACK", "STARTUPINFO", "ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS", "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", diff --git a/Misc/NEWS.d/next/Windows/2024-04-26-14-23-07.gh-issue-118293.ohhPtW.rst b/Misc/NEWS.d/next/Windows/2024-04-26-14-23-07.gh-issue-118293.ohhPtW.rst new file mode 100644 index 00000000000000..7383a2b32d7240 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-04-26-14-23-07.gh-issue-118293.ohhPtW.rst @@ -0,0 +1,2 @@ +The ``multiprocessing`` module now passes the ``STARTF_FORCEOFFFEEDBACK`` +flag when spawning processes to tell Windows not to change the mouse cursor. diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 57b8bdc7ea2448..23e3c0d87f0319 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -72,9 +72,45 @@ #ifndef STARTF_USESHOWWINDOW #define STARTF_USESHOWWINDOW 0x00000001 #endif +#ifndef STARTF_USESIZE +#define STARTF_USESIZE 0x00000002 +#endif +#ifndef STARTF_USEPOSITION +#define STARTF_USEPOSITION 0x00000004 +#endif +#ifndef STARTF_USECOUNTCHARS +#define STARTF_USECOUNTCHARS 0x00000008 +#endif +#ifndef STARTF_USEFILLATTRIBUTE +#define STARTF_USEFILLATTRIBUTE 0x00000010 +#endif +#ifndef STARTF_RUNFULLSCREEN +#define STARTF_RUNFULLSCREEN 0x00000020 +#endif +#ifndef STARTF_FORCEONFEEDBACK +#define STARTF_FORCEONFEEDBACK 0x00000040 +#endif +#ifndef STARTF_FORCEOFFFEEDBACK +#define STARTF_FORCEOFFFEEDBACK 0x00000080 +#endif #ifndef STARTF_USESTDHANDLES #define STARTF_USESTDHANDLES 0x00000100 #endif +#ifndef STARTF_USEHOTKEY +#define STARTF_USEHOTKEY 0x00000200 +#endif +#ifndef STARTF_TITLEISLINKNAME +#define STARTF_TITLEISLINKNAME 0x00000800 +#endif +#ifndef STARTF_TITLEISAPPID +#define STARTF_TITLEISAPPID 0x00001000 +#endif +#ifndef STARTF_PREVENTPINNING +#define STARTF_PREVENTPINNING 0x00002000 +#endif +#ifndef STARTF_UNTRUSTEDSOURCE +#define STARTF_UNTRUSTEDSOURCE 0x00008000 +#endif typedef struct { PyTypeObject *overlapped_type; @@ -3061,7 +3097,19 @@ static int winapi_exec(PyObject *m) WINAPI_CONSTANT(F_DWORD, SEC_RESERVE); WINAPI_CONSTANT(F_DWORD, SEC_WRITECOMBINE); WINAPI_CONSTANT(F_DWORD, STARTF_USESHOWWINDOW); + WINAPI_CONSTANT(F_DWORD, STARTF_USESIZE); + WINAPI_CONSTANT(F_DWORD, STARTF_USEPOSITION); + WINAPI_CONSTANT(F_DWORD, STARTF_USECOUNTCHARS); + WINAPI_CONSTANT(F_DWORD, STARTF_USEFILLATTRIBUTE); + WINAPI_CONSTANT(F_DWORD, STARTF_RUNFULLSCREEN); + WINAPI_CONSTANT(F_DWORD, STARTF_FORCEONFEEDBACK); + WINAPI_CONSTANT(F_DWORD, STARTF_FORCEOFFFEEDBACK); WINAPI_CONSTANT(F_DWORD, STARTF_USESTDHANDLES); + WINAPI_CONSTANT(F_DWORD, STARTF_USEHOTKEY); + WINAPI_CONSTANT(F_DWORD, STARTF_TITLEISLINKNAME); + WINAPI_CONSTANT(F_DWORD, STARTF_TITLEISAPPID); + WINAPI_CONSTANT(F_DWORD, STARTF_PREVENTPINNING); + WINAPI_CONSTANT(F_DWORD, STARTF_UNTRUSTEDSOURCE); WINAPI_CONSTANT(F_DWORD, STD_INPUT_HANDLE); WINAPI_CONSTANT(F_DWORD, STD_OUTPUT_HANDLE); WINAPI_CONSTANT(F_DWORD, STD_ERROR_HANDLE); From aa8f6d2708bce1462544d2e2cae05a2595ffd9ec Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Mon, 29 Apr 2024 08:38:46 +0300 Subject: [PATCH 082/217] gh-118374: test_ast: Add ``ctx`` argument to ``ast.Name`` calls (#118375) --- Lib/test/test_ast.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 44bcb9bae1cfde..68024304dfb746 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -1353,13 +1353,13 @@ def test_dump_incomplete(self): [], [ast.keyword('a', ast.Constant(None))], [], - [ast.Name('dataclass')], + [ast.Name('dataclass', ctx=ast.Load())], ) self.assertEqual(ast.dump(node), - "ClassDef(name='T', keywords=[keyword(arg='a', value=Constant(value=None))], decorator_list=[Name(id='dataclass')])", + "ClassDef(name='T', keywords=[keyword(arg='a', value=Constant(value=None))], decorator_list=[Name(id='dataclass', ctx=Load())])", ) self.assertEqual(ast.dump(node, annotate_fields=False), - "ClassDef('T', [], [keyword('a', Constant(None))], [], [Name('dataclass')])", + "ClassDef('T', [], [keyword('a', Constant(None))], [], [Name('dataclass', Load())])", ) def test_dump_show_empty(self): From ab6eda0ee59587e84cb417dd84452b9c6845434c Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 29 Apr 2024 07:54:05 +0100 Subject: [PATCH 083/217] GH-118095: Allow a variant of RESUME_CHECK in tier 2 (GH-118286) --- Include/internal/pycore_uop_ids.h | 206 +++++++++++++------------ Include/internal/pycore_uop_metadata.h | 8 + Python/bytecodes.c | 23 +++ Python/executor_cases.c.h | 27 ++++ Python/optimizer.c | 20 ++- Python/optimizer_cases.c.h | 8 + 6 files changed, 189 insertions(+), 103 deletions(-) diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index beb182c436d52a..030321ef4fcb23 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -94,46 +94,47 @@ extern "C" { #define _DYNAMIC_EXIT 343 #define _END_SEND END_SEND #define _ERROR_POP_N 344 +#define _EVAL_BREAKER_EXIT 345 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK -#define _FATAL_ERROR 345 +#define _FATAL_ERROR 346 #define _FORMAT_SIMPLE FORMAT_SIMPLE #define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC -#define _FOR_ITER 346 -#define _FOR_ITER_GEN_FRAME 347 -#define _FOR_ITER_TIER_TWO 348 +#define _FOR_ITER 347 +#define _FOR_ITER_GEN_FRAME 348 +#define _FOR_ITER_TIER_TWO 349 #define _GET_AITER GET_AITER #define _GET_ANEXT GET_ANEXT #define _GET_AWAITABLE GET_AWAITABLE #define _GET_ITER GET_ITER #define _GET_LEN GET_LEN #define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER -#define _GUARD_BOTH_FLOAT 349 -#define _GUARD_BOTH_INT 350 -#define _GUARD_BOTH_UNICODE 351 -#define _GUARD_BUILTINS_VERSION 352 -#define _GUARD_DORV_NO_DICT 353 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 354 -#define _GUARD_GLOBALS_VERSION 355 -#define _GUARD_IS_FALSE_POP 356 -#define _GUARD_IS_NONE_POP 357 -#define _GUARD_IS_NOT_NONE_POP 358 -#define _GUARD_IS_TRUE_POP 359 -#define _GUARD_KEYS_VERSION 360 -#define _GUARD_NOS_FLOAT 361 -#define _GUARD_NOS_INT 362 -#define _GUARD_NOT_EXHAUSTED_LIST 363 -#define _GUARD_NOT_EXHAUSTED_RANGE 364 -#define _GUARD_NOT_EXHAUSTED_TUPLE 365 -#define _GUARD_TOS_FLOAT 366 -#define _GUARD_TOS_INT 367 -#define _GUARD_TYPE_VERSION 368 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 369 -#define _INIT_CALL_PY_EXACT_ARGS 370 -#define _INIT_CALL_PY_EXACT_ARGS_0 371 -#define _INIT_CALL_PY_EXACT_ARGS_1 372 -#define _INIT_CALL_PY_EXACT_ARGS_2 373 -#define _INIT_CALL_PY_EXACT_ARGS_3 374 -#define _INIT_CALL_PY_EXACT_ARGS_4 375 +#define _GUARD_BOTH_FLOAT 350 +#define _GUARD_BOTH_INT 351 +#define _GUARD_BOTH_UNICODE 352 +#define _GUARD_BUILTINS_VERSION 353 +#define _GUARD_DORV_NO_DICT 354 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 355 +#define _GUARD_GLOBALS_VERSION 356 +#define _GUARD_IS_FALSE_POP 357 +#define _GUARD_IS_NONE_POP 358 +#define _GUARD_IS_NOT_NONE_POP 359 +#define _GUARD_IS_TRUE_POP 360 +#define _GUARD_KEYS_VERSION 361 +#define _GUARD_NOS_FLOAT 362 +#define _GUARD_NOS_INT 363 +#define _GUARD_NOT_EXHAUSTED_LIST 364 +#define _GUARD_NOT_EXHAUSTED_RANGE 365 +#define _GUARD_NOT_EXHAUSTED_TUPLE 366 +#define _GUARD_TOS_FLOAT 367 +#define _GUARD_TOS_INT 368 +#define _GUARD_TYPE_VERSION 369 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 370 +#define _INIT_CALL_PY_EXACT_ARGS 371 +#define _INIT_CALL_PY_EXACT_ARGS_0 372 +#define _INIT_CALL_PY_EXACT_ARGS_1 373 +#define _INIT_CALL_PY_EXACT_ARGS_2 374 +#define _INIT_CALL_PY_EXACT_ARGS_3 375 +#define _INIT_CALL_PY_EXACT_ARGS_4 376 #define _INSTRUMENTED_CALL INSTRUMENTED_CALL #define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX #define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW @@ -150,65 +151,65 @@ extern "C" { #define _INSTRUMENTED_RETURN_CONST INSTRUMENTED_RETURN_CONST #define _INSTRUMENTED_RETURN_VALUE INSTRUMENTED_RETURN_VALUE #define _INSTRUMENTED_YIELD_VALUE INSTRUMENTED_YIELD_VALUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 376 -#define _IS_NONE 377 +#define _INTERNAL_INCREMENT_OPT_COUNTER 377 +#define _IS_NONE 378 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 378 -#define _ITER_CHECK_RANGE 379 -#define _ITER_CHECK_TUPLE 380 -#define _ITER_JUMP_LIST 381 -#define _ITER_JUMP_RANGE 382 -#define _ITER_JUMP_TUPLE 383 -#define _ITER_NEXT_LIST 384 -#define _ITER_NEXT_RANGE 385 -#define _ITER_NEXT_TUPLE 386 -#define _JUMP_TO_TOP 387 +#define _ITER_CHECK_LIST 379 +#define _ITER_CHECK_RANGE 380 +#define _ITER_CHECK_TUPLE 381 +#define _ITER_JUMP_LIST 382 +#define _ITER_JUMP_RANGE 383 +#define _ITER_JUMP_TUPLE 384 +#define _ITER_NEXT_LIST 385 +#define _ITER_NEXT_RANGE 386 +#define _ITER_NEXT_TUPLE 387 +#define _JUMP_TO_TOP 388 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND #define _LOAD_ASSERTION_ERROR LOAD_ASSERTION_ERROR -#define _LOAD_ATTR 388 -#define _LOAD_ATTR_CLASS 389 -#define _LOAD_ATTR_CLASS_0 390 -#define _LOAD_ATTR_CLASS_1 391 +#define _LOAD_ATTR 389 +#define _LOAD_ATTR_CLASS 390 +#define _LOAD_ATTR_CLASS_0 391 +#define _LOAD_ATTR_CLASS_1 392 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 392 -#define _LOAD_ATTR_INSTANCE_VALUE_0 393 -#define _LOAD_ATTR_INSTANCE_VALUE_1 394 -#define _LOAD_ATTR_METHOD_LAZY_DICT 395 -#define _LOAD_ATTR_METHOD_NO_DICT 396 -#define _LOAD_ATTR_METHOD_WITH_VALUES 397 -#define _LOAD_ATTR_MODULE 398 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 399 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 400 +#define _LOAD_ATTR_INSTANCE_VALUE 393 +#define _LOAD_ATTR_INSTANCE_VALUE_0 394 +#define _LOAD_ATTR_INSTANCE_VALUE_1 395 +#define _LOAD_ATTR_METHOD_LAZY_DICT 396 +#define _LOAD_ATTR_METHOD_NO_DICT 397 +#define _LOAD_ATTR_METHOD_WITH_VALUES 398 +#define _LOAD_ATTR_MODULE 399 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 400 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 401 #define _LOAD_ATTR_PROPERTY LOAD_ATTR_PROPERTY -#define _LOAD_ATTR_SLOT 401 -#define _LOAD_ATTR_SLOT_0 402 -#define _LOAD_ATTR_SLOT_1 403 -#define _LOAD_ATTR_WITH_HINT 404 +#define _LOAD_ATTR_SLOT 402 +#define _LOAD_ATTR_SLOT_0 403 +#define _LOAD_ATTR_SLOT_1 404 +#define _LOAD_ATTR_WITH_HINT 405 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS #define _LOAD_CONST LOAD_CONST -#define _LOAD_CONST_INLINE 405 -#define _LOAD_CONST_INLINE_BORROW 406 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 407 -#define _LOAD_CONST_INLINE_WITH_NULL 408 +#define _LOAD_CONST_INLINE 406 +#define _LOAD_CONST_INLINE_BORROW 407 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 408 +#define _LOAD_CONST_INLINE_WITH_NULL 409 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 409 -#define _LOAD_FAST_0 410 -#define _LOAD_FAST_1 411 -#define _LOAD_FAST_2 412 -#define _LOAD_FAST_3 413 -#define _LOAD_FAST_4 414 -#define _LOAD_FAST_5 415 -#define _LOAD_FAST_6 416 -#define _LOAD_FAST_7 417 +#define _LOAD_FAST 410 +#define _LOAD_FAST_0 411 +#define _LOAD_FAST_1 412 +#define _LOAD_FAST_2 413 +#define _LOAD_FAST_3 414 +#define _LOAD_FAST_4 415 +#define _LOAD_FAST_5 416 +#define _LOAD_FAST_6 417 +#define _LOAD_FAST_7 418 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 418 -#define _LOAD_GLOBAL_BUILTINS 419 -#define _LOAD_GLOBAL_MODULE 420 +#define _LOAD_GLOBAL 419 +#define _LOAD_GLOBAL_BUILTINS 420 +#define _LOAD_GLOBAL_MODULE 421 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR @@ -222,50 +223,51 @@ extern "C" { #define _MATCH_SEQUENCE MATCH_SEQUENCE #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_FRAME 421 -#define _POP_JUMP_IF_FALSE 422 -#define _POP_JUMP_IF_TRUE 423 +#define _POP_FRAME 422 +#define _POP_JUMP_IF_FALSE 423 +#define _POP_JUMP_IF_TRUE 424 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 424 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 425 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 425 +#define _PUSH_FRAME 426 #define _PUSH_NULL PUSH_NULL -#define _REPLACE_WITH_TRUE 426 +#define _REPLACE_WITH_TRUE 427 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR -#define _SAVE_RETURN_OFFSET 427 -#define _SEND 428 +#define _SAVE_RETURN_OFFSET 428 +#define _SEND 429 #define _SEND_GEN SEND_GEN #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _SIDE_EXIT 429 -#define _START_EXECUTOR 430 -#define _STORE_ATTR 431 -#define _STORE_ATTR_INSTANCE_VALUE 432 -#define _STORE_ATTR_SLOT 433 +#define _SIDE_EXIT 430 +#define _START_EXECUTOR 431 +#define _STORE_ATTR 432 +#define _STORE_ATTR_INSTANCE_VALUE 433 +#define _STORE_ATTR_SLOT 434 #define _STORE_ATTR_WITH_HINT STORE_ATTR_WITH_HINT #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 434 -#define _STORE_FAST_0 435 -#define _STORE_FAST_1 436 -#define _STORE_FAST_2 437 -#define _STORE_FAST_3 438 -#define _STORE_FAST_4 439 -#define _STORE_FAST_5 440 -#define _STORE_FAST_6 441 -#define _STORE_FAST_7 442 +#define _STORE_FAST 435 +#define _STORE_FAST_0 436 +#define _STORE_FAST_1 437 +#define _STORE_FAST_2 438 +#define _STORE_FAST_3 439 +#define _STORE_FAST_4 440 +#define _STORE_FAST_5 441 +#define _STORE_FAST_6 442 +#define _STORE_FAST_7 443 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME #define _STORE_SLICE STORE_SLICE -#define _STORE_SUBSCR 443 +#define _STORE_SUBSCR 444 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TO_BOOL 444 +#define _TIER2_RESUME_CHECK 445 +#define _TO_BOOL 446 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -275,12 +277,12 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 445 +#define _UNPACK_SEQUENCE 447 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START -#define MAX_UOP_ID 445 +#define MAX_UOP_ID 447 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 776728d04bce00..59e690f3aace35 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -253,6 +253,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_DEOPT] = 0, [_SIDE_EXIT] = 0, [_ERROR_POP_N] = HAS_ARG_FLAG, + [_TIER2_RESUME_CHECK] = HAS_EXIT_FLAG, + [_EVAL_BREAKER_EXIT] = HAS_ESCAPES_FLAG, }; const uint8_t _PyUop_Replication[MAX_UOP_ID+1] = { @@ -336,6 +338,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_DYNAMIC_EXIT] = "_DYNAMIC_EXIT", [_END_SEND] = "_END_SEND", [_ERROR_POP_N] = "_ERROR_POP_N", + [_EVAL_BREAKER_EXIT] = "_EVAL_BREAKER_EXIT", [_EXIT_INIT_CHECK] = "_EXIT_INIT_CHECK", [_EXIT_TRACE] = "_EXIT_TRACE", [_FATAL_ERROR] = "_FATAL_ERROR", @@ -481,6 +484,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_STORE_SUBSCR_DICT] = "_STORE_SUBSCR_DICT", [_STORE_SUBSCR_LIST_INT] = "_STORE_SUBSCR_LIST_INT", [_SWAP] = "_SWAP", + [_TIER2_RESUME_CHECK] = "_TIER2_RESUME_CHECK", [_TO_BOOL] = "_TO_BOOL", [_TO_BOOL_BOOL] = "_TO_BOOL_BOOL", [_TO_BOOL_INT] = "_TO_BOOL_INT", @@ -968,6 +972,10 @@ int _PyUop_num_popped(int opcode, int oparg) return 0; case _ERROR_POP_N: return oparg; + case _TIER2_RESUME_CHECK: + return 0; + case _EVAL_BREAKER_EXIT: + return 0; default: return -1; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fe3d61362e6b02..f688856d6909ca 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4258,6 +4258,29 @@ dummy_func( } + /* Special version of RESUME_CHECK that (when paired with _EVAL_BREAKER_EXIT) + * is safe for tier 2. Progress is guaranteed because _EVAL_BREAKER_EXIT calls + * _Py_HandlePending which clears the eval_breaker so that _TIER2_RESUME_CHECK + * will not exit if it is immediately executed again. */ + tier2 op(_TIER2_RESUME_CHECK, (--)) { +#if defined(__EMSCRIPTEN__) + EXIT_IF(_Py_emscripten_signal_clock == 0); + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; +#endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + EXIT_IF(eval_breaker & _PY_EVAL_EVENTS_MASK); + assert(eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); + } + + tier2 op(_EVAL_BREAKER_EXIT, (--)) { + _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); + QSBR_QUIESCENT_STATE(tstate); + if (_Py_HandlePending(tstate) != 0) { + GOTO_UNWIND(); + } + EXIT_TO_TRACE(); + } + // END BYTECODES // } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 280cca1592ae18..2d9acfeea432bc 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4295,4 +4295,31 @@ break; } + case _TIER2_RESUME_CHECK: { + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + if (eval_breaker & _PY_EVAL_EVENTS_MASK) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + assert(eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); + break; + } + + case _EVAL_BREAKER_EXIT: { + _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); + QSBR_QUIESCENT_STATE(tstate); + if (_Py_HandlePending(tstate) != 0) { + GOTO_UNWIND(); + } + EXIT_TO_TRACE(); + break; + } + #undef TIER_TWO diff --git a/Python/optimizer.c b/Python/optimizer.c index 02c9b395027791..fcd7d18f2c2e22 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -690,6 +690,12 @@ translate_bytecode_to_trace( break; } + case RESUME: + /* Use a special tier 2 version of RESUME_CHECK to allow traces to + * start with RESUME_CHECK */ + ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target); + break; + default: { const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode]; @@ -967,7 +973,18 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) int32_t target = (int32_t)uop_get_target(inst); if (_PyUop_Flags[opcode] & (HAS_EXIT_FLAG | HAS_DEOPT_FLAG)) { if (target != current_jump_target) { - uint16_t exit_op = (_PyUop_Flags[opcode] & HAS_EXIT_FLAG) ? _SIDE_EXIT : _DEOPT; + uint16_t exit_op; + if (_PyUop_Flags[opcode] & HAS_EXIT_FLAG) { + if (opcode == _TIER2_RESUME_CHECK) { + exit_op = _EVAL_BREAKER_EXIT; + } + else { + exit_op = _SIDE_EXIT; + } + } + else { + exit_op = _DEOPT; + } make_exit(&buffer[next_spare], exit_op, target); current_jump_target = target; current_jump = next_spare; @@ -1075,6 +1092,7 @@ sanity_check(_PyExecutorObject *executor) CHECK( opcode == _DEOPT || opcode == _SIDE_EXIT || + opcode == _EVAL_BREAKER_EXIT || opcode == _ERROR_POP_N); if (opcode == _SIDE_EXIT) { CHECK(inst->format == UOP_FORMAT_EXIT); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index b1965687701050..4102d00171fbaf 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2145,3 +2145,11 @@ break; } + case _TIER2_RESUME_CHECK: { + break; + } + + case _EVAL_BREAKER_EXIT: { + break; + } + From c7e7bfc4ca26bf90e0d4959e303770fbfc3a3795 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 29 Apr 2024 08:58:57 +0200 Subject: [PATCH 084/217] gh-115119: Detect _decimal dependencies using pkg-config (#115406) pkg-config is supported for libmpdec 4.0.0 and newer. --- Doc/using/configure.rst | 13 ++ ...-02-13-15-31-28.gh-issue-115119.FnQzAW.rst | 2 + Modules/_decimal/_decimal.c | 14 +- configure | 159 +++++++++++++----- configure.ac | 127 +++++++------- 5 files changed, 211 insertions(+), 104 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-02-13-15-31-28.gh-issue-115119.FnQzAW.rst diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 580d31fd422c5a..e662c0dafdb8de 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -389,6 +389,17 @@ Options for third-party dependencies C compiler and linker flags for ``libffi``, used by :mod:`ctypes` module, overriding ``pkg-config``. +.. option:: LIBMPDEC_CFLAGS +.. option:: LIBMPDEC_LIBS + + C compiler and linker flags for ``libmpdec``, used by :mod:`decimal` module, + overriding ``pkg-config``. + + .. note:: + + These environment variables have no effect unless + :option:`--with-system-libmpdec` is specified. + .. option:: LIBLZMA_CFLAGS .. option:: LIBLZMA_LIBS @@ -798,6 +809,8 @@ Libraries options .. versionadded:: 3.3 + .. seealso:: :option:`LIBMPDEC_CFLAGS` and :option:`LIBMPDEC_LIBS`. + .. option:: --with-readline=readline|editline Designate a backend library for the :mod:`readline` module. diff --git a/Misc/NEWS.d/next/Build/2024-02-13-15-31-28.gh-issue-115119.FnQzAW.rst b/Misc/NEWS.d/next/Build/2024-02-13-15-31-28.gh-issue-115119.FnQzAW.rst new file mode 100644 index 00000000000000..5111d8f4db6b83 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-02-13-15-31-28.gh-issue-115119.FnQzAW.rst @@ -0,0 +1,2 @@ +:program:`configure` now uses :program:`pkg-config` to detect :mod:`decimal` +dependencies if the :option:`--with-system-libmpdec` option is given. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index fa425f4f740d31..c105367a270458 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -34,7 +34,19 @@ #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_typeobject.h" #include "complexobject.h" -#include "mpdecimal.h" + +#include + +// Reuse config from mpdecimal.h if present. +#if defined(MPD_CONFIG_64) + #ifndef CONFIG_64 + #define CONFIG_64 MPD_CONFIG_64 + #endif +#elif defined(MPD_CONFIG_32) + #ifndef CONFIG_32 + #define CONFIG_32 MPD_CONFIG_32 + #endif +#endif #include // isascii() #include diff --git a/configure b/configure index 78f86d83077eaa..571ab8c882aa68 100755 --- a/configure +++ b/configure @@ -880,6 +880,7 @@ TCLTK_CFLAGS LIBSQLITE3_LIBS LIBSQLITE3_CFLAGS LIBMPDEC_INTERNAL +LIBMPDEC_LIBS LIBMPDEC_CFLAGS MODULE__CTYPES_MALLOC_CLOSURE LIBFFI_LIBS @@ -1148,6 +1149,8 @@ LIBUUID_CFLAGS LIBUUID_LIBS LIBFFI_CFLAGS LIBFFI_LIBS +LIBMPDEC_CFLAGS +LIBMPDEC_LIBS LIBSQLITE3_CFLAGS LIBSQLITE3_LIBS TCLTK_CFLAGS @@ -1969,6 +1972,10 @@ Some influential environment variables: LIBFFI_CFLAGS C compiler flags for LIBFFI, overriding pkg-config LIBFFI_LIBS linker flags for LIBFFI, overriding pkg-config + LIBMPDEC_CFLAGS + C compiler flags for LIBMPDEC, overriding pkg-config + LIBMPDEC_LIBS + linker flags for LIBMPDEC, overriding pkg-config LIBSQLITE3_CFLAGS C compiler flags for LIBSQLITE3, overriding pkg-config LIBSQLITE3_LIBS @@ -14591,27 +14598,91 @@ printf "%s\n" "$with_system_libmpdec" >&6; } if test "x$with_system_libmpdec" = xyes then : - LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} - LIBMPDEC_LDFLAGS=${LIBMPDEC_LDFLAGS-"-lmpdec"} - LIBMPDEC_INTERNAL= - -else $as_nop +pkg_failed=no +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libmpdec" >&5 +printf %s "checking for libmpdec... " >&6; } - LIBMPDEC_CFLAGS="-I\$(srcdir)/Modules/_decimal/libmpdec" - LIBMPDEC_LDFLAGS="-lm \$(LIBMPDEC_A)" - LIBMPDEC_INTERNAL="\$(LIBMPDEC_HEADERS) \$(LIBMPDEC_A)" +if test -n "$LIBMPDEC_CFLAGS"; then + pkg_cv_LIBMPDEC_CFLAGS="$LIBMPDEC_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmpdec\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libmpdec") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBMPDEC_CFLAGS=`$PKG_CONFIG --cflags "libmpdec" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBMPDEC_LIBS"; then + pkg_cv_LIBMPDEC_LIBS="$LIBMPDEC_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmpdec\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libmpdec") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBMPDEC_LIBS=`$PKG_CONFIG --libs "libmpdec" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi - if test "x$with_pydebug" = xyes -then : - as_fn_append LIBMPDEC_CFLAGS " -DTEST_COVERAGE" -fi +if test $pkg_failed = yes; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no fi + if test $_pkg_short_errors_supported = yes; then + LIBMPDEC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmpdec" 2>&1` + else + LIBMPDEC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmpdec" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBMPDEC_PKG_ERRORS" >&5 + LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} + LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} + LIBMPDEC_INTERNAL= +elif test $pkg_failed = untried; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} + LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} + LIBMPDEC_INTERNAL= +else + LIBMPDEC_CFLAGS=$pkg_cv_LIBMPDEC_CFLAGS + LIBMPDEC_LIBS=$pkg_cv_LIBMPDEC_LIBS + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +fi +else $as_nop + LIBMPDEC_CFLAGS="-I\$(srcdir)/Modules/_decimal/libmpdec" + LIBMPDEC_LIBS="-lm \$(LIBMPDEC_A)" + LIBMPDEC_INTERNAL="\$(LIBMPDEC_HEADERS) \$(LIBMPDEC_A)" +fi +# Disable forced inlining in debug builds, see GH-94847 +if test "x$with_pydebug" = xyes +then : + as_fn_append LIBMPDEC_CFLAGS " -DTEST_COVERAGE" +fi # Check whether _decimal should use a coroutine-local or thread-local context { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-decimal-contextvar" >&5 @@ -14636,51 +14707,53 @@ fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_decimal_contextvar" >&5 printf "%s\n" "$with_decimal_contextvar" >&6; } -# Check for libmpdec machine flavor -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for decimal libmpdec machine" >&5 +if test "x$with_system_libmpdec" = xno +then : + # Check for libmpdec machine flavor + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for decimal libmpdec machine" >&5 printf %s "checking for decimal libmpdec machine... " >&6; } -case $ac_sys_system in #( + case $ac_sys_system in #( Darwin*) : libmpdec_system=Darwin ;; #( SunOS*) : libmpdec_system=sunos ;; #( *) : libmpdec_system=other - ;; + ;; esac -libmpdec_machine=unknown -if test "$libmpdec_system" = Darwin; then - # universal here means: build libmpdec with the same arch options - # the python interpreter was built with - libmpdec_machine=universal -elif test $ac_cv_sizeof_size_t -eq 8; then - if test "$ac_cv_gcc_asm_for_x64" = yes; then - libmpdec_machine=x64 - elif test "$ac_cv_type___uint128_t" = yes; then - libmpdec_machine=uint128 - else - libmpdec_machine=ansi64 - fi -elif test $ac_cv_sizeof_size_t -eq 4; then - if test "$ac_cv_gcc_asm_for_x87" = yes -a "$libmpdec_system" != sunos; then - case $CC in #( + libmpdec_machine=unknown + if test "$libmpdec_system" = Darwin; then + # universal here means: build libmpdec with the same arch options + # the python interpreter was built with + libmpdec_machine=universal + elif test $ac_cv_sizeof_size_t -eq 8; then + if test "$ac_cv_gcc_asm_for_x64" = yes; then + libmpdec_machine=x64 + elif test "$ac_cv_type___uint128_t" = yes; then + libmpdec_machine=uint128 + else + libmpdec_machine=ansi64 + fi + elif test $ac_cv_sizeof_size_t -eq 4; then + if test "$ac_cv_gcc_asm_for_x87" = yes -a "$libmpdec_system" != sunos; then + case $CC in #( *gcc*) : libmpdec_machine=ppro ;; #( *clang*) : libmpdec_machine=ppro ;; #( *) : libmpdec_machine=ansi32 - ;; + ;; esac - else - libmpdec_machine=ansi32 - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libmpdec_machine" >&5 + else + libmpdec_machine=ansi32 + fi + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libmpdec_machine" >&5 printf "%s\n" "$libmpdec_machine" >&6; } -case $libmpdec_machine in #( + case $libmpdec_machine in #( x64) : as_fn_append LIBMPDEC_CFLAGS " -DCONFIG_64=1 -DASM=1" ;; #( uint128) : @@ -14697,8 +14770,9 @@ case $libmpdec_machine in #( as_fn_append LIBMPDEC_CFLAGS " -DUNIVERSAL=1" ;; #( *) : as_fn_error $? "_decimal: unsupported architecture" "$LINENO" 5 - ;; + ;; esac +fi if test "$have_ipa_pure_const_bug" = yes; then # Some versions of gcc miscompile inline asm: @@ -14717,6 +14791,9 @@ fi + + + if test "$ac_sys_system" = "Emscripten" -a -z "$LIBSQLITE3_CFLAGS" -a -z "$LIBSQLITE3_LIBS" then : @@ -30310,7 +30387,7 @@ fi then : as_fn_append MODULE_BLOCK "MODULE__DECIMAL_CFLAGS=$LIBMPDEC_CFLAGS$as_nl" - as_fn_append MODULE_BLOCK "MODULE__DECIMAL_LDFLAGS=$LIBMPDEC_LDFLAGS$as_nl" + as_fn_append MODULE_BLOCK "MODULE__DECIMAL_LDFLAGS=$LIBMPDEC_LIBS$as_nl" fi if test "$py_cv_module__decimal" = yes; then diff --git a/configure.ac b/configure.ac index 719b8d3a9573b9..a2d6b1357efbc9 100644 --- a/configure.ac +++ b/configure.ac @@ -3961,23 +3961,21 @@ AC_ARG_WITH( [with_system_libmpdec="no"]) AC_MSG_RESULT([$with_system_libmpdec]) -AS_VAR_IF([with_system_libmpdec], [yes], [ - LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} - LIBMPDEC_LDFLAGS=${LIBMPDEC_LDFLAGS-"-lmpdec"} - LIBMPDEC_INTERNAL= -], [ - LIBMPDEC_CFLAGS="-I\$(srcdir)/Modules/_decimal/libmpdec" - LIBMPDEC_LDFLAGS="-lm \$(LIBMPDEC_A)" - LIBMPDEC_INTERNAL="\$(LIBMPDEC_HEADERS) \$(LIBMPDEC_A)" - - dnl Disable forced inlining in debug builds, see GH-94847 - AS_VAR_IF([with_pydebug], [yes], [ - AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DTEST_COVERAGE"]) - ]) -]) - -AC_SUBST([LIBMPDEC_CFLAGS]) -AC_SUBST([LIBMPDEC_INTERNAL]) +AS_VAR_IF( + [with_system_libmpdec], [yes], + [PKG_CHECK_MODULES( + [LIBMPDEC], [libmpdec], [], + [LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} + LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} + LIBMPDEC_INTERNAL=])], + [LIBMPDEC_CFLAGS="-I\$(srcdir)/Modules/_decimal/libmpdec" + LIBMPDEC_LIBS="-lm \$(LIBMPDEC_A)" + LIBMPDEC_INTERNAL="\$(LIBMPDEC_HEADERS) \$(LIBMPDEC_A)"]) + +# Disable forced inlining in debug builds, see GH-94847 +AS_VAR_IF( + [with_pydebug], [yes], + [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DTEST_COVERAGE"])]) # Check whether _decimal should use a coroutine-local or thread-local context AC_MSG_CHECKING([for --with-decimal-contextvar]) @@ -3998,50 +3996,52 @@ fi AC_MSG_RESULT([$with_decimal_contextvar]) -# Check for libmpdec machine flavor -AC_MSG_CHECKING([for decimal libmpdec machine]) -AS_CASE([$ac_sys_system], - [Darwin*], [libmpdec_system=Darwin], - [SunOS*], [libmpdec_system=sunos], - [libmpdec_system=other] -) - -libmpdec_machine=unknown -if test "$libmpdec_system" = Darwin; then - # universal here means: build libmpdec with the same arch options - # the python interpreter was built with - libmpdec_machine=universal -elif test $ac_cv_sizeof_size_t -eq 8; then - if test "$ac_cv_gcc_asm_for_x64" = yes; then - libmpdec_machine=x64 - elif test "$ac_cv_type___uint128_t" = yes; then - libmpdec_machine=uint128 - else - libmpdec_machine=ansi64 - fi -elif test $ac_cv_sizeof_size_t -eq 4; then - if test "$ac_cv_gcc_asm_for_x87" = yes -a "$libmpdec_system" != sunos; then - AS_CASE([$CC], - [*gcc*], [libmpdec_machine=ppro], - [*clang*], [libmpdec_machine=ppro], - [libmpdec_machine=ansi32] - ) - else - libmpdec_machine=ansi32 - fi -fi -AC_MSG_RESULT([$libmpdec_machine]) - -AS_CASE([$libmpdec_machine], - [x64], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DASM=1"])], - [uint128], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DANSI=1 -DHAVE_UINT128_T=1"])], - [ansi64], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DANSI=1"])], - [ppro], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1 -DASM=1 -Wno-unknown-pragmas"])], - [ansi32], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1"])], - [ansi-legacy], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1 -DLEGACY_COMPILER=1"])], - [universal], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DUNIVERSAL=1"])], - [AC_MSG_ERROR([_decimal: unsupported architecture])] -) +AS_VAR_IF( + [with_system_libmpdec], [no], + [# Check for libmpdec machine flavor + AC_MSG_CHECKING([for decimal libmpdec machine]) + AS_CASE([$ac_sys_system], + [Darwin*], [libmpdec_system=Darwin], + [SunOS*], [libmpdec_system=sunos], + [libmpdec_system=other] + ) + + libmpdec_machine=unknown + if test "$libmpdec_system" = Darwin; then + # universal here means: build libmpdec with the same arch options + # the python interpreter was built with + libmpdec_machine=universal + elif test $ac_cv_sizeof_size_t -eq 8; then + if test "$ac_cv_gcc_asm_for_x64" = yes; then + libmpdec_machine=x64 + elif test "$ac_cv_type___uint128_t" = yes; then + libmpdec_machine=uint128 + else + libmpdec_machine=ansi64 + fi + elif test $ac_cv_sizeof_size_t -eq 4; then + if test "$ac_cv_gcc_asm_for_x87" = yes -a "$libmpdec_system" != sunos; then + AS_CASE([$CC], + [*gcc*], [libmpdec_machine=ppro], + [*clang*], [libmpdec_machine=ppro], + [libmpdec_machine=ansi32] + ) + else + libmpdec_machine=ansi32 + fi + fi + AC_MSG_RESULT([$libmpdec_machine]) + + AS_CASE([$libmpdec_machine], + [x64], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DASM=1"])], + [uint128], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DANSI=1 -DHAVE_UINT128_T=1"])], + [ansi64], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DANSI=1"])], + [ppro], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1 -DASM=1 -Wno-unknown-pragmas"])], + [ansi32], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1"])], + [ansi-legacy], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1 -DLEGACY_COMPILER=1"])], + [universal], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DUNIVERSAL=1"])], + [AC_MSG_ERROR([_decimal: unsupported architecture])] + )]) if test "$have_ipa_pure_const_bug" = yes; then # Some versions of gcc miscompile inline asm: @@ -4056,6 +4056,9 @@ if test "$have_glibc_memmove_bug" = yes; then AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -U_FORTIFY_SOURCE"]) fi +AC_SUBST([LIBMPDEC_CFLAGS]) +AC_SUBST([LIBMPDEC_INTERNAL]) + dnl detect sqlite3 from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) @@ -7643,7 +7646,7 @@ PY_STDLIB_MOD([_curses_panel], [], [test "$have_panel" != "no"], [$PANEL_CFLAGS $CURSES_CFLAGS], [$PANEL_LIBS $CURSES_LIBS] ) -PY_STDLIB_MOD([_decimal], [], [], [$LIBMPDEC_CFLAGS], [$LIBMPDEC_LDFLAGS]) +PY_STDLIB_MOD([_decimal], [], [], [$LIBMPDEC_CFLAGS], [$LIBMPDEC_LIBS]) PY_STDLIB_MOD([_dbm], [test -n "$with_dbmliborder"], [test "$have_dbm" != "no"], [$DBM_CFLAGS], [$DBM_LIBS]) From 375c94c75dd9eaefaddd89a7f704a031441af286 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 29 Apr 2024 01:54:52 -0700 Subject: [PATCH 085/217] gh-107674: Lazy load line number to improve performance of tracing (GH-118127) --- ...-04-20-20-30-15.gh-issue-107674.GZPOP7.rst | 1 + Objects/frameobject.c | 16 +++-- Python/instrumentation.c | 62 ++++++++++++++----- Python/legacy_tracing.c | 1 + 4 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-20-20-30-15.gh-issue-107674.GZPOP7.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-20-20-30-15.gh-issue-107674.GZPOP7.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-20-20-30-15.gh-issue-107674.GZPOP7.rst new file mode 100644 index 00000000000000..29d16bd7dd6581 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-20-20-30-15.gh-issue-107674.GZPOP7.rst @@ -0,0 +1 @@ +Lazy load frame line number to improve performance of tracing diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 07b7ef3df46a5c..36538b1f6d53fe 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -41,12 +41,20 @@ int PyFrame_GetLineNumber(PyFrameObject *f) { assert(f != NULL); - if (f->f_lineno != 0) { - return f->f_lineno; + if (f->f_lineno == -1) { + // We should calculate it once. If we can't get the line number, + // set f->f_lineno to 0. + f->f_lineno = PyUnstable_InterpreterFrame_GetLine(f->f_frame); + if (f->f_lineno < 0) { + f->f_lineno = 0; + return -1; + } } - else { - return PyUnstable_InterpreterFrame_GetLine(f->f_frame); + + if (f->f_lineno > 0) { + return f->f_lineno; } + return PyUnstable_InterpreterFrame_GetLine(f->f_frame); } static PyObject * diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 71efeff077633d..328a3b1733d604 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -268,14 +268,15 @@ get_events(_Py_GlobalMonitors *m, int tool_id) * 8 bit value. * if line_delta == -128: * line = None # represented as -1 - * elif line_delta == -127: + * elif line_delta == -127 or line_delta == -126: * line = PyCode_Addr2Line(code, offset * sizeof(_Py_CODEUNIT)); * else: * line = first_line + (offset >> OFFSET_SHIFT) + line_delta; */ #define NO_LINE -128 -#define COMPUTED_LINE -127 +#define COMPUTED_LINE_LINENO_CHANGE -127 +#define COMPUTED_LINE -126 #define OFFSET_SHIFT 4 @@ -302,7 +303,7 @@ compute_line(PyCodeObject *code, int offset, int8_t line_delta) return -1; } - assert(line_delta == COMPUTED_LINE); + assert(line_delta == COMPUTED_LINE || line_delta == COMPUTED_LINE_LINENO_CHANGE); /* Look it up */ return PyCode_Addr2Line(code, offset * sizeof(_Py_CODEUNIT)); } @@ -1224,18 +1225,26 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, } PyInterpreterState *interp = tstate->interp; int8_t line_delta = line_data->line_delta; - int line = compute_line(code, i, line_delta); - assert(line >= 0); - assert(prev != NULL); - int prev_index = (int)(prev - _PyCode_CODE(code)); - int prev_line = _Py_Instrumentation_GetLine(code, prev_index); - if (prev_line == line) { - int prev_opcode = _PyCode_CODE(code)[prev_index].op.code; - /* RESUME and INSTRUMENTED_RESUME are needed for the operation of - * instrumentation, so must never be hidden by an INSTRUMENTED_LINE. - */ - if (prev_opcode != RESUME && prev_opcode != INSTRUMENTED_RESUME) { - goto done; + int line = 0; + + if (line_delta == COMPUTED_LINE_LINENO_CHANGE) { + // We know the line number must have changed, don't need to calculate + // the line number for now because we might not need it. + line = -1; + } else { + line = compute_line(code, i, line_delta); + assert(line >= 0); + assert(prev != NULL); + int prev_index = (int)(prev - _PyCode_CODE(code)); + int prev_line = _Py_Instrumentation_GetLine(code, prev_index); + if (prev_line == line) { + int prev_opcode = _PyCode_CODE(code)[prev_index].op.code; + /* RESUME and INSTRUMENTED_RESUME are needed for the operation of + * instrumentation, so must never be hidden by an INSTRUMENTED_LINE. + */ + if (prev_opcode != RESUME && prev_opcode != INSTRUMENTED_RESUME) { + goto done; + } } } @@ -1260,6 +1269,12 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, tstate->tracing++; /* Call c_tracefunc directly, having set the line number. */ Py_INCREF(frame_obj); + if (line == -1 && line_delta > COMPUTED_LINE) { + /* Only assign f_lineno if it's easy to calculate, otherwise + * do lazy calculation by setting the f_lineno to 0. + */ + line = compute_line(code, i, line_delta); + } frame_obj->f_lineno = line; int err = tstate->c_tracefunc(tstate->c_traceobj, frame_obj, PyTrace_LINE, Py_None); frame_obj->f_lineno = 0; @@ -1276,6 +1291,11 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, if (tools == 0) { goto done; } + + if (line == -1) { + /* Need to calculate the line number now for monitoring events */ + line = compute_line(code, i, line_delta); + } PyObject *line_obj = PyLong_FromLong(line); if (line_obj == NULL) { return -1; @@ -1477,6 +1497,13 @@ initialize_lines(PyCodeObject *code) */ if (line != current_line && line >= 0) { line_data[i].original_opcode = opcode; + if (line_data[i].line_delta == COMPUTED_LINE) { + /* Label this line as a line with a line number change + * which could help the monitoring callback to quickly + * identify the line number change. + */ + line_data[i].line_delta = COMPUTED_LINE_LINENO_CHANGE; + } } else { line_data[i].original_opcode = 0; @@ -1529,6 +1556,11 @@ initialize_lines(PyCodeObject *code) assert(target >= 0); if (line_data[target].line_delta != NO_LINE) { line_data[target].original_opcode = _Py_GetBaseOpcode(code, target); + if (line_data[target].line_delta == COMPUTED_LINE_LINENO_CHANGE) { + // If the line is a jump target, we are not sure if the line + // number changes, so we set it to COMPUTED_LINE. + line_data[target].line_delta = COMPUTED_LINE; + } } } /* Scan exception table */ diff --git a/Python/legacy_tracing.c b/Python/legacy_tracing.c index b5a17405931825..74118030925e3e 100644 --- a/Python/legacy_tracing.c +++ b/Python/legacy_tracing.c @@ -174,6 +174,7 @@ call_trace_func(_PyLegacyEventHandler *self, PyObject *arg) Py_INCREF(frame); int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, arg); + frame->f_lineno = 0; Py_DECREF(frame); if (err) { return NULL; From 030fcc47fb71719f63d5c6f8c68717250ec3d5cb Mon Sep 17 00:00:00 2001 From: Xie Yanbo Date: Mon, 29 Apr 2024 18:59:38 +0800 Subject: [PATCH 086/217] Fix typo in Doc/c-api/typeobj.rst (GH-118377) --- Doc/c-api/typeobj.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 1105b943325077..a6a2c437ea4e16 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1381,7 +1381,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) Py_VISIT(Py_TYPE(self)); It is only needed since Python 3.9. To support Python 3.8 and older, this - line must be conditionnal:: + line must be conditional:: #if PY_VERSION_HEX >= 0x03090000 Py_VISIT(Py_TYPE(self)); From 0315521c52bf162bd01d21699f3ac5e5bbe78a77 Mon Sep 17 00:00:00 2001 From: Xie Yanbo Date: Mon, 29 Apr 2024 19:01:03 +0800 Subject: [PATCH 087/217] Fix typo in Doc/howto/timerfd.rst (GH-118376) --- Doc/howto/timerfd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/timerfd.rst b/Doc/howto/timerfd.rst index 98f0294f9d082d..b5fc06ae8c6810 100644 --- a/Doc/howto/timerfd.rst +++ b/Doc/howto/timerfd.rst @@ -108,7 +108,7 @@ descriptors to wait until the file descriptor is ready for reading: # In 1.5 seconds, 1st timer, 2nd timer and 3rd timer fires at once. # # If a timer file descriptor is signaled more than once since - # the last os.read() call, os.read() returns the nubmer of signaled + # the last os.read() call, os.read() returns the number of signaled # as host order of class bytes. print(f"Signaled events={events}") for fd, event in events: From 98739c9078d57f37df7d7b9d76ad32f7a92381a3 Mon Sep 17 00:00:00 2001 From: Xie Yanbo Date: Mon, 29 Apr 2024 19:02:54 +0800 Subject: [PATCH 088/217] Fix typo in Doc/c-api/exceptions.rst (GH-118371) --- Doc/c-api/exceptions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index d5c25a68c47bd6..499bfb47cc4be5 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -104,7 +104,7 @@ Printing and clearing Similar to :c:func:`PyErr_WriteUnraisable`, but the *format* and subsequent parameters help format the warning message; they have the same meaning and values as in :c:func:`PyUnicode_FromFormat`. - ``PyErr_WriteUnraisable(obj)`` is roughtly equivalent to + ``PyErr_WriteUnraisable(obj)`` is roughly equivalent to ``PyErr_FormatUnraisable("Exception ignored in: %R", obj)``. If *format* is ``NULL``, only the traceback is printed. From 3c39f335cb7972937465560f510f53bc7772ac06 Mon Sep 17 00:00:00 2001 From: Xie Yanbo Date: Mon, 29 Apr 2024 19:15:15 +0800 Subject: [PATCH 089/217] gh-114099: Fix typos in iOS/README.rst (GH-118378) --- iOS/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/README.rst b/iOS/README.rst index a0ffa5737aae43..96cb00eb2e9bfe 100644 --- a/iOS/README.rst +++ b/iOS/README.rst @@ -224,7 +224,7 @@ content of the two "thin" ``Python.framework`` directories, plus the ``bin`` and $ lipo -create -output module.dylib path/to/x86_64/module.dylib path/to/arm64/module.dylib -* The header files will be indentical on both architectures, except for +* The header files will be identical on both architectures, except for ``pyconfig.h``. Copy all the headers from one platform (say, arm64), rename ``pyconfig.h`` to ``pyconfig-arm64.h``, and copy the ``pyconfig.h`` for the other architecture into the merged header folder as ``pyconfig-x86_64.h``. @@ -355,7 +355,7 @@ pass in command line arguments to configure test suite operation. To work around this limitation, the arguments that would normally be passed as command line arguments are configured as a static string at the start of the XCTest method ``- (void)testPython`` in ``iOSTestbedTests.m``. To pass an argument to the test -suite, add a a string to the ``argv`` defintion. These arguments will be passed +suite, add a a string to the ``argv`` definition. These arguments will be passed to the test suite as if they had been passed to ``python -m test`` at the command line. From 23d0371bb99b1df183c36883e256f82fdf6a4bea Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 29 Apr 2024 14:16:51 +0300 Subject: [PATCH 090/217] Uncomment one grammar test (#118361) --- Lib/test/test_grammar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 8501006b799262..c72f4387108ca8 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -164,7 +164,7 @@ def test_floats(self): x = 3.14 x = 314. x = 0.314 - # XXX x = 000.314 + x = 000.314 x = .314 x = 3e14 x = 3E14 From 44f57a952ea1c25699f19c6cf1fa47cd300e33aa Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 29 Apr 2024 09:29:07 -0600 Subject: [PATCH 091/217] gh-117953: Split Up _PyImport_LoadDynamicModuleWithSpec() (gh-118203) Basically, I've turned most of _PyImport_LoadDynamicModuleWithSpec() into two new functions (_PyImport_GetModInitFunc() and _PyImport_RunModInitFunc()) and moved the rest of it out into _imp_create_dynamic_impl(). There shouldn't be any changes in behavior. This change makes some future changes simpler. This is particularly relevant to potentially calling each module init function in the main interpreter first. Thus the critical part of the PR is the addition of _PyImport_RunModInitFunc(), which is strictly focused on running the init func and validating the result. A later PR will take it a step farther by capturing error information rather than raising exceptions. FWIW, this change also helps readers by clarifying a bit more about what happens when an extension/builtin module is imported. --- Include/internal/pycore_import.h | 5 +- Include/internal/pycore_importdl.h | 11 +- Include/moduleobject.h | 2 +- Python/import.c | 217 +++++++++++++++++------------ Python/importdl.c | 101 ++++++++------ 5 files changed, 192 insertions(+), 144 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 8d7f0543f8d315..b02769903a6f9b 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -29,9 +29,6 @@ extern int _PyImport_FixupBuiltin( const char *name, /* UTF-8 encoded string */ PyObject *modules ); -// We could probably drop this: -extern int _PyImport_FixupExtensionObject(PyObject*, PyObject *, - PyObject *, PyObject *); // Export for many shared extensions, like '_json' PyAPI_FUNC(PyObject*) _PyImport_GetModuleAttr(PyObject *, PyObject *); @@ -55,7 +52,7 @@ struct _import_runtime_state { Only legacy (single-phase init) extension modules are added and only if they support multiple initialization (m_size >- 0) or are imported in the main interpreter. - This is initialized lazily in _PyImport_FixupExtensionObject(). + This is initialized lazily in fix_up_extension() in import.c. Modules are added there and looked up in _imp.find_extension(). */ _Py_hashtable_t *hashtable; } extensions; diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index 8bf7c2a48f66be..55c26f2c5a475e 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -40,10 +40,17 @@ extern int _Py_ext_module_loader_info_init_from_spec( struct _Py_ext_module_loader_info *info, PyObject *spec); -extern PyObject *_PyImport_LoadDynamicModuleWithSpec( +struct _Py_ext_module_loader_result { + PyModuleDef *def; + PyObject *module; +}; +extern PyModInitFunction _PyImport_GetModInitFunc( struct _Py_ext_module_loader_info *info, - PyObject *spec, FILE *fp); +extern int _PyImport_RunModInitFunc( + PyModInitFunction p0, + struct _Py_ext_module_loader_info *info, + struct _Py_ext_module_loader_result *p_res); /* Max length of module suffix searched for -- accommodates "module.slb" */ diff --git a/Include/moduleobject.h b/Include/moduleobject.h index 42b87cc4e91012..83f8c2030dbb8f 100644 --- a/Include/moduleobject.h +++ b/Include/moduleobject.h @@ -53,7 +53,7 @@ typedef struct PyModuleDef_Base { /* A copy of the module's __dict__ after the first time it was loaded. This is only set/used for legacy modules that do not support multiple initializations. - It is set by _PyImport_FixupExtensionObject(). */ + It is set by fix_up_extension() in import.c. */ PyObject* m_copy; } PyModuleDef_Base; diff --git a/Python/import.c b/Python/import.c index 56011295f95190..f440cd52866b60 100644 --- a/Python/import.c +++ b/Python/import.c @@ -632,44 +632,45 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) (6). first time (not found in _PyRuntime.imports.extensions): A. _imp_create_dynamic_impl() -> import_find_extension() - B. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() - C. _PyImport_LoadDynamicModuleWithSpec(): load - D. _PyImport_LoadDynamicModuleWithSpec(): call - E. -> PyModule_Create() -> PyModule_Create2() + B. _imp_create_dynamic_impl() -> _PyImport_GetModInitFunc() + C. _PyImport_GetModInitFunc(): load + D. _imp_create_dynamic_impl() -> _PyImport_RunModInitFunc() + E. _PyImport_RunModInitFunc(): call + F. -> PyModule_Create() -> PyModule_Create2() -> PyModule_CreateInitialized() - F. PyModule_CreateInitialized() -> PyModule_New() - G. PyModule_CreateInitialized(): allocate mod->md_state - H. PyModule_CreateInitialized() -> PyModule_AddFunctions() - I. PyModule_CreateInitialized() -> PyModule_SetDocString() - J. PyModule_CreateInitialized(): set mod->md_def - K. : initialize the module, etc. - L. _PyImport_LoadDynamicModuleWithSpec() - -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() - M. _PyImport_LoadDynamicModuleWithSpec(): set def->m_base.m_init - N. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_FixupExtensionObject() - O. _PyImport_FixupExtensionObject() -> update_global_state_for_extension() - P. update_global_state_for_extension(): - copy __dict__ into def->m_base.m_copy - Q. update_global_state_for_extension(): - add it to _PyRuntime.imports.extensions - R. _PyImport_FixupExtensionObject() -> finish_singlephase_extension() - S. finish_singlephase_extension(): - add it to interp->imports.modules_by_index - T. finish_singlephase_extension(): add it to sys.modules - U. _imp_create_dynamic_impl(): set __file__ - - Step (P) is skipped for core modules (sys/builtins). + G. PyModule_CreateInitialized() -> PyModule_New() + H. PyModule_CreateInitialized(): allocate mod->md_state + I. PyModule_CreateInitialized() -> PyModule_AddFunctions() + J. PyModule_CreateInitialized() -> PyModule_SetDocString() + K. PyModule_CreateInitialized(): set mod->md_def + L. : initialize the module, etc. + M. _PyImport_RunModInitFunc(): set def->m_base.m_init + N. _imp_create_dynamic_impl() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + O. _imp_create_dynamic_impl(): set __file__ + P. _imp_create_dynamic_impl() -> update_global_state_for_extension() + Q. update_global_state_for_extension(): + copy __dict__ into def->m_base.m_copy + R. update_global_state_for_extension(): + add it to _PyRuntime.imports.extensions + S. _imp_create_dynamic_impl() -> finish_singlephase_extension() + T. finish_singlephase_extension(): + add it to interp->imports.modules_by_index + U. finish_singlephase_extension(): add it to sys.modules + + Step (Q) is skipped for core modules (sys/builtins). (6). subsequent times (found in _PyRuntime.imports.extensions): A. _imp_create_dynamic_impl() -> import_find_extension() - B. import_find_extension() -> import_add_module() - C. if name in sys.modules: use that module - D. else: + B. import_find_extension() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + C. import_find_extension() -> import_add_module() + D. if name in sys.modules: use that module + E. else: 1. import_add_module() -> PyModule_NewObject() 2. import_add_module(): set it on sys.modules - E. import_find_extension(): copy the "m_copy" dict into __dict__ - F. _imp_create_dynamic_impl() - -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + F. import_find_extension(): copy the "m_copy" dict into __dict__ + G. import_find_extension(): add to modules_by_index (10). (every time): A. noop @@ -678,19 +679,22 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) ...for single-phase init modules, where m_size >= 0: (6). not main interpreter and never loaded there - every time (not found in _PyRuntime.imports.extensions): - A-N. (same as for m_size == -1) - O-Q. (skipped) - R-U. (same as for m_size == -1) + A-O. (same as for m_size == -1) + P-R. (skipped) + S-U. (same as for m_size == -1) (6). main interpreter - first time (not found in _PyRuntime.imports.extensions): - A-O. (same as for m_size == -1) - P. (skipped) - Q-U. (same as for m_size == -1) + A-Q. (same as for m_size == -1) + R. (skipped) + S-U. (same as for m_size == -1) - (6). previously loaded in main interpreter (found in _PyRuntime.imports.extensions): + (6). subsequent times (found in _PyRuntime.imports.extensions): A. _imp_create_dynamic_impl() -> import_find_extension() - B. import_find_extension(): call def->m_base.m_init - C. import_find_extension(): add the module to sys.modules + B. import_find_extension() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + C. import_find_extension(): call def->m_base.m_init (see above) + D. import_find_extension(): add the module to sys.modules + E. import_find_extension(): add to modules_by_index (10). every time: A. noop @@ -1270,7 +1274,7 @@ finish_singlephase_extension(PyThreadState *tstate, PyObject *name, PyObject *modules) { assert(mod != NULL && PyModule_Check(mod)); - assert(def == PyModule_GetDef(mod)); + assert(def == _PyModule_GetDef(mod)); if (_modules_by_index_set(tstate->interp, def, mod) < 0) { return -1; @@ -1285,47 +1289,6 @@ finish_singlephase_extension(PyThreadState *tstate, return 0; } -int -_PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, - PyObject *filename, PyObject *modules) -{ - PyThreadState *tstate = _PyThreadState_GET(); - - if (mod == NULL || !PyModule_Check(mod)) { - PyErr_BadInternalCall(); - return -1; - } - PyModuleDef *def = PyModule_GetDef(mod); - if (def == NULL) { - PyErr_BadInternalCall(); - return -1; - } - - /* Only single-phase init extension modules can reach here. */ - assert(is_singlephase(def)); - assert(!is_core_module(tstate->interp, name, filename)); - assert(!is_core_module(tstate->interp, name, name)); - - struct singlephase_global_update singlephase = {0}; - // gh-88216: Extensions and def->m_base.m_copy can be updated - // when the extension module doesn't support sub-interpreters. - if (def->m_size == -1) { - singlephase.m_dict = PyModule_GetDict(mod); - assert(singlephase.m_dict != NULL); - } - if (update_global_state_for_extension( - tstate, filename, name, def, &singlephase) < 0) - { - return -1; - } - - if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) { - return -1; - } - - return 0; -} - static PyObject * import_find_extension(PyThreadState *tstate, @@ -1514,7 +1477,12 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) } PyObject *mod = import_find_extension(tstate, &info); - if (mod || _PyErr_Occurred(tstate)) { + if (mod != NULL) { + assert(!_PyErr_Occurred(tstate)); + assert(is_singlephase(_PyModule_GetDef(mod))); + goto finally; + } + else if (_PyErr_Occurred(tstate)) { goto finally; } @@ -3900,19 +3868,24 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) /*[clinic end generated code: output=83249b827a4fde77 input=c31b954f4cf4e09d]*/ { PyObject *mod = NULL; - FILE *fp; + PyModuleDef *def = NULL; + PyThreadState *tstate = _PyThreadState_GET(); struct _Py_ext_module_loader_info info; if (_Py_ext_module_loader_info_init_from_spec(&info, spec) < 0) { return NULL; } - PyThreadState *tstate = _PyThreadState_GET(); mod = import_find_extension(tstate, &info); - if (mod != NULL || _PyErr_Occurred(tstate)) { - assert(mod == NULL || !_PyErr_Occurred(tstate)); + if (mod != NULL) { + assert(!_PyErr_Occurred(tstate)); + assert(is_singlephase(_PyModule_GetDef(mod))); goto finally; } + else if (_PyErr_Occurred(tstate)) { + goto finally; + } + /* Otherwise it must be multi-phase init or the first time it's loaded. */ if (PySys_Audit("import", "OOOOO", info.name, info.filename, Py_None, Py_None, Py_None) < 0) @@ -3920,11 +3893,10 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) goto finally; } - /* Is multi-phase init or this is the first time being loaded. */ - /* We would move this (and the fclose() below) into * _PyImport_GetModInitFunc(), but it isn't clear if the intervening * code relies on fp still being open. */ + FILE *fp; if (file != NULL) { fp = _Py_fopen_obj(info.filename, "r"); if (fp == NULL) { @@ -3935,7 +3907,70 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) fp = NULL; } - mod = _PyImport_LoadDynamicModuleWithSpec(&info, spec, fp); + PyModInitFunction p0 = _PyImport_GetModInitFunc(&info, fp); + if (p0 == NULL) { + goto finally; + } + + struct _Py_ext_module_loader_result res; + if (_PyImport_RunModInitFunc(p0, &info, &res) < 0) { + assert(PyErr_Occurred()); + goto finally; + } + + mod = res.module; + res.module = NULL; + def = res.def; + assert(def != NULL); + + if (mod == NULL) { + //assert(!is_singlephase(def)); + mod = PyModule_FromDefAndSpec(def, spec); + if (mod == NULL) { + goto finally; + } + } + else { + assert(is_singlephase(def)); + assert(!is_core_module(tstate->interp, info.name, info.filename)); + assert(!is_core_module(tstate->interp, info.name, info.name)); + + const char *name_buf = PyBytes_AS_STRING(info.name_encoded); + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + Py_CLEAR(mod); + goto finally; + } + + /* Remember pointer to module init function. */ + res.def->m_base.m_init = p0; + + /* Remember the filename as the __file__ attribute */ + if (PyModule_AddObjectRef(mod, "__file__", info.filename) < 0) { + PyErr_Clear(); /* Not important enough to report */ + } + + struct singlephase_global_update singlephase = {0}; + // gh-88216: Extensions and def->m_base.m_copy can be updated + // when the extension module doesn't support sub-interpreters. + if (def->m_size == -1) { + singlephase.m_dict = PyModule_GetDict(mod); + assert(singlephase.m_dict != NULL); + } + if (update_global_state_for_extension( + tstate, info.filename, info.name, def, &singlephase) < 0) + { + Py_CLEAR(mod); + goto finally; + } + + PyObject *modules = get_modules_dict(tstate, true); + if (finish_singlephase_extension( + tstate, mod, def, info.name, modules) < 0) + { + Py_CLEAR(mod); + goto finally; + } + } // XXX Shouldn't this happen in the error cases too. if (fp) { diff --git a/Python/importdl.c b/Python/importdl.c index f2ad95fbbb507d..cc70a6daf5d0f1 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -179,17 +179,12 @@ _Py_ext_module_loader_info_init_from_spec( } -PyObject * -_PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info, - PyObject *spec, FILE *fp) +PyModInitFunction +_PyImport_GetModInitFunc(struct _Py_ext_module_loader_info *info, + FILE *fp) { - PyObject *m = NULL; const char *name_buf = PyBytes_AS_STRING(info->name_encoded); - const char *oldcontext; dl_funcptr exportfunc; - PyModInitFunction p0; - PyModuleDef *def; - #ifdef MS_WINDOWS exportfunc = _PyImport_FindSharedFuncptrWindows( info->hook_prefix, name_buf, info->filename, fp); @@ -213,16 +208,29 @@ _PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info, Py_DECREF(msg); } } - goto error; + return NULL; } - p0 = (PyModInitFunction)exportfunc; + return (PyModInitFunction)exportfunc; +} + +int +_PyImport_RunModInitFunc(PyModInitFunction p0, + struct _Py_ext_module_loader_info *info, + struct _Py_ext_module_loader_result *p_res) +{ + struct _Py_ext_module_loader_result res = {0}; + const char *name_buf = PyBytes_AS_STRING(info->name_encoded); + + /* Call the module init function. */ /* Package context is needed for single-phase init */ - oldcontext = _PyImport_SwapPackageContext(info->newcontext); - m = p0(); + const char *oldcontext = _PyImport_SwapPackageContext(info->newcontext); + PyObject *m = p0(); _PyImport_SwapPackageContext(oldcontext); + /* Validate the result (and populate "res". */ + if (m == NULL) { if (!PyErr_Occurred()) { PyErr_Format( @@ -236,9 +244,13 @@ _PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info, PyExc_SystemError, "initialization of %s raised unreported exception", name_buf); + /* We would probably be correct to decref m here, + * but we weren't doing so before, + * so we stick with doing nothing. */ m = NULL; goto error; } + if (Py_IS_TYPE(m, NULL)) { /* This can happen when a PyModuleDef is returned without calling * PyModuleDef_Init on it @@ -246,55 +258,52 @@ _PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info, PyErr_Format(PyExc_SystemError, "init function of %s returned uninitialized object", name_buf); + /* Likewise, decref'ing here makes sense. However, the original + * code has a note about "prevent segfault in DECREF", + * so we play it safe and leave it alone. */ m = NULL; /* prevent segfault in DECREF */ goto error; } - if (PyObject_TypeCheck(m, &PyModuleDef_Type)) { - return PyModule_FromDefAndSpec((PyModuleDef*)m, spec); - } - /* Fall back to single-phase init mechanism */ - - if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { - goto error; + if (PyObject_TypeCheck(m, &PyModuleDef_Type)) { + /* multi-phase init */ + res.def = (PyModuleDef *)m; + /* Run PyModule_FromDefAndSpec() to finish loading the module. */ } - - if (info->hook_prefix == nonascii_prefix) { - /* don't allow legacy init for non-ASCII module names */ + else if (info->hook_prefix == nonascii_prefix) { + /* It should have been multi-phase init? */ + /* Don't allow legacy init for non-ASCII module names. */ PyErr_Format( PyExc_SystemError, "initialization of %s did not return PyModuleDef", name_buf); - goto error; - } - - /* Remember pointer to module init function. */ - def = PyModule_GetDef(m); - if (def == NULL) { - PyErr_Format(PyExc_SystemError, - "initialization of %s did not return an extension " - "module", name_buf); - goto error; - } - def->m_base.m_init = p0; - - /* Remember the filename as the __file__ attribute */ - if (PyModule_AddObjectRef(m, "__file__", info->filename) < 0) { - PyErr_Clear(); /* Not important enough to report */ + Py_DECREF(m); + return -1; } + else { + /* single-phase init (legacy) */ + res.module = m; - PyObject *modules = PyImport_GetModuleDict(); - if (_PyImport_FixupExtensionObject( - m, info->name, info->filename, modules) < 0) - { - goto error; + res.def = PyModule_GetDef(m); + if (res.def == NULL) { + PyErr_Clear(); + PyErr_Format(PyExc_SystemError, + "initialization of %s did not return an extension " + "module", name_buf); + goto error; + } } - return m; + assert(!PyErr_Occurred()); + *p_res = res; + return 0; error: - Py_XDECREF(m); - return NULL; + assert(PyErr_Occurred()); + Py_CLEAR(res.module); + res.def = NULL; + *p_res = res; + return -1; } #endif /* HAVE_DYNAMIC_LOADING */ From 51c70de998ead35674bf4b2b236e9ce8e89d17b4 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Mon, 29 Apr 2024 18:50:11 +0300 Subject: [PATCH 092/217] gh-118351: Adapt support.TEST_MODULES_ENABLED for builds without the config variable (GH-118354) --- Lib/test/support/__init__.py | 5 +++-- Lib/test/test_capi/test_run.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index ea4945466cac82..70d2610aa495c7 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1181,8 +1181,9 @@ def requires_limited_api(test): return test -TEST_MODULES_ENABLED = sysconfig.get_config_var('TEST_MODULES') == 'yes' - +# Windows build doesn't support --disable-test-modules feature, so there's no +# 'TEST_MODULES' var in config +TEST_MODULES_ENABLED = (sysconfig.get_config_var('TEST_MODULES') or 'yes') == 'yes' def requires_specialization(test): return unittest.skipUnless( diff --git a/Lib/test/test_capi/test_run.py b/Lib/test/test_capi/test_run.py index bc0ca9dbb7f31e..3a390273ec21ad 100644 --- a/Lib/test/test_capi/test_run.py +++ b/Lib/test/test_capi/test_run.py @@ -2,7 +2,7 @@ import unittest from collections import UserDict from test.support import import_helper -from test.support.os_helper import unlink, TESTFN, TESTFN_UNDECODABLE +from test.support.os_helper import unlink, TESTFN, TESTFN_ASCII, TESTFN_UNDECODABLE NULL = None _testcapi = import_helper.import_module('_testcapi') @@ -35,6 +35,7 @@ class CAPITest(unittest.TestCase): def test_run_stringflags(self): # Test PyRun_StringFlags(). + # XXX: fopen() uses different path encoding than Python on Windows. def run(s, *args): return _testcapi.run_stringflags(s, Py_file_input, *args) source = b'a\n' @@ -63,7 +64,7 @@ def run(s, *args): def test_run_fileexflags(self): # Test PyRun_FileExFlags(). - filename = os.fsencode(TESTFN) + filename = os.fsencode(TESTFN if os.name != 'nt' else TESTFN_ASCII) with open(filename, 'wb') as fp: fp.write(b'a\n') self.addCleanup(unlink, filename) @@ -89,6 +90,7 @@ def run(*args): # CRASHES run(UserDict(), dict(a=1)) @unittest.skipUnless(TESTFN_UNDECODABLE, 'only works if there are undecodable paths') + @unittest.skipIf(os.name == 'nt', 'does not work on Windows') def test_run_fileexflags_with_undecodable_filename(self): run = _testcapi.run_fileexflags try: From 444ac0b7a64ff6b6caba9c2731bd33151ce18ad1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 29 Apr 2024 19:30:48 +0300 Subject: [PATCH 093/217] gh-118285: Fix signatures of operator.{attrgetter,itemgetter,methodcaller} instances (GH-118316) * Allow to specify the signature of custom callable instances of extension type by the __text_signature__ attribute. * Specify signatures of operator.attrgetter, operator.itemgetter, and operator.methodcaller instances. --- Lib/inspect.py | 7 ++++++ Lib/operator.py | 10 ++++---- Lib/test/test_inspect/test_inspect.py | 22 ++++++++++++++++++ Lib/test/test_operator.py | 23 +++++++++++++++++++ ...-04-26-14-53-28.gh-issue-118285.A0_pte.rst | 4 ++++ Modules/_operator.c | 15 ++++++++++++ 6 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-26-14-53-28.gh-issue-118285.A0_pte.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index 3c346b27b1f06d..1f4216f0389d28 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2692,6 +2692,13 @@ def _signature_from_callable(obj, *, # An object with __call__ call = getattr_static(type(obj), '__call__', None) if call is not None: + try: + text_sig = obj.__text_signature__ + except AttributeError: + pass + else: + if text_sig: + return _signature_fromstr(sigcls, obj, text_sig) call = _descriptor_get(call, obj) return _get_signature_of(call) diff --git a/Lib/operator.py b/Lib/operator.py index 30116c1189a499..02ccdaa13ddb31 100644 --- a/Lib/operator.py +++ b/Lib/operator.py @@ -239,7 +239,7 @@ class attrgetter: """ __slots__ = ('_attrs', '_call') - def __init__(self, attr, *attrs): + def __init__(self, attr, /, *attrs): if not attrs: if not isinstance(attr, str): raise TypeError('attribute name must be a string') @@ -257,7 +257,7 @@ def func(obj): return tuple(getter(obj) for getter in getters) self._call = func - def __call__(self, obj): + def __call__(self, obj, /): return self._call(obj) def __repr__(self): @@ -276,7 +276,7 @@ class itemgetter: """ __slots__ = ('_items', '_call') - def __init__(self, item, *items): + def __init__(self, item, /, *items): if not items: self._items = (item,) def func(obj): @@ -288,7 +288,7 @@ def func(obj): return tuple(obj[i] for i in items) self._call = func - def __call__(self, obj): + def __call__(self, obj, /): return self._call(obj) def __repr__(self): @@ -315,7 +315,7 @@ def __init__(self, name, /, *args, **kwargs): self._args = args self._kwargs = kwargs - def __call__(self, obj): + def __call__(self, obj, /): return getattr(obj, self._name)(*self._args, **self._kwargs) def __repr__(self): diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 169d1edb706fc3..6b577090bdff68 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4090,6 +4090,28 @@ class C: ((('a', ..., ..., "positional_or_keyword"),), ...)) + def test_signature_on_callable_objects_with_text_signature_attr(self): + class C: + __text_signature__ = '(a, /, b, c=True)' + def __call__(self, *args, **kwargs): + pass + + self.assertEqual(self.signature(C), ((), ...)) + self.assertEqual(self.signature(C()), + ((('a', ..., ..., "positional_only"), + ('b', ..., ..., "positional_or_keyword"), + ('c', True, ..., "positional_or_keyword"), + ), + ...)) + + c = C() + c.__text_signature__ = '(x, y)' + self.assertEqual(self.signature(c), + ((('x', ..., ..., "positional_or_keyword"), + ('y', ..., ..., "positional_or_keyword"), + ), + ...)) + def test_signature_on_wrapper(self): class Wrapper: def __call__(self, b): diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index 0d34d671563d19..f8eac8dc002636 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -1,4 +1,5 @@ import unittest +import inspect import pickle import sys from decimal import Decimal @@ -602,6 +603,28 @@ def test_dunder_is_original(self): if dunder: self.assertIs(dunder, orig) + def test_attrgetter_signature(self): + operator = self.module + sig = inspect.signature(operator.attrgetter) + self.assertEqual(str(sig), '(attr, /, *attrs)') + sig = inspect.signature(operator.attrgetter('x', 'z', 'y')) + self.assertEqual(str(sig), '(obj, /)') + + def test_itemgetter_signature(self): + operator = self.module + sig = inspect.signature(operator.itemgetter) + self.assertEqual(str(sig), '(item, /, *items)') + sig = inspect.signature(operator.itemgetter(2, 3, 5)) + self.assertEqual(str(sig), '(obj, /)') + + def test_methodcaller_signature(self): + operator = self.module + sig = inspect.signature(operator.methodcaller) + self.assertEqual(str(sig), '(name, /, *args, **kwargs)') + sig = inspect.signature(operator.methodcaller('foo', 2, y=3)) + self.assertEqual(str(sig), '(obj, /)') + + class PyOperatorTestCase(OperatorTestCase, unittest.TestCase): module = py_operator diff --git a/Misc/NEWS.d/next/Library/2024-04-26-14-53-28.gh-issue-118285.A0_pte.rst b/Misc/NEWS.d/next/Library/2024-04-26-14-53-28.gh-issue-118285.A0_pte.rst new file mode 100644 index 00000000000000..6e8f8d368ca5a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-26-14-53-28.gh-issue-118285.A0_pte.rst @@ -0,0 +1,4 @@ +Allow to specify the signature of custom callable instances of extension +type by the :attr:`__text_signature__` attribute. Specify signatures of +:class:`operator.attrgetter`, :class:`operator.itemgetter`, and +:class:`operator.methodcaller` instances. diff --git a/Modules/_operator.c b/Modules/_operator.c index 1f6496d381adac..306d4508f52a68 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -966,6 +966,18 @@ static struct PyMethodDef operator_methods[] = { }; + +static PyObject * +text_signature(PyObject *self, void *Py_UNUSED(ignored)) +{ + return PyUnicode_FromString("(obj, /)"); +} + +static PyGetSetDef common_getset[] = { + {"__text_signature__", text_signature, (setter)NULL}, + {NULL} +}; + /* itemgetter object **********************************************************/ typedef struct { @@ -1171,6 +1183,7 @@ static PyType_Slot itemgetter_type_slots[] = { {Py_tp_clear, itemgetter_clear}, {Py_tp_methods, itemgetter_methods}, {Py_tp_members, itemgetter_members}, + {Py_tp_getset, common_getset}, {Py_tp_new, itemgetter_new}, {Py_tp_getattro, PyObject_GenericGetAttr}, {Py_tp_repr, itemgetter_repr}, @@ -1528,6 +1541,7 @@ static PyType_Slot attrgetter_type_slots[] = { {Py_tp_clear, attrgetter_clear}, {Py_tp_methods, attrgetter_methods}, {Py_tp_members, attrgetter_members}, + {Py_tp_getset, common_getset}, {Py_tp_new, attrgetter_new}, {Py_tp_getattro, PyObject_GenericGetAttr}, {Py_tp_repr, attrgetter_repr}, @@ -1863,6 +1877,7 @@ static PyType_Slot methodcaller_type_slots[] = { {Py_tp_clear, methodcaller_clear}, {Py_tp_methods, methodcaller_methods}, {Py_tp_members, methodcaller_members}, + {Py_tp_getset, common_getset}, {Py_tp_new, methodcaller_new}, {Py_tp_getattro, PyObject_GenericGetAttr}, {Py_tp_repr, methodcaller_repr}, From 43fa76638fc75958b592096b6830c15f0afa1a73 Mon Sep 17 00:00:00 2001 From: mpage Date: Mon, 29 Apr 2024 09:56:51 -0700 Subject: [PATCH 094/217] gh-118331: Don't raise an error if tuple allocation fails when clearing weakrefs (#118338) It's not safe to raise an exception in `PyObject_ClearWeakRefs()` if one is not already set, since it may be called by `_Py_Dealloc()`, which requires that the active exception does not change. Additionally, make sure we clear the weakrefs even when tuple allocation fails. --- Lib/test/test_weakref.py | 26 ++++++++++++++++++++++++++ Objects/weakrefobject.c | 4 +++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 499ba77fd19542..a2f5b9b2902d36 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -10,6 +10,7 @@ import threading import time import random +import textwrap from test import support from test.support import script_helper, ALWAYS_EQ @@ -1009,6 +1010,31 @@ def __del__(self): pass del x support.gc_collect() + @support.cpython_only + def test_no_memory_when_clearing(self): + # gh-118331: Make sure we do not raise an exception from the destructor + # when clearing weakrefs if allocating the intermediate tuple fails. + code = textwrap.dedent(""" + import _testcapi + import weakref + + class TestObj: + pass + + def callback(obj): + pass + + obj = TestObj() + # The choice of 50 is arbitrary, but must be large enough to ensure + # the allocation won't be serviced by the free list. + wrs = [weakref.ref(obj, callback) for _ in range(50)] + _testcapi.set_nomemory(0) + del obj + """).strip() + res, _ = script_helper.run_python_until_end("-c", code) + stderr = res.err.decode("ascii", "backslashreplace") + self.assertNotRegex(stderr, "_Py_Dealloc: Deallocator of type 'TestObj'") + class SubclassableWeakrefTestCase(TestBase): diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 206107e8505dc7..93c5fe3aacecfd 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -1016,7 +1016,9 @@ PyObject_ClearWeakRefs(PyObject *object) PyObject *exc = PyErr_GetRaisedException(); PyObject *tuple = PyTuple_New(num_weakrefs * 2); if (tuple == NULL) { - _PyErr_ChainExceptions1(exc); + _PyWeakref_ClearWeakRefsExceptCallbacks(object); + PyErr_WriteUnraisable(NULL); + PyErr_SetRaisedException(exc); return; } From 8d4b756fd31d4d91b55105b1241561e92cc571a3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 29 Apr 2024 20:40:50 +0300 Subject: [PATCH 095/217] Docs: Upgrade to Sphinx 7.3 (#118397) --- .github/workflows/reusable-docs.yml | 2 +- Doc/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 9e26d7847d2bd3..fd519d60f324ab 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -55,7 +55,7 @@ jobs: run: | set -Eeuo pipefail # Build docs with the '-n' (nit-picky) option; write warnings to file - make -C Doc/ PYTHON=../python SPHINXOPTS="-q -n -W --keep-going -w sphinx-warnings.txt" html + make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet --nitpicky --fail-on-warning --keep-going --warning-file sphinx-warnings.txt" html - name: 'Check warnings' if: github.event_name == 'pull_request' run: | diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 118e6c322b4be2..15675ab45fea71 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -6,7 +6,7 @@ # Sphinx version is pinned so that new versions that introduce new warnings # won't suddenly cause build failures. Updating the version is fine as long # as no warnings are raised by doing so. -sphinx~=7.2.0 +sphinx~=7.3.0 blurb From 7ccacb220d99662b626c8bc63b00a27eaf604f0c Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Mon, 29 Apr 2024 14:36:02 -0400 Subject: [PATCH 096/217] gh-117783: Immortalize objects that use deferred reference counting (#118112) Deferred reference counting is not fully implemented yet. As a temporary measure, we immortalize objects that would use deferred reference counting to avoid multi-threaded scaling bottlenecks. This is only performed in the free-threaded build once the first non-main thread is started. Additionally, some tests, including refleak tests, suppress this behavior. --- Include/internal/pycore_gc.h | 17 ++++++++++++++++ Lib/concurrent/futures/process.py | 5 +++-- Lib/test/libregrtest/main.py | 8 ++++++-- Lib/test/libregrtest/single.py | 5 ++++- Lib/test/support/__init__.py | 19 ++++++++++++++++++ Lib/test/test_capi/test_watchers.py | 6 +++++- Lib/test/test_code.py | 6 +++++- Lib/test/test_functools.py | 1 + Lib/test/test_weakref.py | 5 ++++- Modules/_testinternalcapi.c | 22 +++++++++++++++++++++ Objects/object.c | 7 +++++++ Python/gc_free_threading.c | 30 +++++++++++++++++++++++++++++ Python/pystate.c | 11 +++++++++++ 13 files changed, 134 insertions(+), 8 deletions(-) diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 9e465fdd86279f..281094df786735 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -312,6 +312,18 @@ struct _gc_runtime_state { collections, and are awaiting to undergo a full collection for the first time. */ Py_ssize_t long_lived_pending; + + /* gh-117783: Deferred reference counting is not fully implemented yet, so + as a temporary measure we treat objects using deferred referenence + counting as immortal. */ + struct { + /* Immortalize objects instead of marking them as using deferred + reference counting. */ + int enabled; + + /* Set enabled=1 when the first background thread is created. */ + int enable_on_thread_created; + } immortalize; #endif }; @@ -343,6 +355,11 @@ extern void _PyGC_ClearAllFreeLists(PyInterpreterState *interp); extern void _Py_ScheduleGC(PyThreadState *tstate); extern void _Py_RunGC(PyThreadState *tstate); +#ifdef Py_GIL_DISABLED +// gh-117783: Immortalize objects that use deferred reference counting +extern void _PyGC_ImmortalizeDeferredObjects(PyInterpreterState *interp); +#endif + #ifdef __cplusplus } #endif diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index ca843e11eeb83d..bb4892ebdfedf5 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -296,8 +296,9 @@ def __init__(self, executor): # if there is no pending work item. def weakref_cb(_, thread_wakeup=self.thread_wakeup, - shutdown_lock=self.shutdown_lock): - mp.util.debug('Executor collected: triggering callback for' + shutdown_lock=self.shutdown_lock, + mp_util_debug=mp.util.debug): + mp_util_debug('Executor collected: triggering callback for' ' QueueManager wakeup') with shutdown_lock: thread_wakeup.wakeup() diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 3c9d9620053355..9e7a7d60880091 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -7,7 +7,8 @@ import time import trace -from test.support import os_helper, MS_WINDOWS, flush_std_streams +from test.support import (os_helper, MS_WINDOWS, flush_std_streams, + suppress_immortalization) from .cmdline import _parse_args, Namespace from .findtests import findtests, split_test_packages, list_cases @@ -526,7 +527,10 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: if self.num_workers: self._run_tests_mp(runtests, self.num_workers) else: - self.run_tests_sequentially(runtests) + # gh-117783: don't immortalize deferred objects when tracking + # refleaks. Only releveant for the free-threaded build. + with suppress_immortalization(runtests.hunt_refleak): + self.run_tests_sequentially(runtests) coverage = self.results.get_coverage_results() self.display_result(runtests) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 235029d8620ff5..fc2f2716ad4ce0 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -303,7 +303,10 @@ def run_single_test(test_name: TestName, runtests: RunTests) -> TestResult: result = TestResult(test_name) pgo = runtests.pgo try: - _runtest(result, runtests) + # gh-117783: don't immortalize deferred objects when tracking + # refleaks. Only releveant for the free-threaded build. + with support.suppress_immortalization(runtests.hunt_refleak): + _runtest(result, runtests) except: if not pgo: msg = traceback.format_exc() diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 70d2610aa495c7..15f654302b1793 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -516,6 +516,25 @@ def has_no_debug_ranges(): def requires_debug_ranges(reason='requires co_positions / debug_ranges'): return unittest.skipIf(has_no_debug_ranges(), reason) +@contextlib.contextmanager +def suppress_immortalization(suppress=True): + """Suppress immortalization of deferred objects.""" + try: + import _testinternalcapi + except ImportError: + yield + return + + if not suppress: + yield + return + + old_values = _testinternalcapi.set_immortalize_deferred(False) + try: + yield + finally: + _testinternalcapi.set_immortalize_deferred(*old_values) + MS_WINDOWS = (sys.platform == 'win32') # Is not actually used in tests, but is kept for compatibility. diff --git a/Lib/test/test_capi/test_watchers.py b/Lib/test/test_capi/test_watchers.py index 8e84d0077c7573..90665a7561b316 100644 --- a/Lib/test/test_capi/test_watchers.py +++ b/Lib/test/test_capi/test_watchers.py @@ -1,7 +1,9 @@ import unittest from contextlib import contextmanager, ExitStack -from test.support import catch_unraisable_exception, import_helper, gc_collect +from test.support import ( + catch_unraisable_exception, import_helper, + gc_collect, suppress_immortalization) # Skip this test if the _testcapi module isn't available. @@ -382,6 +384,7 @@ def assert_event_counts(self, exp_created_0, exp_destroyed_0, self.assertEqual( exp_destroyed_1, _testcapi.get_code_watcher_num_destroyed_events(1)) + @suppress_immortalization() def test_code_object_events_dispatched(self): # verify that all counts are zero before any watchers are registered self.assert_event_counts(0, 0, 0, 0) @@ -428,6 +431,7 @@ def test_error(self): self.assertIsNone(cm.unraisable.object) self.assertEqual(str(cm.unraisable.exc_value), "boom!") + @suppress_immortalization() def test_dealloc_error(self): co = _testcapi.code_newempty("test_watchers", "dummy0", 0) with self.code_watcher(2): diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index fe8c672e71a7b5..aa793f56225393 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -141,7 +141,8 @@ ctypes = None from test.support import (cpython_only, check_impl_detail, requires_debug_ranges, - gc_collect, Py_GIL_DISABLED) + gc_collect, Py_GIL_DISABLED, + suppress_immortalization) from test.support.script_helper import assert_python_ok from test.support import threading_helper, import_helper from test.support.bytecode_helper import instructions_with_positions @@ -577,6 +578,7 @@ def test_interned_string_with_null(self): class CodeWeakRefTest(unittest.TestCase): + @suppress_immortalization() def test_basic(self): # Create a code object in a clean environment so that we know we have # the only reference to it left. @@ -827,6 +829,7 @@ def test_bad_index(self): self.assertEqual(GetExtra(f.__code__, FREE_INDEX+100, ctypes.c_voidp(100)), 0) + @suppress_immortalization() def test_free_called(self): # Verify that the provided free function gets invoked # when the code object is cleaned up. @@ -854,6 +857,7 @@ def test_get_set(self): del f @threading_helper.requires_working_threading() + @suppress_immortalization() def test_free_different_thread(self): # Freeing a code object on a different thread then # where the co_extra was set should be safe. diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index ec5f6af5e17842..bb4c7cc8701fb4 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1833,6 +1833,7 @@ def f(): return 1 self.assertEqual(f.cache_parameters(), {'maxsize': 1000, "typed": True}) + @support.suppress_immortalization() def test_lru_cache_weakrefable(self): @self.module.lru_cache def test_function(x): diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index a2f5b9b2902d36..df90647baee31e 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -13,7 +13,7 @@ import textwrap from test import support -from test.support import script_helper, ALWAYS_EQ +from test.support import script_helper, ALWAYS_EQ, suppress_immortalization from test.support import gc_collect from test.support import import_helper from test.support import threading_helper @@ -651,6 +651,7 @@ class C(object): # deallocation of c2. del c2 + @suppress_immortalization() def test_callback_in_cycle(self): import gc @@ -743,6 +744,7 @@ class D: del c1, c2, C, D gc.collect() + @suppress_immortalization() def test_callback_in_cycle_resurrection(self): import gc @@ -878,6 +880,7 @@ def test_init(self): # No exception should be raised here gc.collect() + @suppress_immortalization() def test_classes(self): # Check that classes are weakrefable. class A(object): diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index b0bba3422a50a0..99e80baa223e8f 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1957,6 +1957,27 @@ get_py_thread_id(PyObject *self, PyObject *Py_UNUSED(ignored)) } #endif +static PyObject * +set_immortalize_deferred(PyObject *self, PyObject *value) +{ +#ifdef Py_GIL_DISABLED + PyInterpreterState *interp = PyInterpreterState_Get(); + int old_enabled = interp->gc.immortalize.enabled; + int old_enabled_on_thread = interp->gc.immortalize.enable_on_thread_created; + int enabled_on_thread = 0; + if (!PyArg_ParseTuple(value, "i|i", + &interp->gc.immortalize.enabled, + &enabled_on_thread)) + { + return NULL; + } + interp->gc.immortalize.enable_on_thread_created = enabled_on_thread; + return Py_BuildValue("ii", old_enabled, old_enabled_on_thread); +#else + return Py_BuildValue("OO", Py_False, Py_False); +#endif +} + static PyObject * has_inline_values(PyObject *self, PyObject *obj) { @@ -2050,6 +2071,7 @@ static PyMethodDef module_functions[] = { #ifdef Py_GIL_DISABLED {"py_thread_id", get_py_thread_id, METH_NOARGS}, #endif + {"set_immortalize_deferred", set_immortalize_deferred, METH_VARARGS}, {"uop_symbols_test", _Py_uop_symbols_test, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/object.c b/Objects/object.c index 91bb0114cbfc32..8d856939254080 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2430,6 +2430,13 @@ _PyObject_SetDeferredRefcount(PyObject *op) assert(PyType_IS_GC(Py_TYPE(op))); assert(_Py_IsOwnedByCurrentThread(op)); assert(op->ob_ref_shared == 0); + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (interp->gc.immortalize.enabled) { + // gh-117696: immortalize objects instead of using deferred reference + // counting for now. + _Py_SetImmortal(op); + return; + } op->ob_gc_bits |= _PyGC_BITS_DEFERRED; op->ob_ref_local += 1; op->ob_ref_shared = _Py_REF_QUEUED; diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 9cf0e989d0993f..8c0940d8f066a7 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -704,6 +704,12 @@ _PyGC_Init(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; + if (_Py_IsMainInterpreter(interp)) { + // gh-117783: immortalize objects that would use deferred refcounting + // once the first non-main thread is created. + gcstate->immortalize.enable_on_thread_created = 1; + } + gcstate->garbage = PyList_New(0); if (gcstate->garbage == NULL) { return _PyStatus_NO_MEMORY(); @@ -1781,6 +1787,30 @@ custom_visitor_wrapper(const mi_heap_t *heap, const mi_heap_area_t *area, return true; } +// gh-117783: Immortalize objects that use deferred reference counting to +// temporarily work around scaling bottlenecks. +static bool +immortalize_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, + void *block, size_t block_size, void *args) +{ + PyObject *op = op_from_block(block, args, false); + if (op != NULL && _PyObject_HasDeferredRefcount(op)) { + _Py_SetImmortal(op); + op->ob_gc_bits &= ~_PyGC_BITS_DEFERRED; + } + return true; +} + +void +_PyGC_ImmortalizeDeferredObjects(PyInterpreterState *interp) +{ + struct visitor_args args; + _PyEval_StopTheWorld(interp); + gc_visit_heaps(interp, &immortalize_visitor, &args); + interp->gc.immortalize.enabled = 1; + _PyEval_StartTheWorld(interp); +} + void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) { diff --git a/Python/pystate.c b/Python/pystate.c index bca28cebcc9059..78b39c9a577404 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1568,6 +1568,17 @@ new_threadstate(PyInterpreterState *interp, int whence) // Must be called with lock unlocked to avoid re-entrancy deadlock. PyMem_RawFree(new_tstate); } + else { +#ifdef Py_GIL_DISABLED + if (interp->gc.immortalize.enable_on_thread_created && + !interp->gc.immortalize.enabled) + { + // Immortalize objects marked as using deferred reference counting + // the first time a non-main thread is created. + _PyGC_ImmortalizeDeferredObjects(interp); + } +#endif + } #ifdef Py_GIL_DISABLED // Must be called with lock unlocked to avoid lock ordering deadlocks. From 529a160be6733e04d2a44051d3f42f6ada8c1015 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 29 Apr 2024 12:53:04 -0600 Subject: [PATCH 097/217] gh-117953: Share More Machinery Code Between Builtin and Dynamic Extensions (gh-118204) This change will make some later changes simpler. It also brings more consistent behavior and lower maintenance costs. --- Include/internal/pycore_importdl.h | 3 + Python/import.c | 312 +++++++++++++++-------------- Python/importdl.c | 38 +++- 3 files changed, 197 insertions(+), 156 deletions(-) diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index 55c26f2c5a475e..adb89167d124eb 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -36,6 +36,9 @@ extern int _Py_ext_module_loader_info_init( struct _Py_ext_module_loader_info *info, PyObject *name, PyObject *filename); +extern int _Py_ext_module_loader_info_init_for_builtin( + struct _Py_ext_module_loader_info *p_info, + PyObject *name); extern int _Py_ext_module_loader_info_init_from_spec( struct _Py_ext_module_loader_info *info, PyObject *spec); diff --git a/Python/import.c b/Python/import.c index f440cd52866b60..447114ab115bc7 100644 --- a/Python/import.c +++ b/Python/import.c @@ -634,29 +634,30 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) A. _imp_create_dynamic_impl() -> import_find_extension() B. _imp_create_dynamic_impl() -> _PyImport_GetModInitFunc() C. _PyImport_GetModInitFunc(): load - D. _imp_create_dynamic_impl() -> _PyImport_RunModInitFunc() - E. _PyImport_RunModInitFunc(): call - F. -> PyModule_Create() -> PyModule_Create2() - -> PyModule_CreateInitialized() - G. PyModule_CreateInitialized() -> PyModule_New() - H. PyModule_CreateInitialized(): allocate mod->md_state - I. PyModule_CreateInitialized() -> PyModule_AddFunctions() - J. PyModule_CreateInitialized() -> PyModule_SetDocString() - K. PyModule_CreateInitialized(): set mod->md_def - L. : initialize the module, etc. - M. _PyImport_RunModInitFunc(): set def->m_base.m_init - N. _imp_create_dynamic_impl() - -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() - O. _imp_create_dynamic_impl(): set __file__ - P. _imp_create_dynamic_impl() -> update_global_state_for_extension() - Q. update_global_state_for_extension(): - copy __dict__ into def->m_base.m_copy - R. update_global_state_for_extension(): - add it to _PyRuntime.imports.extensions - S. _imp_create_dynamic_impl() -> finish_singlephase_extension() - T. finish_singlephase_extension(): - add it to interp->imports.modules_by_index - U. finish_singlephase_extension(): add it to sys.modules + D. _imp_create_dynamic_impl() -> import_run_extension() + E. import_run_extension() -> _PyImport_RunModInitFunc() + F. _PyImport_RunModInitFunc(): call + G. -> PyModule_Create() -> PyModule_Create2() + -> PyModule_CreateInitialized() + H. PyModule_CreateInitialized() -> PyModule_New() + I. PyModule_CreateInitialized(): allocate mod->md_state + J. PyModule_CreateInitialized() -> PyModule_AddFunctions() + K. PyModule_CreateInitialized() -> PyModule_SetDocString() + L. PyModule_CreateInitialized(): set mod->md_def + M. : initialize the module, etc. + N. _PyImport_RunModInitFunc(): set def->m_base.m_init + O. import_run_extension() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + P. import_run_extension(): set __file__ + Q. import_run_extension() -> update_global_state_for_extension() + R. update_global_state_for_extension(): + copy __dict__ into def->m_base.m_copy + S. update_global_state_for_extension(): + add it to _PyRuntime.imports.extensions + T. import_run_extension() -> finish_singlephase_extension() + U. finish_singlephase_extension(): + add it to interp->imports.modules_by_index + V. finish_singlephase_extension(): add it to sys.modules Step (Q) is skipped for core modules (sys/builtins). @@ -679,14 +680,14 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) ...for single-phase init modules, where m_size >= 0: (6). not main interpreter and never loaded there - every time (not found in _PyRuntime.imports.extensions): - A-O. (same as for m_size == -1) - P-R. (skipped) - S-U. (same as for m_size == -1) + A-P. (same as for m_size == -1) + Q-S. (skipped) + T-V. (same as for m_size == -1) (6). main interpreter - first time (not found in _PyRuntime.imports.extensions): - A-Q. (same as for m_size == -1) - R. (skipped) - S-U. (same as for m_size == -1) + A-R. (same as for m_size == -1) + S. (skipped) + T-V. (same as for m_size == -1) (6). subsequent times (found in _PyRuntime.imports.extensions): A. _imp_create_dynamic_impl() -> import_find_extension() @@ -703,19 +704,21 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) ...for multi-phase init modules: (6). every time: - A. _imp_create_dynamic_impl() -> import_find_extension() (not found) - B. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() - C. _PyImport_LoadDynamicModuleWithSpec(): load module init func - D. _PyImport_LoadDynamicModuleWithSpec(): call module init func - E. _PyImport_LoadDynamicModuleWithSpec() -> PyModule_FromDefAndSpec() - F. PyModule_FromDefAndSpec(): gather/check moduledef slots - G. if there's a Py_mod_create slot: + A. _imp_create_dynamic_impl() -> import_find_extension() (not found) + B. _imp_create_dynamic_impl() -> _PyImport_GetModInitFunc() + C. _PyImport_GetModInitFunc(): load + D. _imp_create_dynamic_impl() -> import_run_extension() + E. import_run_extension() -> _PyImport_RunModInitFunc() + F. _PyImport_RunModInitFunc(): call + G. import_run_extension() -> PyModule_FromDefAndSpec() + H. PyModule_FromDefAndSpec(): gather/check moduledef slots + I. if there's a Py_mod_create slot: 1. PyModule_FromDefAndSpec(): call its function - H. else: + J. else: 1. PyModule_FromDefAndSpec() -> PyModule_NewObject() - I: PyModule_FromDefAndSpec(): set mod->md_def - J. PyModule_FromDefAndSpec() -> _add_methods_to_object() - K. PyModule_FromDefAndSpec() -> PyModule_SetDocString() + K: PyModule_FromDefAndSpec(): set mod->md_def + L. PyModule_FromDefAndSpec() -> _add_methods_to_object() + M. PyModule_FromDefAndSpec() -> PyModule_SetDocString() (10). every time: A. _imp_exec_dynamic_impl() -> exec_builtin_or_dynamic() @@ -1236,6 +1239,20 @@ update_global_state_for_extension(PyThreadState *tstate, assert(!is_core_module(tstate->interp, name, path)); assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0); assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0); + /* XXX gh-88216: The copied dict is owned by the current + * interpreter. That's a problem if the interpreter has + * its own obmalloc state or if the module is successfully + * imported into such an interpreter. If the interpreter + * has its own GIL then there may be data races and + * PyImport_ClearModulesByIndex() can crash. Normally, + * a single-phase init module cannot be imported in an + * isolated interpreter, but there are ways around that. + * Hence, heere be dragons! Ideally we would instead do + * something like make a read-only, immortal copy of the + * dict using PyMem_RawMalloc() and store *that* in m_copy. + * Then we'd need to make sure to clear that when the + * runtime is finalized, rather than in + * PyImport_ClearModulesByIndex(). */ if (def->m_base.m_copy) { /* Somebody already imported the module, likely under a different name. @@ -1299,6 +1316,7 @@ import_find_extension(PyThreadState *tstate, if (def == NULL) { return NULL; } + assert(is_singlephase(def)); /* It may have been successfully imported previously in an interpreter that allows legacy modules @@ -1337,14 +1355,25 @@ import_find_extension(PyThreadState *tstate, Py_DECREF(mod); return NULL; } + /* We can't set mod->md_def if it's missing, + * because _PyImport_ClearModulesByIndex() might break + * due to violating interpreter isolation. See the note + * in fix_up_extension_for_interpreter(). Until that + * is solved, we leave md_def set to NULL. */ + assert(_PyModule_GetDef(mod) == NULL + || _PyModule_GetDef(mod) == def); } else { - if (def->m_base.m_init == NULL) + if (def->m_base.m_init == NULL) { return NULL; - mod = def->m_base.m_init(); - if (mod == NULL) { + } + struct _Py_ext_module_loader_result res; + if (_PyImport_RunModInitFunc(def->m_base.m_init, info, &res) < 0) { return NULL; } + assert(!PyErr_Occurred()); + mod = res.module; + // XXX __file__ doesn't get set! if (PyObject_SetItem(modules, info->name, mod) == -1) { Py_DECREF(mod); return NULL; @@ -1364,6 +1393,86 @@ import_find_extension(PyThreadState *tstate, return mod; } +static PyObject * +import_run_extension(PyThreadState *tstate, PyModInitFunction p0, + struct _Py_ext_module_loader_info *info, + PyObject *spec, PyObject *modules) +{ + PyObject *mod = NULL; + PyModuleDef *def = NULL; + + struct _Py_ext_module_loader_result res; + if (_PyImport_RunModInitFunc(p0, info, &res) < 0) { + /* We discard res.def. */ + assert(res.module == NULL); + assert(PyErr_Occurred()); + goto finally; + } + assert(!PyErr_Occurred()); + + mod = res.module; + res.module = NULL; + def = res.def; + assert(def != NULL); + + if (mod == NULL) { + //assert(!is_singlephase(def)); + assert(mod == NULL); + mod = PyModule_FromDefAndSpec(def, spec); + if (mod == NULL) { + goto finally; + } + } + else { + assert(is_singlephase(def)); + assert(PyModule_Check(mod)); + + const char *name_buf = PyBytes_AS_STRING(info->name_encoded); + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + Py_CLEAR(mod); + goto finally; + } + + /* Remember pointer to module init function. */ + def->m_base.m_init = p0; + + if (info->filename != NULL) { + /* Remember the filename as the __file__ attribute */ + if (PyModule_AddObjectRef(mod, "__file__", info->filename) < 0) { + PyErr_Clear(); /* Not important enough to report */ + } + } + + struct singlephase_global_update singlephase = {0}; + // gh-88216: Extensions and def->m_base.m_copy can be updated + // when the extension module doesn't support sub-interpreters. + if (def->m_size == -1 + && !is_core_module(tstate->interp, info->name, info->path)) + { + singlephase.m_dict = PyModule_GetDict(mod); + assert(singlephase.m_dict != NULL); + } + if (update_global_state_for_extension( + tstate, info->path, info->name, def, &singlephase) < 0) + { + Py_CLEAR(mod); + goto finally; + } + + PyObject *modules = get_modules_dict(tstate, true); + if (finish_singlephase_extension( + tstate, mod, def, info->name, modules) < 0) + { + Py_CLEAR(mod); + goto finally; + } + } + +finally: + return mod; +} + + static int clear_singlephase_extension(PyInterpreterState *interp, PyObject *name, PyObject *path) @@ -1469,10 +1578,8 @@ is_builtin(PyObject *name) static PyObject* create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) { - PyModuleDef *def = NULL; - struct _Py_ext_module_loader_info info; - if (_Py_ext_module_loader_info_init(&info, name, NULL) < 0) { + if (_Py_ext_module_loader_info_init_for_builtin(&info, name) < 0) { return NULL; } @@ -1506,54 +1613,9 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) goto finally; } - mod = p0(); - if (mod == NULL) { - goto finally; - } - - if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { - def = (PyModuleDef*)mod; - assert(!is_singlephase(def)); - mod = PyModule_FromDefAndSpec(def, spec); - if (mod == NULL) { - goto finally; - } - } - else { - assert(PyModule_Check(mod)); - def = PyModule_GetDef(mod); - if (def == NULL) { - Py_CLEAR(mod); - goto finally; - } - assert(is_singlephase(def)); - - /* Remember pointer to module init function. */ - def->m_base.m_init = p0; - - struct singlephase_global_update singlephase = {0}; - // gh-88216: Extensions and def->m_base.m_copy can be updated - // when the extension module doesn't support sub-interpreters. - if (def->m_size == -1 - && !is_core_module(tstate->interp, info.name, info.path)) - { - singlephase.m_dict = PyModule_GetDict(mod); - assert(singlephase.m_dict != NULL); - } - if (update_global_state_for_extension( - tstate, info.name, info.path, def, &singlephase) < 0) - { - Py_CLEAR(mod); - goto finally; - } - PyObject *modules = get_modules_dict(tstate, true); - if (finish_singlephase_extension( - tstate, mod, def, info.name, modules) < 0) - { - Py_CLEAR(mod); - goto finally; - } - } + /* Now load it. */ + mod = import_run_extension( + tstate, p0, &info, spec, get_modules_dict(tstate, true)); finally: _Py_ext_module_loader_info_clear(&info); @@ -3868,7 +3930,6 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) /*[clinic end generated code: output=83249b827a4fde77 input=c31b954f4cf4e09d]*/ { PyObject *mod = NULL; - PyModuleDef *def = NULL; PyThreadState *tstate = _PyThreadState_GET(); struct _Py_ext_module_loader_info info; @@ -3912,67 +3973,12 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) goto finally; } - struct _Py_ext_module_loader_result res; - if (_PyImport_RunModInitFunc(p0, &info, &res) < 0) { - assert(PyErr_Occurred()); - goto finally; - } - - mod = res.module; - res.module = NULL; - def = res.def; - assert(def != NULL); - + mod = import_run_extension( + tstate, p0, &info, spec, get_modules_dict(tstate, true)); if (mod == NULL) { - //assert(!is_singlephase(def)); - mod = PyModule_FromDefAndSpec(def, spec); - if (mod == NULL) { - goto finally; - } - } - else { - assert(is_singlephase(def)); - assert(!is_core_module(tstate->interp, info.name, info.filename)); - assert(!is_core_module(tstate->interp, info.name, info.name)); - - const char *name_buf = PyBytes_AS_STRING(info.name_encoded); - if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { - Py_CLEAR(mod); - goto finally; - } - - /* Remember pointer to module init function. */ - res.def->m_base.m_init = p0; - - /* Remember the filename as the __file__ attribute */ - if (PyModule_AddObjectRef(mod, "__file__", info.filename) < 0) { - PyErr_Clear(); /* Not important enough to report */ - } - - struct singlephase_global_update singlephase = {0}; - // gh-88216: Extensions and def->m_base.m_copy can be updated - // when the extension module doesn't support sub-interpreters. - if (def->m_size == -1) { - singlephase.m_dict = PyModule_GetDict(mod); - assert(singlephase.m_dict != NULL); - } - if (update_global_state_for_extension( - tstate, info.filename, info.name, def, &singlephase) < 0) - { - Py_CLEAR(mod); - goto finally; - } - - PyObject *modules = get_modules_dict(tstate, true); - if (finish_singlephase_extension( - tstate, mod, def, info.name, modules) < 0) - { - Py_CLEAR(mod); - goto finally; - } } - // XXX Shouldn't this happen in the error cases too. + // XXX Shouldn't this happen in the error cases too (i.e. in "finally")? if (fp) { fclose(fp); } diff --git a/Python/importdl.c b/Python/importdl.c index cc70a6daf5d0f1..3e4ea175c0cfbc 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -117,6 +117,7 @@ _Py_ext_module_loader_info_init(struct _Py_ext_module_loader_info *p_info, _Py_ext_module_loader_info_clear(&info); return -1; } + assert(PyUnicode_GetLength(name) > 0); info.name = Py_NewRef(name); info.name_encoded = get_encoded_name(info.name, &info.hook_prefix); @@ -158,6 +159,31 @@ _Py_ext_module_loader_info_init(struct _Py_ext_module_loader_info *p_info, return 0; } +int +_Py_ext_module_loader_info_init_for_builtin( + struct _Py_ext_module_loader_info *info, + PyObject *name) +{ + assert(PyUnicode_Check(name)); + assert(PyUnicode_FindChar(name, '.', 0, PyUnicode_GetLength(name), -1) == -1); + assert(PyUnicode_GetLength(name) > 0); + + PyObject *name_encoded = PyUnicode_AsEncodedString(name, "ascii", NULL); + if (name_encoded == NULL) { + return -1; + } + + *info = (struct _Py_ext_module_loader_info){ + .name=Py_NewRef(name), + .name_encoded=name_encoded, + /* We won't need filename. */ + .path=name, + .hook_prefix=ascii_only_prefix, + .newcontext=NULL, + }; + return 0; +} + int _Py_ext_module_loader_info_init_from_spec( struct _Py_ext_module_loader_info *p_info, @@ -284,14 +310,20 @@ _PyImport_RunModInitFunc(PyModInitFunction p0, /* single-phase init (legacy) */ res.module = m; - res.def = PyModule_GetDef(m); - if (res.def == NULL) { - PyErr_Clear(); + if (!PyModule_Check(m)) { PyErr_Format(PyExc_SystemError, "initialization of %s did not return an extension " "module", name_buf); goto error; } + + res.def = _PyModule_GetDef(m); + if (res.def == NULL) { + PyErr_Format(PyExc_SystemError, + "initialization of %s did not return a valid extension " + "module", name_buf); + goto error; + } } assert(!PyErr_Occurred()); From ee3413c1c70725e133b29bb1d245f569a8f64062 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 29 Apr 2024 22:41:59 +0300 Subject: [PATCH 098/217] gh-118401: Docs: Use Sphinx short options (#118403) --- .github/workflows/reusable-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index fd519d60f324ab..9e26d7847d2bd3 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -55,7 +55,7 @@ jobs: run: | set -Eeuo pipefail # Build docs with the '-n' (nit-picky) option; write warnings to file - make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet --nitpicky --fail-on-warning --keep-going --warning-file sphinx-warnings.txt" html + make -C Doc/ PYTHON=../python SPHINXOPTS="-q -n -W --keep-going -w sphinx-warnings.txt" html - name: 'Check warnings' if: github.event_name == 'pull_request' run: | From 79688b5b0ea761183193ffb0859415f3b02fa44d Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Mon, 29 Apr 2024 15:49:01 -0400 Subject: [PATCH 099/217] gh-118331: Handle errors in _PyObject_SetManagedDict (#118334) When detaching a dict, the `copy_values` call may fail due to out-of-memory errors. This can be triggered by test_no_memory in test_repl. --- Include/cpython/object.h | 2 +- Objects/dictobject.c | 29 ++++++++++++++++++----------- Objects/typeobject.c | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index a8f57827a964cd..a6b93b93ab0f7a 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -493,7 +493,7 @@ do { \ PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj); PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg); -PyAPI_FUNC(void) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict); +PyAPI_FUNC(int) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict); PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj); #define TYPE_MAX_WATCHERS 8 diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 43cb2350b7fc71..1f21f70f149c80 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -7056,11 +7056,12 @@ set_dict_inline_values(PyObject *obj, PyDictObject *new_dict) } } -void +int _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict) { assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); assert(_PyObject_InlineValuesConsistencyCheck(obj)); + int err = 0; PyTypeObject *tp = Py_TYPE(obj); if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { PyDictObject *dict = _PyObject_GetManagedDict(obj); @@ -7076,11 +7077,11 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict) Py_END_CRITICAL_SECTION(); if (dict == NULL) { - return; + return 0; } #else set_dict_inline_values(obj, (PyDictObject *)new_dict); - return; + return 0; #endif } @@ -7089,15 +7090,16 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict) // We've locked dict, but the actual dict could have changed // since we locked it. dict = _PyObject_ManagedDictPointer(obj)->dict; - - FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, - (PyDictObject *)Py_XNewRef(new_dict)); - - _PyDict_DetachFromObject(dict, obj); - + err = _PyDict_DetachFromObject(dict, obj); + if (err == 0) { + FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, + (PyDictObject *)Py_XNewRef(new_dict)); + } Py_END_CRITICAL_SECTION2(); - Py_XDECREF(dict); + if (err == 0) { + Py_XDECREF(dict); + } } else { PyDictObject *dict; @@ -7114,18 +7116,23 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict) Py_XDECREF(dict); } assert(_PyObject_InlineValuesConsistencyCheck(obj)); + return err; } void PyObject_ClearManagedDict(PyObject *obj) { - _PyObject_SetManagedDict(obj, NULL); + if (_PyObject_SetManagedDict(obj, NULL) < 0) { + PyErr_WriteUnraisable(NULL); + } } int _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj) { _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj); + assert(_PyObject_ManagedDictPointer(obj)->dict == mp); + assert(_PyObject_InlineValuesConsistencyCheck(obj)); if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) { return 0; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 07e0a5a02da87f..50efbb6e182df0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3167,7 +3167,7 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context) } if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - _PyObject_SetManagedDict(obj, value); + return _PyObject_SetManagedDict(obj, value); } else { dictptr = _PyObject_ComputedDictPointer(obj); From 2ba1aed596ff9e7c5e193cf990f1f20f08bbf116 Mon Sep 17 00:00:00 2001 From: Alex Turner Date: Mon, 29 Apr 2024 21:26:26 +0100 Subject: [PATCH 100/217] gh-117657: TSAN fix race on `gstate->young.count` (#118313) --- Python/gc_free_threading.c | 25 +++++++++++----------- Tools/tsan/suppressions_free_threading.txt | 1 - 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 8c0940d8f066a7..ef6aaad1252311 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1044,9 +1044,20 @@ record_deallocation(PyThreadState *tstate) } static void -gc_collect_internal(PyInterpreterState *interp, struct collection_state *state) +gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, int generation) { _PyEval_StopTheWorld(interp); + + // update collection and allocation counters + if (generation+1 < NUM_GENERATIONS) { + state->gcstate->old[generation].count += 1; + } + + state->gcstate->young.count = 0; + for (int i = 1; i <= generation; ++i) { + state->gcstate->old[i-1].count = 0; + } + // merge refcounts for all queued objects merge_all_queued_objects(interp, state); process_delayed_frees(interp); @@ -1115,7 +1126,6 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state) static Py_ssize_t gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) { - int i; Py_ssize_t m = 0; /* # objects collected */ Py_ssize_t n = 0; /* # unreachable objects that couldn't be collected */ PyTime_t t1 = 0; /* initialize to prevent a compiler warning */ @@ -1161,15 +1171,6 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) PyDTrace_GC_START(generation); } - /* update collection and allocation counters */ - if (generation+1 < NUM_GENERATIONS) { - gcstate->old[generation].count += 1; - } - gcstate->young.count = 0; - for (i = 1; i <= generation; i++) { - gcstate->old[i-1].count = 0; - } - PyInterpreterState *interp = tstate->interp; struct collection_state state = { @@ -1177,7 +1178,7 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) .gcstate = gcstate, }; - gc_collect_internal(interp, &state); + gc_collect_internal(interp, &state, generation); m = state.collected; n = state.uncollectable; diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 4b1a2fdf6dd43a..4f6648a7573184 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -27,7 +27,6 @@ race:_PyObject_GC_TRACK race:_PyParkingLot_Park race:_PyType_HasFeature race:assign_version_tag -race:gc_collect_main race:gc_restore_tid race:initialize_new_array race:insertdict From 96d8ca7ad6c4f23f023dd6ed0abd042b29dff4a2 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 29 Apr 2024 21:46:25 +0100 Subject: [PATCH 101/217] gh-118347: Fix Windows installer not updating launcher (GH-118386) --- .../2024-04-29-13-53-25.gh-issue-118347.U5ZRm_.rst | 1 + .../bundle/bootstrap/PythonBootstrapperApplication.cpp | 8 ++++---- Tools/msi/bundle/packagegroups/launcher.wxs | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-04-29-13-53-25.gh-issue-118347.U5ZRm_.rst diff --git a/Misc/NEWS.d/next/Windows/2024-04-29-13-53-25.gh-issue-118347.U5ZRm_.rst b/Misc/NEWS.d/next/Windows/2024-04-29-13-53-25.gh-issue-118347.U5ZRm_.rst new file mode 100644 index 00000000000000..8f02ed94f100fe --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-04-29-13-53-25.gh-issue-118347.U5ZRm_.rst @@ -0,0 +1 @@ +Fixes launcher updates not being installed. diff --git a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp index e0e179e3aede6d..7cddda9b06555d 100644 --- a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp +++ b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp @@ -464,11 +464,11 @@ class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication { LOC_STRING *pLocString = nullptr; LPCWSTR locKey = L"#(loc.Include_launcherHelp)"; - LONGLONG detectedLauncher; + LONGLONG blockedLauncher; - if (SUCCEEDED(BalGetNumericVariable(L"DetectedLauncher", &detectedLauncher)) && detectedLauncher) { + if (SUCCEEDED(BalGetNumericVariable(L"BlockedLauncher", &blockedLauncher)) && blockedLauncher) { locKey = L"#(loc.Include_launcherRemove)"; - } else if (SUCCEEDED(BalGetNumericVariable(L"DetectedOldLauncher", &detectedLauncher)) && detectedLauncher) { + } else if (SUCCEEDED(BalGetNumericVariable(L"DetectedOldLauncher", &blockedLauncher)) && blockedLauncher) { locKey = L"#(loc.Include_launcherUpgrade)"; } @@ -2671,7 +2671,7 @@ class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication { /*Elevate when installing for all users*/ L"InstallAllUsers or " /*Elevate when installing the launcher for all users and it was not detected*/ - L"(Include_launcher and InstallLauncherAllUsers and not DetectedLauncher)" + L"(Include_launcher and InstallLauncherAllUsers and not BlockedLauncher)" L")", L"" }; diff --git a/Tools/msi/bundle/packagegroups/launcher.wxs b/Tools/msi/bundle/packagegroups/launcher.wxs index a6922758f31f14..080598a0a486ef 100644 --- a/Tools/msi/bundle/packagegroups/launcher.wxs +++ b/Tools/msi/bundle/packagegroups/launcher.wxs @@ -11,7 +11,7 @@ EnableFeatureSelection="yes" Permanent="yes" Visible="yes" - InstallCondition="(InstallAllUsers or InstallLauncherAllUsers) and Include_launcher and not DetectedLauncher"> + InstallCondition="(InstallAllUsers or InstallLauncherAllUsers) and Include_launcher and not BlockedLauncher"> @@ -25,7 +25,7 @@ EnableFeatureSelection="yes" Permanent="yes" Visible="yes" - InstallCondition="not (InstallAllUsers or InstallLauncherAllUsers) and Include_launcher and not DetectedLauncher"> + InstallCondition="not (InstallAllUsers or InstallLauncherAllUsers) and Include_launcher and not BlockedLauncher"> From 8e4fb5d260e529c9d4ca60980225fbd00dd5c3c8 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 29 Apr 2024 13:57:49 -0700 Subject: [PATCH 102/217] gh-118359: Improve docs for Bdb.user_call (#118368) The `argument_list` parameter of bdb.Bdb.user_call has been useless for 25 years. It is retained for backwards compatibility, but it will always be None. --- Doc/library/bdb.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/bdb.rst b/Doc/library/bdb.rst index 7bf4308a96d0f5..b050560cd9038c 100644 --- a/Doc/library/bdb.rst +++ b/Doc/library/bdb.rst @@ -240,6 +240,9 @@ The :mod:`bdb` module also defines two classes: Called from :meth:`dispatch_call` if a break might stop inside the called function. + *argument_list* is not used anymore and will always be ``None``. + The argument is kept for backwards compatibility. + .. method:: user_line(frame) Called from :meth:`dispatch_line` when either :meth:`stop_here` or From 8b56d82c59c2983b4292a7f506982f2cab352bb2 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 29 Apr 2024 14:09:16 -0700 Subject: [PATCH 103/217] GH-118306: Update JIT to use LLVM 18 (GH-118307) --- .github/workflows/jit.yml | 11 ++-- ...-04-26-05-38-18.gh-issue-118306.vRUEOU.rst | 1 + Python/jit.c | 3 ++ Tools/jit/README.md | 10 ++-- Tools/jit/_llvm.py | 2 +- Tools/jit/_schema.py | 9 ++-- Tools/jit/_stencils.py | 4 +- Tools/jit/_targets.py | 52 +++++++++---------- 8 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-26-05-38-18.gh-issue-118306.vRUEOU.rst diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index b37eb727955909..b969e6d13e61a3 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -41,7 +41,7 @@ jobs: - true - false llvm: - - 16 + - 18 include: - target: i686-pc-windows-msvc/msvc architecture: Win32 @@ -94,6 +94,7 @@ jobs: - name: Native Windows if: runner.os == 'Windows' && matrix.architecture != 'ARM64' run: | + choco upgrade llvm -y choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }} ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '--pgo' }} -p ${{ matrix.architecture }} ./PCbuild/rt.bat ${{ matrix.debug && '-d' }} -p ${{ matrix.architecture }} -q --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 4500 --verbose2 --verbose3 @@ -102,27 +103,31 @@ jobs: - name: Emulated Windows if: runner.os == 'Windows' && matrix.architecture == 'ARM64' run: | + choco upgrade llvm -y choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }} ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} - name: Native macOS if: runner.os == 'macOS' run: | + brew update brew install llvm@${{ matrix.llvm }} SDKROOT="$(xcrun --show-sdk-path)" \ ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }} make all --jobs 4 ./python.exe -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + # --with-lto has been removed temporarily as a result of an open issue in LLVM 18 (see https://github.com/llvm/llvm-project/issues/87553) - name: Native Linux if: runner.os == 'Linux' && matrix.architecture == 'x86_64' run: | sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }} export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH" - ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }} + ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations' }} make all --jobs 4 ./python -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + # --with-lto has been removed temporarily as a result of an open issue in LLVM 18 (see https://github.com/llvm/llvm-project/issues/87553) - name: Emulated Linux if: runner.os == 'Linux' && matrix.architecture != 'x86_64' run: | @@ -139,6 +144,6 @@ jobs: CC="${{ matrix.compiler == 'clang' && 'clang --target=$HOST' || '$HOST-gcc' }}" \ CPP="$CC --preprocess" \ HOSTRUNNER=qemu-${{ matrix.architecture }} \ - ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }} --build=x86_64-linux-gnu --host="$HOST" --with-build-python=../build/bin/python3 --with-pkg-config=no ac_cv_buggy_getaddrinfo=no ac_cv_file__dev_ptc=no ac_cv_file__dev_ptmx=yes + ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations ' }} --build=x86_64-linux-gnu --host="$HOST" --with-build-python=../build/bin/python3 --with-pkg-config=no ac_cv_buggy_getaddrinfo=no ac_cv_file__dev_ptc=no ac_cv_file__dev_ptmx=yes make all --jobs 4 ./python -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 4500 --verbose2 --verbose3 diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-26-05-38-18.gh-issue-118306.vRUEOU.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-26-05-38-18.gh-issue-118306.vRUEOU.rst new file mode 100644 index 00000000000000..051295541ab7e2 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-26-05-38-18.gh-issue-118306.vRUEOU.rst @@ -0,0 +1 @@ +Update JIT compilation to use LLVM 18 diff --git a/Python/jit.c b/Python/jit.c index 8782adb847cfd6..75ec4fb9756eb7 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -234,6 +234,7 @@ patch(unsigned char *base, const Stencil *stencil, uintptr_t patches[]) assert((int64_t)value < (1LL << 31)); *loc32 = (uint32_t)value; continue; + case HoleKind_ARM64_RELOC_BRANCH26: case HoleKind_IMAGE_REL_ARM64_BRANCH26: case HoleKind_R_AARCH64_CALL26: case HoleKind_R_AARCH64_JUMP26: @@ -278,6 +279,7 @@ patch(unsigned char *base, const Stencil *stencil, uintptr_t patches[]) case HoleKind_ARM64_RELOC_GOT_LOAD_PAGE21: case HoleKind_IMAGE_REL_ARM64_PAGEBASE_REL21: case HoleKind_R_AARCH64_ADR_GOT_PAGE: + case HoleKind_R_AARCH64_ADR_PREL_PG_HI21: // 21-bit count of pages between this page and an absolute address's // page... I know, I know, it's weird. Pairs nicely with // ARM64_RELOC_GOT_LOAD_PAGEOFF12 (below). @@ -341,6 +343,7 @@ patch(unsigned char *base, const Stencil *stencil, uintptr_t patches[]) case HoleKind_ARM64_RELOC_PAGEOFF12: case HoleKind_IMAGE_REL_ARM64_PAGEOFFSET_12A: case HoleKind_IMAGE_REL_ARM64_PAGEOFFSET_12L: + case HoleKind_R_AARCH64_ADD_ABS_LO12_NC: case HoleKind_R_AARCH64_LD64_GOT_LO12_NC: // 12-bit low part of an absolute address. Pairs nicely with // ARM64_RELOC_GOT_LOAD_PAGE21 (above). diff --git a/Tools/jit/README.md b/Tools/jit/README.md index 04a6c0780bf972..7b33f99d23f75d 100644 --- a/Tools/jit/README.md +++ b/Tools/jit/README.md @@ -23,17 +23,21 @@ sudo ./llvm.sh 16 ### macOS -Install LLVM 16 with [Homebrew](https://brew.sh): +Install LLVM 18 with [Homebrew](https://brew.sh): ```sh -brew install llvm@16 +brew install llvm@18 ``` Homebrew won't add any of the tools to your `$PATH`. That's okay; the build script knows how to find them. ### Windows -Install LLVM 16 [by searching for it on LLVM's GitHub releases page](https://github.com/llvm/llvm-project/releases?q=16), clicking on "Assets", downloading the appropriate Windows installer for your platform (likely the file ending with `-win64.exe`), and running it. **When installing, be sure to select the option labeled "Add LLVM to the system PATH".** +Install LLVM 18 [by searching for it on LLVM's GitHub releases page](https://github.com/llvm/llvm-project/releases?q=18), clicking on "Assets", downloading the appropriate Windows installer for your platform (likely the file ending with `-win64.exe`), and running it. **When installing, be sure to select the option labeled "Add LLVM to the system PATH".** + +### Dev Containers + +If you are working CPython in a [Codespaces instance](https://devguide.python.org/getting-started/setup-building/#using-codespaces), there's no need to install LLVM as the Fedora 40 base image includes LLVM 18 out of the box. ## Building diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py index 603bbef59ba2e6..74a048ccbfcc58 100644 --- a/Tools/jit/_llvm.py +++ b/Tools/jit/_llvm.py @@ -7,7 +7,7 @@ import subprocess import typing -_LLVM_VERSION = 16 +_LLVM_VERSION = 18 _LLVM_VERSION_PATTERN = re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\s+") _P = typing.ParamSpec("_P") diff --git a/Tools/jit/_schema.py b/Tools/jit/_schema.py index 045fd502a03c12..6aef887e12475b 100644 --- a/Tools/jit/_schema.py +++ b/Tools/jit/_schema.py @@ -2,6 +2,7 @@ import typing HoleKind: typing.TypeAlias = typing.Literal[ + "ARM64_RELOC_BRANCH26", "ARM64_RELOC_GOT_LOAD_PAGE21", "ARM64_RELOC_GOT_LOAD_PAGEOFF12", "ARM64_RELOC_PAGE21", @@ -16,8 +17,10 @@ "IMAGE_REL_I386_REL32", "R_AARCH64_ABS64", "R_AARCH64_ADR_GOT_PAGE", + "R_AARCH64_ADR_PREL_PG_HI21", "R_AARCH64_CALL26", "R_AARCH64_JUMP26", + "R_AARCH64_ADD_ABS_LO12_NC", "R_AARCH64_LD64_GOT_LO12_NC", "R_AARCH64_MOVW_UABS_G0_NC", "R_AARCH64_MOVW_UABS_G1_NC", @@ -68,12 +71,12 @@ class _COFFSymbol(typing.TypedDict): class _ELFSymbol(typing.TypedDict): - Name: dict[typing.Literal["Value"], str] + Name: dict[typing.Literal["Name"], str] Value: int class _MachOSymbol(typing.TypedDict): - Name: dict[typing.Literal["Value"], str] + Name: dict[typing.Literal["Name"], str] Value: int @@ -99,7 +102,7 @@ class ELFSection(typing.TypedDict): Relocations: list[dict[typing.Literal["Relocation"], ELFRelocation]] SectionData: dict[typing.Literal["Bytes"], list[int]] Symbols: list[dict[typing.Literal["Symbol"], _ELFSymbol]] - Type: dict[typing.Literal["Value"], str] + Type: dict[typing.Literal["Name"], str] class MachOSection(typing.TypedDict): diff --git a/Tools/jit/_stencils.py b/Tools/jit/_stencils.py index 243bb3dd134f70..f8ecffcf3ddda2 100644 --- a/Tools/jit/_stencils.py +++ b/Tools/jit/_stencils.py @@ -203,7 +203,9 @@ def process_relocations(self, *, alignment: int = 1) -> None: """Fix up all GOT and internal relocations for this stencil group.""" for hole in self.code.holes.copy(): if ( - hole.kind in {"R_AARCH64_CALL26", "R_AARCH64_JUMP26"} + hole.kind in { + "R_AARCH64_CALL26", "R_AARCH64_JUMP26", "ARM64_RELOC_BRANCH26" + } and hole.value is HoleValue.ZERO ): self.code.pad(alignment) diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 66db358679239e..91734b36b4ab1b 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -221,7 +221,7 @@ def _handle_relocation( case { "Offset": offset, "Symbol": s, - "Type": {"Value": "IMAGE_REL_I386_DIR32" as kind}, + "Type": {"Name": "IMAGE_REL_I386_DIR32" as kind}, }: offset += base value, symbol = self._unwrap_dllimport(s) @@ -230,7 +230,7 @@ def _handle_relocation( "Offset": offset, "Symbol": s, "Type": { - "Value": "IMAGE_REL_AMD64_REL32" | "IMAGE_REL_I386_REL32" as kind + "Name": "IMAGE_REL_AMD64_REL32" | "IMAGE_REL_I386_REL32" as kind }, }: offset += base @@ -242,7 +242,7 @@ def _handle_relocation( "Offset": offset, "Symbol": s, "Type": { - "Value": "IMAGE_REL_ARM64_BRANCH26" + "Name": "IMAGE_REL_ARM64_BRANCH26" | "IMAGE_REL_ARM64_PAGEBASE_REL21" | "IMAGE_REL_ARM64_PAGEOFFSET_12A" | "IMAGE_REL_ARM64_PAGEOFFSET_12L" as kind @@ -262,7 +262,7 @@ class _ELF( def _handle_section( self, section: _schema.ELFSection, group: _stencils.StencilGroup ) -> None: - section_type = section["Type"]["Value"] + section_type = section["Type"]["Name"] flags = {flag["Name"] for flag in section["Flags"]["Flags"]} if section_type == "SHT_RELA": assert "SHF_INFO_LINK" in flags, flags @@ -290,7 +290,7 @@ def _handle_section( for wrapped_symbol in section["Symbols"]: symbol = wrapped_symbol["Symbol"] offset = len(stencil.body) + symbol["Value"] - name = symbol["Name"]["Value"] + name = symbol["Name"]["Name"] name = name.removeprefix(self.prefix) group.symbols[name] = value, offset stencil.body.extend(section["SectionData"]["Bytes"]) @@ -312,9 +312,9 @@ def _handle_relocation( case { "Addend": addend, "Offset": offset, - "Symbol": {"Value": s}, + "Symbol": {"Name": s}, "Type": { - "Value": "R_AARCH64_ADR_GOT_PAGE" + "Name": "R_AARCH64_ADR_GOT_PAGE" | "R_AARCH64_LD64_GOT_LO12_NC" | "R_X86_64_GOTPCREL" | "R_X86_64_GOTPCRELX" @@ -327,8 +327,8 @@ def _handle_relocation( case { "Addend": addend, "Offset": offset, - "Symbol": {"Value": s}, - "Type": {"Value": kind}, + "Symbol": {"Name": s}, + "Type": {"Name": kind}, }: offset += base s = s.removeprefix(self.prefix) @@ -371,7 +371,7 @@ def _handle_section( for wrapped_symbol in section["Symbols"]: symbol = wrapped_symbol["Symbol"] offset = symbol["Value"] - start_address - name = symbol["Name"]["Value"] + name = symbol["Name"]["Name"] name = name.removeprefix(self.prefix) group.symbols[name] = value, offset assert "Relocations" in section @@ -387,9 +387,9 @@ def _handle_relocation( match relocation: case { "Offset": offset, - "Symbol": {"Value": s}, + "Symbol": {"Name": s}, "Type": { - "Value": "ARM64_RELOC_GOT_LOAD_PAGE21" + "Name": "ARM64_RELOC_GOT_LOAD_PAGE21" | "ARM64_RELOC_GOT_LOAD_PAGEOFF12" as kind }, }: @@ -399,8 +399,8 @@ def _handle_relocation( addend = 0 case { "Offset": offset, - "Symbol": {"Value": s}, - "Type": {"Value": "X86_64_RELOC_GOT" | "X86_64_RELOC_GOT_LOAD" as kind}, + "Symbol": {"Name": s}, + "Type": {"Name": "X86_64_RELOC_GOT" | "X86_64_RELOC_GOT_LOAD" as kind}, }: offset += base s = s.removeprefix(self.prefix) @@ -410,13 +410,13 @@ def _handle_relocation( ) case { "Offset": offset, - "Section": {"Value": s}, - "Type": {"Value": "X86_64_RELOC_SIGNED" as kind}, + "Section": {"Name": s}, + "Type": {"Name": "X86_64_RELOC_SIGNED" as kind}, } | { "Offset": offset, - "Symbol": {"Value": s}, + "Symbol": {"Name": s}, "Type": { - "Value": "X86_64_RELOC_BRANCH" | "X86_64_RELOC_SIGNED" as kind + "Name": "X86_64_RELOC_BRANCH" | "X86_64_RELOC_SIGNED" as kind }, }: offset += base @@ -427,12 +427,12 @@ def _handle_relocation( ) case { "Offset": offset, - "Section": {"Value": s}, - "Type": {"Value": kind}, + "Section": {"Name": s}, + "Type": {"Name": kind}, } | { "Offset": offset, - "Symbol": {"Value": s}, - "Type": {"Value": kind}, + "Symbol": {"Name": s}, + "Type": {"Name": kind}, }: offset += base s = s.removeprefix(self.prefix) @@ -446,13 +446,12 @@ def _handle_relocation( def get_target(host: str) -> _COFF | _ELF | _MachO: """Build a _Target for the given host "triple" and options.""" if re.fullmatch(r"aarch64-apple-darwin.*", host): - args = ["-mcmodel=large"] - return _MachO(host, alignment=8, args=args, prefix="_") + return _MachO(host, alignment=8, prefix="_") if re.fullmatch(r"aarch64-pc-windows-msvc", host): args = ["-fms-runtime-lib=dll"] return _COFF(host, alignment=8, args=args) if re.fullmatch(r"aarch64-.*-linux-gnu", host): - args = ["-mcmodel=large"] + args = ["-fpic"] return _ELF(host, alignment=8, args=args) if re.fullmatch(r"i686-pc-windows-msvc", host): args = ["-DPy_NO_ENABLE_SHARED"] @@ -463,5 +462,6 @@ def get_target(host: str) -> _COFF | _ELF | _MachO: args = ["-fms-runtime-lib=dll"] return _COFF(host, args=args) if re.fullmatch(r"x86_64-.*-linux-gnu", host): - return _ELF(host) + args = ["-fpic"] + return _ELF(host, args=args) raise ValueError(host) From 11cbf77f9799e86603bca927a98959c7b94fff80 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 30 Apr 2024 13:42:13 +0900 Subject: [PATCH 104/217] gh-118392: Add note about random.random for multi thread app (gh-118396) --- Doc/library/random.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 8fbce18c56f17c..61798263d61195 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -59,6 +59,12 @@ from sources provided by the operating system. random number generator with a long period and comparatively simple update operations. +.. note:: + The global random number generator and instances of :class:`Random` are thread-safe. + However, in the free-threaded build, concurrent calls to the global generator or + to the same instance of :class:`Random` may encounter contention and poor performance. + Consider using separate instances of :class:`Random` per thread instead. + Bookkeeping functions --------------------- From 0f797402bc77192c76a952410ca8e17359feab3c Mon Sep 17 00:00:00 2001 From: edson duarte Date: Tue, 30 Apr 2024 05:34:15 -0300 Subject: [PATCH 105/217] gh-85453: Consistent backquotes on None occurences across datetime.rst (#118282) --- Doc/library/datetime.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index b1b5e04c74b066..f40c6cea83820c 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1735,7 +1735,7 @@ day, and subject to adjustment via a :class:`tzinfo` object. * ``fold in [0, 1]``. If an argument outside those ranges is given, :exc:`ValueError` is raised. All - default to 0 except *tzinfo*, which defaults to :const:`None`. + default to 0 except *tzinfo*, which defaults to ``None``. Class attributes: From 5b05d452cd20d9f0cfecdeec90adad3af5e4dfff Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 30 Apr 2024 11:33:13 +0100 Subject: [PATCH 106/217] GH-118095: Add tier 2 support for YIELD_VALUE (GH-118380) --- Include/internal/pycore_opcode_metadata.h | 1 + Include/internal/pycore_uop_ids.h | 1 + Include/internal/pycore_uop_metadata.h | 4 +++ Python/bytecodes.c | 16 ++++++--- Python/executor_cases.c.h | 41 +++++++++++++++++++++++ Python/generated_cases.c.h | 18 +++++++--- Python/optimizer.c | 4 +-- Python/optimizer_bytecodes.c | 9 +++++ Python/optimizer_cases.c.h | 14 +++++--- 9 files changed, 92 insertions(+), 16 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 4b1f43cf2af06e..2ccc548ca6c5fd 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1347,6 +1347,7 @@ _PyOpcode_macro_expansion[256] = { [UNPACK_SEQUENCE_TUPLE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE_TUPLE, 0, 0 } } }, [UNPACK_SEQUENCE_TWO_TUPLE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE_TWO_TUPLE, 0, 0 } } }, [WITH_EXCEPT_START] = { .nuops = 1, .uops = { { _WITH_EXCEPT_START, 0, 0 } } }, + [YIELD_VALUE] = { .nuops = 1, .uops = { { _YIELD_VALUE, 0, 0 } } }, }; #endif // NEED_OPCODE_METADATA diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 030321ef4fcb23..0a2231e88488c9 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -282,6 +282,7 @@ extern "C" { #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START +#define _YIELD_VALUE YIELD_VALUE #define MAX_UOP_ID 447 #ifdef __cplusplus diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 59e690f3aace35..a84212c1ec0b69 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -91,6 +91,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GET_AITER] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_ANEXT] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_GET_AWAITABLE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_YIELD_VALUE] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_POP_EXCEPT] = HAS_ESCAPES_FLAG, [_LOAD_ASSERTION_ERROR] = 0, [_LOAD_BUILD_CLASS] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -500,6 +501,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_UNPACK_SEQUENCE_TUPLE] = "_UNPACK_SEQUENCE_TUPLE", [_UNPACK_SEQUENCE_TWO_TUPLE] = "_UNPACK_SEQUENCE_TWO_TUPLE", [_WITH_EXCEPT_START] = "_WITH_EXCEPT_START", + [_YIELD_VALUE] = "_YIELD_VALUE", }; int _PyUop_num_popped(int opcode, int oparg) { @@ -648,6 +650,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _GET_AWAITABLE: return 1; + case _YIELD_VALUE: + return 1; case _POP_EXCEPT: return 1; case _LOAD_ASSERTION_ERROR: diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f688856d6909ca..eee8b328a65b8c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1089,32 +1089,38 @@ dummy_func( goto resume_frame; } - tier1 inst(YIELD_VALUE, (retval -- unused)) { + inst(YIELD_VALUE, (retval -- value)) { // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() // or throw() call. + #if TIER_ONE assert(frame != &entry_frame); - frame->instr_ptr = next_instr; + #endif + frame->instr_ptr++; PyGenObject *gen = _PyFrame_GetGenerator(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); gen->gi_frame_state = FRAME_SUSPENDED + oparg; - _PyFrame_SetStackPointer(frame, stack_pointer - 1); + SYNC_SP(); + _PyFrame_SetStackPointer(frame, stack_pointer); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; - _PyFrame_StackPush(frame, retval); /* We don't know which of these is relevant here, so keep them equal */ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + #if TIER_ONE assert(_PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); + #endif LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - goto resume_frame; + LOAD_SP(); + value = retval; + LLTRACE_RESUME_FRAME(); } inst(POP_EXCEPT, (exc_value -- )) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 2d9acfeea432bc..b17f3762714c72 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1110,6 +1110,47 @@ /* _INSTRUMENTED_YIELD_VALUE is not a viable micro-op for tier 2 because it is instrumented */ + case _YIELD_VALUE: { + PyObject *retval; + PyObject *value; + oparg = CURRENT_OPARG(); + retval = stack_pointer[-1]; + // NOTE: It's important that YIELD_VALUE never raises an exception! + // The compiler treats any exception raised here as a failed close() + // or throw() call. + #if TIER_ONE + assert(frame != &entry_frame); + #endif + frame->instr_ptr++; + PyGenObject *gen = _PyFrame_GetGenerator(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + gen->gi_frame_state = FRAME_SUSPENDED + oparg; + stack_pointer += -1; + _PyFrame_SetStackPointer(frame, stack_pointer); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + /* We don't know which of these is relevant here, so keep them equal */ + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + #if TIER_ONE + assert(_PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || + _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); + #endif + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + LOAD_SP(); + value = retval; + LLTRACE_RESUME_FRAME(); + stack_pointer[0] = value; + stack_pointer += 1; + break; + } + case _POP_EXCEPT: { PyObject *exc_value; exc_value = stack_pointer[-1]; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index c27505fde3d9fa..7c1cc147de4e1a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -6012,31 +6012,41 @@ next_instr += 1; INSTRUCTION_STATS(YIELD_VALUE); PyObject *retval; + PyObject *value; retval = stack_pointer[-1]; // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() // or throw() call. + #if TIER_ONE assert(frame != &entry_frame); - frame->instr_ptr = next_instr; + #endif + frame->instr_ptr++; PyGenObject *gen = _PyFrame_GetGenerator(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); gen->gi_frame_state = FRAME_SUSPENDED + oparg; - _PyFrame_SetStackPointer(frame, stack_pointer - 1); + stack_pointer += -1; + _PyFrame_SetStackPointer(frame, stack_pointer); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; - _PyFrame_StackPush(frame, retval); /* We don't know which of these is relevant here, so keep them equal */ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + #if TIER_ONE assert(_PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); + #endif LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - goto resume_frame; + LOAD_SP(); + value = retval; + LLTRACE_RESUME_FRAME(); + stack_pointer[0] = value; + stack_pointer += 1; + DISPATCH(); } #undef TIER_ONE diff --git a/Python/optimizer.c b/Python/optimizer.c index fcd7d18f2c2e22..a9a35fc902018a 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -704,7 +704,7 @@ translate_bytecode_to_trace( int nuops = expansion->nuops; RESERVE(nuops + 1); /* One extra for exit */ int16_t last_op = expansion->uops[nuops-1].uop; - if (last_op == _POP_FRAME || last_op == _RETURN_GENERATOR) { + if (last_op == _POP_FRAME || last_op == _RETURN_GENERATOR || last_op == _YIELD_VALUE) { // Check for trace stack underflow now: // We can't bail e.g. in the middle of // LOAD_CONST + _POP_FRAME. @@ -763,7 +763,7 @@ translate_bytecode_to_trace( Py_FatalError("garbled expansion"); } - if (uop == _POP_FRAME || uop == _RETURN_GENERATOR) { + if (uop == _POP_FRAME || uop == _RETURN_GENERATOR || uop == _YIELD_VALUE) { TRACE_STACK_POP(); /* Set the operand to the function or code object returned to, * to assist optimization passes. (See _PUSH_FRAME below.) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 8bc56342774790..60763286178c71 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -673,6 +673,15 @@ dummy_func(void) { } } + op(_YIELD_VALUE, (unused -- res)) { + OUT_OF_SPACE_IF_NULL(res = sym_new_unknown(ctx)); + } + + op(_FOR_ITER_GEN_FRAME, ( -- )) { + /* We are about to hit the end of the trace */ + goto done; + } + op(_CHECK_STACK_SPACE, ( --)) { assert(corresponding_check_stack == NULL); corresponding_check_stack = this_instr; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 4102d00171fbaf..0b696921ebfc9f 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -684,6 +684,13 @@ /* _INSTRUMENTED_YIELD_VALUE is not a viable micro-op for tier 2 */ + case _YIELD_VALUE: { + _Py_UopsSymbol *res; + OUT_OF_SPACE_IF_NULL(res = sym_new_unknown(ctx)); + stack_pointer[-1] = res; + break; + } + case _POP_EXCEPT: { stack_pointer += -1; break; @@ -1440,11 +1447,8 @@ } case _FOR_ITER_GEN_FRAME: { - _PyInterpreterFrame *gen_frame; - gen_frame = sym_new_not_null(ctx); - if (gen_frame == NULL) goto out_of_space; - stack_pointer[0] = (_Py_UopsSymbol *)gen_frame; - stack_pointer += 1; + /* We are about to hit the end of the trace */ + goto done; break; } From 11f8348d78c22f85694d7a424541b34d6054a8ee Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 30 Apr 2024 15:04:16 +0300 Subject: [PATCH 107/217] gh-118404: Fix inspect.signature() for non-comparable callables (GH-118405) --- Lib/inspect.py | 6 ++++-- Lib/test/test_inspect/test_inspect.py | 10 ++++++++++ .../2024-04-29-22-11-54.gh-issue-118404.GYfMaD.rst | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-29-22-11-54.gh-issue-118404.GYfMaD.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index 1f4216f0389d28..d46514f4b10467 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2179,8 +2179,10 @@ def _signature_is_builtin(obj): ismethoddescriptor(obj) or isinstance(obj, _NonUserDefinedCallables) or # Can't test 'isinstance(type)' here, as it would - # also be True for regular python classes - obj in (type, object)) + # also be True for regular python classes. + # Can't use the `in` operator here, as it would + # invoke the custom __eq__ method. + obj is type or obj is object) def _signature_is_functionlike(obj): diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 6b577090bdff68..fbef34eddacb3a 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4690,6 +4690,16 @@ class D2(D1): self.assertEqual(inspect.signature(D2), inspect.signature(D1)) + def test_signature_on_non_comparable(self): + class NoncomparableCallable: + def __call__(self, a): + pass + def __eq__(self, other): + 1/0 + self.assertEqual(self.signature(NoncomparableCallable()), + ((('a', ..., ..., 'positional_or_keyword'),), + ...)) + class TestParameterObject(unittest.TestCase): def test_signature_parameter_kinds(self): diff --git a/Misc/NEWS.d/next/Library/2024-04-29-22-11-54.gh-issue-118404.GYfMaD.rst b/Misc/NEWS.d/next/Library/2024-04-29-22-11-54.gh-issue-118404.GYfMaD.rst new file mode 100644 index 00000000000000..b8f9ee061ac164 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-29-22-11-54.gh-issue-118404.GYfMaD.rst @@ -0,0 +1 @@ +Fix :func:`inspect.signature` for non-comparable callables. From 3b268f4edc02b22257d745363b5cae199b6e5720 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Tue, 30 Apr 2024 15:00:31 +0100 Subject: [PATCH 108/217] gh-116622: Redirect stdout and stderr to system log when embedded in an Android app (#118063) --- Lib/_android_support.py | 94 +++++ Lib/test/test_android.py | 332 ++++++++++++++++++ ...-04-17-22-49-15.gh-issue-116622.tthNUF.rst | 1 + Python/pylifecycle.c | 77 ++++ Python/stdlib_module_names.h | 1 + configure | 3 + configure.ac | 3 + 7 files changed, 511 insertions(+) create mode 100644 Lib/_android_support.py create mode 100644 Lib/test/test_android.py create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-17-22-49-15.gh-issue-116622.tthNUF.rst diff --git a/Lib/_android_support.py b/Lib/_android_support.py new file mode 100644 index 00000000000000..590e85ea8c2db1 --- /dev/null +++ b/Lib/_android_support.py @@ -0,0 +1,94 @@ +import io +import sys + + +# The maximum length of a log message in bytes, including the level marker and +# tag, is defined as LOGGER_ENTRY_MAX_PAYLOAD in +# platform/system/logging/liblog/include/log/log.h. As of API level 30, messages +# longer than this will be be truncated by logcat. This limit has already been +# reduced at least once in the history of Android (from 4076 to 4068 between API +# level 23 and 26), so leave some headroom. +MAX_BYTES_PER_WRITE = 4000 + +# UTF-8 uses a maximum of 4 bytes per character, so limiting text writes to this +# size ensures that TextIOWrapper can always avoid exceeding MAX_BYTES_PER_WRITE. +# However, if the actual number of bytes per character is smaller than that, +# then TextIOWrapper may still join multiple consecutive text writes into binary +# writes containing a larger number of characters. +MAX_CHARS_PER_WRITE = MAX_BYTES_PER_WRITE // 4 + + +# When embedded in an app on current versions of Android, there's no easy way to +# monitor the C-level stdout and stderr. The testbed comes with a .c file to +# redirect them to the system log using a pipe, but that wouldn't be convenient +# or appropriate for all apps. So we redirect at the Python level instead. +def init_streams(android_log_write, stdout_prio, stderr_prio): + if sys.executable: + return # Not embedded in an app. + + sys.stdout = TextLogStream( + android_log_write, stdout_prio, "python.stdout", errors=sys.stdout.errors) + sys.stderr = TextLogStream( + android_log_write, stderr_prio, "python.stderr", errors=sys.stderr.errors) + + +class TextLogStream(io.TextIOWrapper): + def __init__(self, android_log_write, prio, tag, **kwargs): + kwargs.setdefault("encoding", "UTF-8") + kwargs.setdefault("line_buffering", True) + super().__init__(BinaryLogStream(android_log_write, prio, tag), **kwargs) + self._CHUNK_SIZE = MAX_BYTES_PER_WRITE + + def __repr__(self): + return f"" + + def write(self, s): + if not isinstance(s, str): + raise TypeError( + f"write() argument must be str, not {type(s).__name__}") + + # In case `s` is a str subclass that writes itself to stdout or stderr + # when we call its methods, convert it to an actual str. + s = str.__str__(s) + + # We want to emit one log message per line wherever possible, so split + # the string before sending it to the superclass. Note that + # "".splitlines() == [], so nothing will be logged for an empty string. + for line in s.splitlines(keepends=True): + while line: + super().write(line[:MAX_CHARS_PER_WRITE]) + line = line[MAX_CHARS_PER_WRITE:] + + return len(s) + + +class BinaryLogStream(io.RawIOBase): + def __init__(self, android_log_write, prio, tag): + self.android_log_write = android_log_write + self.prio = prio + self.tag = tag + + def __repr__(self): + return f"" + + def writable(self): + return True + + def write(self, b): + if type(b) is not bytes: + try: + b = bytes(memoryview(b)) + except TypeError: + raise TypeError( + f"write() argument must be bytes-like, not {type(b).__name__}" + ) from None + + # Writing an empty string to the stream should have no effect. + if b: + # Encode null bytes using "modified UTF-8" to avoid truncating the + # message. This should not affect the return value, as the caller + # may be expecting it to match the length of the input. + self.android_log_write(self.prio, self.tag, + b.replace(b"\x00", b"\xc0\x80")) + + return len(b) diff --git a/Lib/test/test_android.py b/Lib/test/test_android.py new file mode 100644 index 00000000000000..115882a4c281f6 --- /dev/null +++ b/Lib/test/test_android.py @@ -0,0 +1,332 @@ +import platform +import queue +import re +import subprocess +import sys +import unittest +from array import array +from contextlib import contextmanager +from threading import Thread +from test.support import LOOPBACK_TIMEOUT +from time import time + + +if sys.platform != "android": + raise unittest.SkipTest("Android-specific") + +api_level = platform.android_ver().api_level + + +# Test redirection of stdout and stderr to the Android log. +@unittest.skipIf( + api_level < 23 and platform.machine() == "aarch64", + "SELinux blocks reading logs on older ARM64 emulators" +) +class TestAndroidOutput(unittest.TestCase): + maxDiff = None + + def setUp(self): + self.logcat_process = subprocess.Popen( + ["logcat", "-v", "tag"], stdout=subprocess.PIPE, + errors="backslashreplace" + ) + self.logcat_queue = queue.Queue() + + def logcat_thread(): + for line in self.logcat_process.stdout: + self.logcat_queue.put(line.rstrip("\n")) + self.logcat_process.stdout.close() + Thread(target=logcat_thread).start() + + from ctypes import CDLL, c_char_p, c_int + android_log_write = getattr(CDLL("liblog.so"), "__android_log_write") + android_log_write.argtypes = (c_int, c_char_p, c_char_p) + ANDROID_LOG_INFO = 4 + + # Separate tests using a marker line with a different tag. + tag, message = "python.test", f"{self.id()} {time()}" + android_log_write( + ANDROID_LOG_INFO, tag.encode("UTF-8"), message.encode("UTF-8")) + self.assert_log("I", tag, message, skip=True, timeout=5) + + def assert_logs(self, level, tag, expected, **kwargs): + for line in expected: + self.assert_log(level, tag, line, **kwargs) + + def assert_log(self, level, tag, expected, *, skip=False, timeout=0.5): + deadline = time() + timeout + while True: + try: + line = self.logcat_queue.get(timeout=(deadline - time())) + except queue.Empty: + self.fail(f"line not found: {expected!r}") + if match := re.fullmatch(fr"(.)/{tag}: (.*)", line): + try: + self.assertEqual(level, match[1]) + self.assertEqual(expected, match[2]) + break + except AssertionError: + if not skip: + raise + + def tearDown(self): + self.logcat_process.terminate() + self.logcat_process.wait(LOOPBACK_TIMEOUT) + + @contextmanager + def unbuffered(self, stream): + stream.reconfigure(write_through=True) + try: + yield + finally: + stream.reconfigure(write_through=False) + + def test_str(self): + for stream_name, level in [("stdout", "I"), ("stderr", "W")]: + with self.subTest(stream=stream_name): + stream = getattr(sys, stream_name) + tag = f"python.{stream_name}" + self.assertEqual(f"", repr(stream)) + + self.assertTrue(stream.writable()) + self.assertFalse(stream.readable()) + self.assertEqual("UTF-8", stream.encoding) + self.assertTrue(stream.line_buffering) + self.assertFalse(stream.write_through) + + # stderr is backslashreplace by default; stdout is configured + # that way by libregrtest.main. + self.assertEqual("backslashreplace", stream.errors) + + def write(s, lines=None, *, write_len=None): + if write_len is None: + write_len = len(s) + self.assertEqual(write_len, stream.write(s)) + if lines is None: + lines = [s] + self.assert_logs(level, tag, lines) + + # Single-line messages, + with self.unbuffered(stream): + write("", []) + + write("a") + write("Hello") + write("Hello world") + write(" ") + write(" ") + + # Non-ASCII text + write("ol\u00e9") # Spanish + write("\u4e2d\u6587") # Chinese + + # Non-BMP emoji + write("\U0001f600") + + # Non-encodable surrogates + write("\ud800\udc00", [r"\ud800\udc00"]) + + # Code used by surrogateescape (which isn't enabled here) + write("\udc80", [r"\udc80"]) + + # Null characters are logged using "modified UTF-8". + write("\u0000", [r"\xc0\x80"]) + write("a\u0000", [r"a\xc0\x80"]) + write("\u0000b", [r"\xc0\x80b"]) + write("a\u0000b", [r"a\xc0\x80b"]) + + # Multi-line messages. Avoid identical consecutive lines, as + # they may activate "chatty" filtering and break the tests. + write("\nx", [""]) + write("\na\n", ["x", "a"]) + write("\n", [""]) + write("b\n", ["b"]) + write("c\n\n", ["c", ""]) + write("d\ne", ["d"]) + write("xx", []) + write("f\n\ng", ["exxf", ""]) + write("\n", ["g"]) + + with self.unbuffered(stream): + write("\nx", ["", "x"]) + write("\na\n", ["", "a"]) + write("\n", [""]) + write("b\n", ["b"]) + write("c\n\n", ["c", ""]) + write("d\ne", ["d", "e"]) + write("xx", ["xx"]) + write("f\n\ng", ["f", "", "g"]) + write("\n", [""]) + + # "\r\n" should be translated into "\n". + write("hello\r\n", ["hello"]) + write("hello\r\nworld\r\n", ["hello", "world"]) + write("\r\n", [""]) + + # Non-standard line separators should be preserved. + write("before form feed\x0cafter form feed\n", + ["before form feed\x0cafter form feed"]) + write("before line separator\u2028after line separator\n", + ["before line separator\u2028after line separator"]) + + # String subclasses are accepted, but they should be converted + # to a standard str without calling any of their methods. + class CustomStr(str): + def splitlines(self, *args, **kwargs): + raise AssertionError() + + def __len__(self): + raise AssertionError() + + def __str__(self): + raise AssertionError() + + write(CustomStr("custom\n"), ["custom"], write_len=7) + + # Non-string classes are not accepted. + for obj in [b"", b"hello", None, 42]: + with self.subTest(obj=obj): + with self.assertRaisesRegex( + TypeError, + fr"write\(\) argument must be str, not " + fr"{type(obj).__name__}" + ): + stream.write(obj) + + # Manual flushing is supported. + write("hello", []) + stream.flush() + self.assert_log(level, tag, "hello") + write("hello", []) + write("world", []) + stream.flush() + self.assert_log(level, tag, "helloworld") + + # Long lines are split into blocks of 1000 characters + # (MAX_CHARS_PER_WRITE in _android_support.py), but + # TextIOWrapper should then join them back together as much as + # possible without exceeding 4000 UTF-8 bytes + # (MAX_BYTES_PER_WRITE). + # + # ASCII (1 byte per character) + write(("foobar" * 700) + "\n", + [("foobar" * 666) + "foob", # 4000 bytes + "ar" + ("foobar" * 33)]) # 200 bytes + + # "Full-width" digits 0-9 (3 bytes per character) + s = "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19" + write((s * 150) + "\n", + [s * 100, # 3000 bytes + s * 50]) # 1500 bytes + + s = "0123456789" + write(s * 200, []) + write(s * 150, []) + write(s * 51, [s * 350]) # 3500 bytes + write("\n", [s * 51]) # 510 bytes + + def test_bytes(self): + for stream_name, level in [("stdout", "I"), ("stderr", "W")]: + with self.subTest(stream=stream_name): + stream = getattr(sys, stream_name).buffer + tag = f"python.{stream_name}" + self.assertEqual(f"", repr(stream)) + self.assertTrue(stream.writable()) + self.assertFalse(stream.readable()) + + def write(b, lines=None, *, write_len=None): + if write_len is None: + write_len = len(b) + self.assertEqual(write_len, stream.write(b)) + if lines is None: + lines = [b.decode()] + self.assert_logs(level, tag, lines) + + # Single-line messages, + write(b"", []) + + write(b"a") + write(b"Hello") + write(b"Hello world") + write(b" ") + write(b" ") + + # Non-ASCII text + write(b"ol\xc3\xa9") # Spanish + write(b"\xe4\xb8\xad\xe6\x96\x87") # Chinese + + # Non-BMP emoji + write(b"\xf0\x9f\x98\x80") + + # Null bytes are logged using "modified UTF-8". + write(b"\x00", [r"\xc0\x80"]) + write(b"a\x00", [r"a\xc0\x80"]) + write(b"\x00b", [r"\xc0\x80b"]) + write(b"a\x00b", [r"a\xc0\x80b"]) + + # Invalid UTF-8 + write(b"\xff", [r"\xff"]) + write(b"a\xff", [r"a\xff"]) + write(b"\xffb", [r"\xffb"]) + write(b"a\xffb", [r"a\xffb"]) + + # Log entries containing newlines are shown differently by + # `logcat -v tag`, `logcat -v long`, and Android Studio. We + # currently use `logcat -v tag`, which shows each line as if it + # was a separate log entry, but strips a single trailing + # newline. + # + # On newer versions of Android, all three of the above tools (or + # maybe Logcat itself) will also strip any number of leading + # newlines. + write(b"\nx", ["", "x"] if api_level < 30 else ["x"]) + write(b"\na\n", ["", "a"] if api_level < 30 else ["a"]) + write(b"\n", [""]) + write(b"b\n", ["b"]) + write(b"c\n\n", ["c", ""]) + write(b"d\ne", ["d", "e"]) + write(b"xx", ["xx"]) + write(b"f\n\ng", ["f", "", "g"]) + write(b"\n", [""]) + + # "\r\n" should be translated into "\n". + write(b"hello\r\n", ["hello"]) + write(b"hello\r\nworld\r\n", ["hello", "world"]) + write(b"\r\n", [""]) + + # Other bytes-like objects are accepted. + write(bytearray(b"bytearray")) + + mv = memoryview(b"memoryview") + write(mv, ["memoryview"]) # Continuous + write(mv[::2], ["mmrve"]) # Discontinuous + + write( + # Android only supports little-endian architectures, so the + # bytes representation is as follows: + array("H", [ + 0, # 00 00 + 1, # 01 00 + 65534, # FE FF + 65535, # FF FF + ]), + + # After encoding null bytes with modified UTF-8, the only + # valid UTF-8 sequence is \x01. All other bytes are handled + # by backslashreplace. + ["\\xc0\\x80\\xc0\\x80" + "\x01\\xc0\\x80" + "\\xfe\\xff" + "\\xff\\xff"], + write_len=8, + ) + + # Non-bytes-like classes are not accepted. + for obj in ["", "hello", None, 42]: + with self.subTest(obj=obj): + with self.assertRaisesRegex( + TypeError, + fr"write\(\) argument must be bytes-like, not " + fr"{type(obj).__name__}" + ): + stream.write(obj) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-17-22-49-15.gh-issue-116622.tthNUF.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-17-22-49-15.gh-issue-116622.tthNUF.rst new file mode 100644 index 00000000000000..04f84792f667a0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-17-22-49-15.gh-issue-116622.tthNUF.rst @@ -0,0 +1 @@ +Redirect stdout and stderr to system log when embedded in an Android app. diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 0f3ca4a6687514..a672d8c7915ccd 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -71,6 +71,9 @@ static PyStatus add_main_module(PyInterpreterState *interp); static PyStatus init_import_site(void); static PyStatus init_set_builtins_open(void); static PyStatus init_sys_streams(PyThreadState *tstate); +#ifdef __ANDROID__ +static PyStatus init_android_streams(PyThreadState *tstate); +#endif static void wait_for_thread_shutdown(PyThreadState *tstate); static void call_ll_exitfuncs(_PyRuntimeState *runtime); @@ -1223,6 +1226,13 @@ init_interp_main(PyThreadState *tstate) return status; } +#ifdef __ANDROID__ + status = init_android_streams(tstate); + if (_PyStatus_EXCEPTION(status)) { + return status; + } +#endif + #ifdef Py_DEBUG run_presite(tstate); #endif @@ -2719,6 +2729,73 @@ init_sys_streams(PyThreadState *tstate) } +#ifdef __ANDROID__ +#include + +static PyObject * +android_log_write_impl(PyObject *self, PyObject *args) +{ + int prio = 0; + const char *tag = NULL; + const char *text = NULL; + if (!PyArg_ParseTuple(args, "isy", &prio, &tag, &text)) { + return NULL; + } + + // Despite its name, this function is part of the public API + // (https://developer.android.com/ndk/reference/group/logging). + __android_log_write(prio, tag, text); + Py_RETURN_NONE; +} + + +static PyMethodDef android_log_write_method = { + "android_log_write", android_log_write_impl, METH_VARARGS +}; + + +static PyStatus +init_android_streams(PyThreadState *tstate) +{ + PyStatus status = _PyStatus_OK(); + PyObject *_android_support = NULL; + PyObject *android_log_write = NULL; + PyObject *result = NULL; + + _android_support = PyImport_ImportModule("_android_support"); + if (_android_support == NULL) { + goto error; + } + + android_log_write = PyCFunction_New(&android_log_write_method, NULL); + if (android_log_write == NULL) { + goto error; + } + + // These log priorities match those used by Java's System.out and System.err. + result = PyObject_CallMethod( + _android_support, "init_streams", "Oii", + android_log_write, ANDROID_LOG_INFO, ANDROID_LOG_WARN); + if (result == NULL) { + goto error; + } + + goto done; + +error: + _PyErr_Print(tstate); + status = _PyStatus_ERR("failed to initialize Android streams"); + +done: + Py_XDECREF(result); + Py_XDECREF(android_log_write); + Py_XDECREF(_android_support); + return status; +} + +#endif // __ANDROID__ + + static void _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, PyThreadState *tstate) diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 08a66f447e2258..f44abf1f9c3121 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -5,6 +5,7 @@ static const char* _Py_stdlib_module_names[] = { "__future__", "_abc", "_aix_support", +"_android_support", "_ast", "_asyncio", "_bisect", diff --git a/configure b/configure index 571ab8c882aa68..e1d0f36463c44b 100755 --- a/configure +++ b/configure @@ -7103,6 +7103,9 @@ printf "%s\n" "$ANDROID_API_LEVEL" >&6; } printf "%s\n" "#define ANDROID_API_LEVEL $ANDROID_API_LEVEL" >>confdefs.h + # For __android_log_write() in Python/pylifecycle.c. + LIBS="$LIBS -llog" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the Android arm ABI" >&5 printf %s "checking for the Android arm ABI... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $_arm_arch" >&5 diff --git a/configure.ac b/configure.ac index a2d6b1357efbc9..7681ea3d3fe3c6 100644 --- a/configure.ac +++ b/configure.ac @@ -1192,6 +1192,9 @@ if $CPP $CPPFLAGS conftest.c >conftest.out 2>/dev/null; then AC_DEFINE_UNQUOTED([ANDROID_API_LEVEL], [$ANDROID_API_LEVEL], [The Android API level.]) + # For __android_log_write() in Python/pylifecycle.c. + LIBS="$LIBS -llog" + AC_MSG_CHECKING([for the Android arm ABI]) AC_MSG_RESULT([$_arm_arch]) if test "$_arm_arch" = 7; then From c0eaa232f63a62e0e0408911ab5f118dca2af607 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 30 Apr 2024 17:23:44 +0300 Subject: [PATCH 109/217] gh-117860: Add tests for resolving names when import rebind names (GH-118176) Add tests for "import", pkgutil.resolve_name() and unittest.mock.path() for cases when "import a.b as x" and "from a import b as x" give different results. --- Lib/test/test_import/__init__.py | 28 ++++++++ .../test_import/data/package3/__init__.py | 2 + .../test_import/data/package3/submodule.py | 7 ++ .../test_import/data/package4/__init__.py | 5 ++ .../test_import/data/package4/submodule.py | 3 + Lib/test/test_pkgutil.py | 35 ++++++++++ Lib/test/test_unittest/testmock/testpatch.py | 67 +++++++++++++++++++ Makefile.pre.in | 2 + 8 files changed, 149 insertions(+) create mode 100644 Lib/test/test_import/data/package3/__init__.py create mode 100644 Lib/test/test_import/data/package3/submodule.py create mode 100644 Lib/test/test_import/data/package4/__init__.py create mode 100644 Lib/test/test_import/data/package4/submodule.py diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 40c3023a827067..f88b51f916cc81 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1327,6 +1327,34 @@ def test_import_from_unloaded_package(self): import package2.submodule1 package2.submodule1.submodule2 + def test_rebinding(self): + # The same data is also used for testing pkgutil.resolve_name() + # in test_pkgutil and mock.patch in test_unittest. + path = os.path.join(os.path.dirname(__file__), 'data') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + from package3 import submodule + self.assertEqual(submodule.attr, 'rebound') + import package3.submodule as submodule + self.assertEqual(submodule.attr, 'rebound') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + import package3.submodule as submodule + self.assertEqual(submodule.attr, 'rebound') + from package3 import submodule + self.assertEqual(submodule.attr, 'rebound') + + def test_rebinding2(self): + path = os.path.join(os.path.dirname(__file__), 'data') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + import package4.submodule as submodule + self.assertEqual(submodule.attr, 'submodule') + from package4 import submodule + self.assertEqual(submodule.attr, 'submodule') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + from package4 import submodule + self.assertEqual(submodule.attr, 'origin') + import package4.submodule as submodule + self.assertEqual(submodule.attr, 'submodule') + class OverridingImportBuiltinTests(unittest.TestCase): def test_override_builtin(self): diff --git a/Lib/test/test_import/data/package3/__init__.py b/Lib/test/test_import/data/package3/__init__.py new file mode 100644 index 00000000000000..7033c22a719ec4 --- /dev/null +++ b/Lib/test/test_import/data/package3/__init__.py @@ -0,0 +1,2 @@ +"""Rebinding the package attribute after importing the module.""" +from .submodule import submodule diff --git a/Lib/test/test_import/data/package3/submodule.py b/Lib/test/test_import/data/package3/submodule.py new file mode 100644 index 00000000000000..cd7b30db15e449 --- /dev/null +++ b/Lib/test/test_import/data/package3/submodule.py @@ -0,0 +1,7 @@ +attr = 'submodule' +class A: + attr = 'submodule' +class submodule: + attr = 'rebound' + class B: + attr = 'rebound' diff --git a/Lib/test/test_import/data/package4/__init__.py b/Lib/test/test_import/data/package4/__init__.py new file mode 100644 index 00000000000000..d8af60ab38a319 --- /dev/null +++ b/Lib/test/test_import/data/package4/__init__.py @@ -0,0 +1,5 @@ +"""Binding the package attribute without importing the module.""" +class submodule: + attr = 'origin' + class B: + attr = 'origin' diff --git a/Lib/test/test_import/data/package4/submodule.py b/Lib/test/test_import/data/package4/submodule.py new file mode 100644 index 00000000000000..c861417aece9c3 --- /dev/null +++ b/Lib/test/test_import/data/package4/submodule.py @@ -0,0 +1,3 @@ +attr = 'submodule' +class A: + attr = 'submodule' diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index 6fcd726345eeac..e19dce1dbd2583 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -12,6 +12,9 @@ import shutil import zipfile +from test.support.import_helper import DirsOnSysPath +from test.test_importlib.util import uncache + # Note: pkgutil.walk_packages is currently tested in test_runpy. This is # a hack to get a major issue resolved for 3.3b2. Longer term, it should # be moved back here, perhaps by factoring out the helper code for @@ -318,6 +321,38 @@ def test_name_resolution(self): with self.assertRaises(exc): pkgutil.resolve_name(s) + def test_name_resolution_import_rebinding(self): + # The same data is also used for testing import in test_import and + # mock.patch in test_unittest. + path = os.path.join(os.path.dirname(__file__), 'test_import', 'data') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + self.assertEqual(pkgutil.resolve_name('package3.submodule.attr'), 'submodule') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + self.assertEqual(pkgutil.resolve_name('package3.submodule:attr'), 'submodule') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + self.assertEqual(pkgutil.resolve_name('package3:submodule.attr'), 'rebound') + self.assertEqual(pkgutil.resolve_name('package3.submodule.attr'), 'submodule') + self.assertEqual(pkgutil.resolve_name('package3:submodule.attr'), 'rebound') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + self.assertEqual(pkgutil.resolve_name('package3:submodule.attr'), 'rebound') + self.assertEqual(pkgutil.resolve_name('package3.submodule:attr'), 'submodule') + self.assertEqual(pkgutil.resolve_name('package3:submodule.attr'), 'rebound') + + def test_name_resolution_import_rebinding2(self): + path = os.path.join(os.path.dirname(__file__), 'test_import', 'data') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + self.assertEqual(pkgutil.resolve_name('package4.submodule.attr'), 'submodule') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + self.assertEqual(pkgutil.resolve_name('package4.submodule:attr'), 'submodule') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + self.assertEqual(pkgutil.resolve_name('package4:submodule.attr'), 'origin') + self.assertEqual(pkgutil.resolve_name('package4.submodule.attr'), 'submodule') + self.assertEqual(pkgutil.resolve_name('package4:submodule.attr'), 'submodule') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + self.assertEqual(pkgutil.resolve_name('package4:submodule.attr'), 'origin') + self.assertEqual(pkgutil.resolve_name('package4.submodule:attr'), 'submodule') + self.assertEqual(pkgutil.resolve_name('package4:submodule.attr'), 'submodule') + class PkgutilPEP302Tests(unittest.TestCase): diff --git a/Lib/test/test_unittest/testmock/testpatch.py b/Lib/test/test_unittest/testmock/testpatch.py index d0046d702a53f4..be75fda7826af1 100644 --- a/Lib/test/test_unittest/testmock/testpatch.py +++ b/Lib/test/test_unittest/testmock/testpatch.py @@ -7,9 +7,11 @@ from collections import OrderedDict import unittest +import test from test.test_unittest.testmock import support from test.test_unittest.testmock.support import SomeClass, is_instance +from test.support.import_helper import DirsOnSysPath from test.test_importlib.util import uncache from unittest.mock import ( NonCallableMock, CallableMixin, sentinel, @@ -1728,6 +1730,71 @@ def test(mock): 'exception traceback not propagated') + def test_name_resolution_import_rebinding(self): + # Currently mock.patch uses pkgutil.resolve_name(), but repeat + # similar tests just for the case. + # The same data is also used for testing import in test_import and + # pkgutil.resolve_name() in test_pkgutil. + path = os.path.join(os.path.dirname(test.__file__), 'test_import', 'data') + def check(name): + p = patch(name) + p.start() + p.stop() + def check_error(name): + p = patch(name) + self.assertRaises(AttributeError, p.start) + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + check('package3.submodule.A.attr') + check_error('package3.submodule.B.attr') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + check('package3.submodule:A.attr') + check_error('package3.submodule:B.attr') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + check('package3:submodule.B.attr') + check_error('package3:submodule.A.attr') + check('package3.submodule.A.attr') + check_error('package3.submodule.B.attr') + check('package3:submodule.B.attr') + check_error('package3:submodule.A.attr') + with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): + check('package3:submodule.B.attr') + check_error('package3:submodule.A.attr') + check('package3.submodule:A.attr') + check_error('package3.submodule:B.attr') + check('package3:submodule.B.attr') + check_error('package3:submodule.A.attr') + + def test_name_resolution_import_rebinding2(self): + path = os.path.join(os.path.dirname(test.__file__), 'test_import', 'data') + def check(name): + p = patch(name) + p.start() + p.stop() + def check_error(name): + p = patch(name) + self.assertRaises(AttributeError, p.start) + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + check('package4.submodule.A.attr') + check_error('package4.submodule.B.attr') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + check('package4.submodule:A.attr') + check_error('package4.submodule:B.attr') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + check('package4:submodule.B.attr') + check_error('package4:submodule.A.attr') + check('package4.submodule.A.attr') + check_error('package4.submodule.B.attr') + check('package4:submodule.A.attr') + check_error('package4:submodule.B.attr') + with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): + check('package4:submodule.B.attr') + check_error('package4:submodule.A.attr') + check('package4.submodule:A.attr') + check_error('package4.submodule:B.attr') + check('package4:submodule.A.attr') + check_error('package4:submodule.B.attr') + + def test_create_and_specs(self): for kwarg in ('spec', 'spec_set', 'autospec'): p = patch('%s.doesnotexist' % __name__, create=True, diff --git a/Makefile.pre.in b/Makefile.pre.in index 0e52e10602cf85..4e4d01d14ee3cb 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2380,6 +2380,8 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_import/data/circular_imports/subpkg2/parent \ test/test_import/data/package \ test/test_import/data/package2 \ + test/test_import/data/package3 \ + test/test_import/data/package4 \ test/test_import/data/unwritable \ test/test_importlib \ test/test_importlib/builtin \ From 02887c6428042832f17fd71ed90619f25d8f951d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 30 Apr 2024 17:35:19 +0300 Subject: [PATCH 110/217] gh-102402: Make test_relativeCreated_has_higher_precision less implementation dependent (GH-118062) --- Lib/test/test_logging.py | 60 ++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index d8ae607d1d8d01..e651d96f100a83 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4598,25 +4598,55 @@ def test_msecs_has_no_floating_point_precision_loss(self): self.assertEqual(record.msecs, want) self.assertEqual(record.created, ns / 1e9) - # The test overrides a private attribute - @support.cpython_only def test_relativeCreated_has_higher_precision(self): - # See issue gh-102402 - ns = 1_677_903_920_000_998_503 # approx. 2023-03-04 04:25:20 UTC + # See issue gh-102402. + # Run the code in the subprocess, because the time module should + # be patched before the first import of the logging package. + # Temporary unloading and re-importing the logging package has + # side effects (including registering the atexit callback and + # references leak). + start_ns = 1_677_903_920_000_998_503 # approx. 2023-03-04 04:25:20 UTC offsets_ns = (200, 500, 12_354, 99_999, 1_677_903_456_999_123_456) + code = textwrap.dedent(f""" + start_ns = {start_ns!r} + offsets_ns = {offsets_ns!r} + start_monotonic_ns = start_ns - 1 + + import time + # Only time.time_ns needs to be patched for the current + # implementation, but patch also other functions to make + # the test less implementation depending. + old_time_ns = time.time_ns + old_time = time.time + old_monotonic_ns = time.monotonic_ns + old_monotonic = time.monotonic + time_ns_result = start_ns + time.time_ns = lambda: time_ns_result + time.time = lambda: time.time_ns()/1e9 + time.monotonic_ns = lambda: time_ns_result - start_monotonic_ns + time.monotonic = lambda: time.monotonic_ns()/1e9 + try: + import logging - with (patch("time.time_ns") as time_ns_mock, - support.swap_attr(logging, '_startTime', ns)): - for offset_ns in offsets_ns: - # mock for log record creation - new_ns = ns + offset_ns - time_ns_mock.return_value = new_ns - - record = logging.makeLogRecord({'msg': 'test'}) - self.assertAlmostEqual(record.created, new_ns / 1e9, places=6) - + for offset_ns in offsets_ns: + # mock for log record creation + time_ns_result = start_ns + offset_ns + record = logging.makeLogRecord({{'msg': 'test'}}) + print(record.created, record.relativeCreated) + finally: + time.time_ns = old_time_ns + time.time = old_time + time.monotonic_ns = old_monotonic_ns + time.monotonic = old_monotonic + """) + rc, out, err = assert_python_ok("-c", code) + out = out.decode() + for offset_ns, line in zip(offsets_ns, out.splitlines(), strict=True): + with self.subTest(offset_ns=offset_ns): + created, relativeCreated = map(float, line.split()) + self.assertAlmostEqual(created, (start_ns + offset_ns) / 1e9, places=6) # After PR gh-102412, precision (places) increases from 3 to 7 - self.assertAlmostEqual(record.relativeCreated, offset_ns / 1e6, places=7) + self.assertAlmostEqual(relativeCreated, offset_ns / 1e6, places=7) class TestBufferingFormatter(logging.BufferingFormatter): From 17a8af9508d6001e1666de984e2fb73f02167306 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 30 Apr 2024 17:49:28 +0300 Subject: [PATCH 111/217] gh-118402: Fix inspect.signature() for functools.cmp_to_key() result (GH-118427) --- Lib/test/test_functools.py | 12 +++++++++--- .../2024-04-29-21-51-28.gh-issue-118402.Z_06Th.rst | 2 ++ Modules/_functoolsmodule.c | 12 ++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-29-21-51-28.gh-issue-118402.Z_06Th.rst diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index bb4c7cc8701fb4..4a9a7313712f60 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -947,8 +947,13 @@ def mycmp(x, y): @unittest.skipIf(support.MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") def test_cmp_to_signature(self): - self.assertEqual(str(Signature.from_callable(self.cmp_to_key)), - '(mycmp)') + sig = Signature.from_callable(self.cmp_to_key) + self.assertEqual(str(sig), '(mycmp)') + def mycmp(x, y): + return y - x + sig = Signature.from_callable(self.cmp_to_key(mycmp)) + self.assertEqual(str(sig), '(obj)') + @unittest.skipUnless(c_functools, 'requires the C _functools module') @@ -1864,9 +1869,10 @@ def test_staticmethod(x): self.assertIsNone(ref()) def test_common_signatures(self): - def orig(): ... + def orig(a, /, b, c=True): ... lru = self.module.lru_cache(1)(orig) + self.assertEqual(str(Signature.from_callable(lru)), '(a, /, b, c=True)') self.assertEqual(str(Signature.from_callable(lru.cache_info)), '()') self.assertEqual(str(Signature.from_callable(lru.cache_clear)), '()') diff --git a/Misc/NEWS.d/next/Library/2024-04-29-21-51-28.gh-issue-118402.Z_06Th.rst b/Misc/NEWS.d/next/Library/2024-04-29-21-51-28.gh-issue-118402.Z_06Th.rst new file mode 100644 index 00000000000000..25d7b45b8726c9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-29-21-51-28.gh-issue-118402.Z_06Th.rst @@ -0,0 +1,2 @@ +Fix :func:`inspect.signature` for the result of the +:func:`functools.cmp_to_key` call. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 406fcf0da2f7e4..e37473a582b55f 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -571,6 +571,17 @@ static PyMemberDef keyobject_members[] = { {NULL} }; +static PyObject * +keyobject_text_signature(PyObject *self, void *Py_UNUSED(ignored)) +{ + return PyUnicode_FromString("(obj)"); +} + +static PyGetSetDef keyobject_getset[] = { + {"__text_signature__", keyobject_text_signature, (setter)NULL}, + {NULL} +}; + static PyObject * keyobject_call(keyobject *ko, PyObject *args, PyObject *kwds); @@ -585,6 +596,7 @@ static PyType_Slot keyobject_type_slots[] = { {Py_tp_clear, keyobject_clear}, {Py_tp_richcompare, keyobject_richcompare}, {Py_tp_members, keyobject_members}, + {Py_tp_getset, keyobject_getset}, {0, 0} }; From 9a75d56d5d9fdffb6ce9d83ede98486df238102d Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Tue, 30 Apr 2024 16:55:15 +0200 Subject: [PATCH 112/217] gh-118379: Use PyTuple_Pack instead of Py_BuildValue if possible (GH-118381) --- Modules/_decimal/_decimal.c | 4 ++-- Modules/_testbuffer.c | 2 +- Modules/_testcapimodule.c | 4 ++-- Modules/_testclinic.c | 2 +- Modules/posixmodule.c | 2 +- Python/errors.c | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index c105367a270458..fe6711143b845b 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -4287,7 +4287,7 @@ nm_mpd_qdivmod(PyObject *v, PyObject *w) return NULL; } - ret = Py_BuildValue("(OO)", q, r); + ret = PyTuple_Pack(2, q, r); Py_DECREF(r); Py_DECREF(q); return ret; @@ -5312,7 +5312,7 @@ ctx_mpd_qdivmod(PyObject *context, PyObject *args) return NULL; } - ret = Py_BuildValue("(OO)", q, r); + ret = PyTuple_Pack(2, q, r); Py_DECREF(r); Py_DECREF(q); return ret; diff --git a/Modules/_testbuffer.c b/Modules/_testbuffer.c index cad21bdb4d85ed..9e77744efad728 100644 --- a/Modules/_testbuffer.c +++ b/Modules/_testbuffer.c @@ -1217,7 +1217,7 @@ init_ndbuf(PyObject *items, PyObject *shape, PyObject *strides, /* convert scalar to list */ if (ndim == 0) { - items = Py_BuildValue("(O)", items); + items = PyTuple_Pack(1, items); if (items == NULL) return NULL; } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 0bdd252efdabc7..3448291e401e35 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2475,7 +2475,7 @@ get_basic_static_type(PyObject *self, PyObject *args) PyTypeObject *cls = &BasicStaticTypes[num_basic_static_types_used++]; if (base != NULL) { - cls->tp_bases = Py_BuildValue("(O)", base); + cls->tp_bases = PyTuple_Pack(1, base); if (cls->tp_bases == NULL) { return NULL; } @@ -3474,7 +3474,7 @@ typedef struct { static PyObject * ipowType_ipow(PyObject *self, PyObject *other, PyObject *mod) { - return Py_BuildValue("OO", other, mod); + return PyTuple_Pack(2, other, mod); } static PyNumberMethods ipowType_as_number = { diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 454173b434fb6b..2e9d00ac2189eb 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -1243,7 +1243,7 @@ _testclinic_TestClass_get_defining_class_arg_impl(PyObject *self, PyObject *arg) /*[clinic end generated code: output=fe7e49d96cbb7718 input=d1b83d3b853af6d9]*/ { - return Py_BuildValue("(OO)", cls, arg); + return PyTuple_Pack(2, cls, arg); } static struct PyMethodDef test_class_methods[] = { diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index c9d67ccbb8c908..722159a39d098f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5500,7 +5500,7 @@ os__path_splitroot_ex_impl(PyObject *module, PyObject *path) if (tail == NULL) { goto exit; } - result = Py_BuildValue("(OOO)", drv, root, tail); + result = PyTuple_Pack(3, drv, root, tail); exit: PyMem_Free(buffer); Py_XDECREF(drv); diff --git a/Python/errors.c b/Python/errors.c index e5f176a5dd208e..433253b8f9aada 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -632,8 +632,8 @@ _PyErr_StackItemToExcInfoTuple(_PyErr_StackItem *err_info) PyObject *exc_type = get_exc_type(exc_value); PyObject *exc_traceback = get_exc_traceback(exc_value); - return Py_BuildValue( - "(OOO)", + return PyTuple_Pack( + 3, exc_type ? exc_type : Py_None, exc_value ? exc_value : Py_None, exc_traceback ? exc_traceback : Py_None); From 72dae53e09a5344bf4922d934a34a2fa48a11c86 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Tue, 30 Apr 2024 10:05:05 -0500 Subject: [PATCH 113/217] gh-116122: Add SBOM generation to PCbuild/build.bat (GH-116138) --- PCbuild/regen.targets | 15 +++++++++++++-- Tools/build/generate_sbom.py | 21 ++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/PCbuild/regen.targets b/PCbuild/regen.targets index b72ef5b5c6055c..4aa14ed1fad9eb 100644 --- a/PCbuild/regen.targets +++ b/PCbuild/regen.targets @@ -33,11 +33,15 @@ <_JITOutputs Include="$(GeneratedPyConfigDir)jit_stencils.h"/> <_CasesSources Include="$(PySourcePath)Python\bytecodes.c;$(PySourcePath)Python\optimizer_bytecodes.c;"/> <_CasesOutputs Include="$(PySourcePath)Python\generated_cases.c.h;$(PySourcePath)Include\opcode_ids.h;$(PySourcePath)Include\internal\pycore_uop_ids.h;$(PySourcePath)Python\opcode_targets.h;$(PySourcePath)Include\internal\pycore_opcode_metadata.h;$(PySourcePath)Include\internal\pycore_uop_metadata.h;$(PySourcePath)Python\optimizer_cases.c.h;$(PySourcePath)Lib\_opcode_metadata.py"/> + <_SbomSources Include="$(PySourcePath)PCbuild\get_externals.bat" /> + <_SbomOutputs Include="$(PySourcePath)Misc\externals.spdx.json;$(PySourcePath)Misc\sbom.spdx.json"> + json + - @@ -126,7 +130,14 @@ DependsOnTargets="_TouchRegenSources;_RegenPegen;_RegenAST_H;_RegenTokens;_RegenKeywords;_RegenGlobalObjects"> - + + + + + diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index 5c1851f09338a0..258b58c03c6800 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -4,13 +4,13 @@ import hashlib import json import glob -import pathlib +from pathlib import Path, PurePosixPath, PureWindowsPath import subprocess import sys import urllib.request import typing -CPYTHON_ROOT_DIR = pathlib.Path(__file__).parent.parent.parent +CPYTHON_ROOT_DIR = Path(__file__).parent.parent.parent # Before adding a new entry to this list, double check that # the license expression is a valid SPDX license expression: @@ -119,9 +119,16 @@ def filter_gitignored_paths(paths: list[str]) -> list[str]: # 1 means matches, 0 means no matches. assert git_check_ignore_proc.returncode in (0, 1) + # Paths may or may not be quoted, Windows quotes paths. + git_check_ignore_re = re.compile(r"^::\s+(\"([^\"]+)\"|(.+))\Z") + # Return the list of paths sorted git_check_ignore_lines = git_check_ignore_proc.stdout.decode().splitlines() - return sorted([line.split()[-1] for line in git_check_ignore_lines if line.startswith("::")]) + git_check_not_ignored = [] + for line in git_check_ignore_lines: + if match := git_check_ignore_re.fullmatch(line): + git_check_not_ignored.append(match.group(2) or match.group(3)) + return sorted(git_check_not_ignored) def get_externals() -> list[str]: @@ -238,12 +245,20 @@ def create_source_sbom() -> None: ) for path in paths: + + # Normalize the filename from any combination of slashes. + path = str(PurePosixPath(PureWindowsPath(path))) + # Skip directories and excluded files if not (CPYTHON_ROOT_DIR / path).is_file() or path in exclude: continue # SPDX requires SHA1 to be used for files, but we provide SHA256 too. data = (CPYTHON_ROOT_DIR / path).read_bytes() + # We normalize line-endings for consistent checksums. + # This is a rudimentary check for binary files. + if b"\x00" not in data: + data = data.replace(b"\r\n", b"\n") checksum_sha1 = hashlib.sha1(data).hexdigest() checksum_sha256 = hashlib.sha256(data).hexdigest() From 4a5ad8469af9a6fc0ec1355eb203cc22bb4321d5 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 30 Apr 2024 18:44:37 +0300 Subject: [PATCH 114/217] gh-118418: Use a default value for `type_params` in `typing._eval_type` (#118431) --- Lib/typing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index b3f4ba99f9ec21..eff65cfb68b866 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -400,7 +400,7 @@ def inner(*args, **kwds): return decorator -def _eval_type(t, globalns, localns, type_params, *, recursive_guard=frozenset()): +def _eval_type(t, globalns, localns, type_params=None, *, recursive_guard=frozenset()): """Evaluate all forward references in the given type t. For use of globalns and localns see the docstring for get_type_hints(). @@ -981,7 +981,7 @@ def __init__(self, arg, is_argument=True, module=None, *, is_class=False): self.__forward_is_class__ = is_class self.__forward_module__ = module - def _evaluate(self, globalns, localns, type_params, *, recursive_guard): + def _evaluate(self, globalns, localns, type_params=None, *, recursive_guard): if self.__forward_arg__ in recursive_guard: return self if not self.__forward_evaluated__ or localns is not globalns: From d7ac427a796a3f869b813dac37b030889b56cd3b Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 30 Apr 2024 11:18:01 -0700 Subject: [PATCH 115/217] gh-117618: Make package.module searchable for breakpoints and clean up docs (#117619) --- Doc/library/pdb.rst | 19 ++++++--- Lib/pdb.py | 24 ++++++----- Lib/test/test_pdb.py | 40 +++++++++++++++++++ ...-04-08-03-23-22.gh-issue-117618.-4DCUw.rst | 1 + 4 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-08-03-23-22.gh-issue-117618.-4DCUw.rst diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index ac3007f70c3534..a640976493caba 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -328,12 +328,16 @@ can be overridden by the local file. .. pdbcommand:: b(reak) [([filename:]lineno | function) [, condition]] - With a *lineno* argument, set a break there in the current file. With a - *function* argument, set a break at the first executable statement within - that function. The line number may be prefixed with a filename and a colon, - to specify a breakpoint in another file (probably one that hasn't been loaded - yet). The file is searched on :data:`sys.path`. Note that each breakpoint - is assigned a number to which all the other breakpoint commands refer. + With a *lineno* argument, set a break at line *lineno* in the current file. + The line number may be prefixed with a *filename* and a colon, + to specify a breakpoint in another file (possibly one that hasn't been loaded + yet). The file is searched on :data:`sys.path`. Accepatable forms of *filename* + are ``/abspath/to/file.py``, ``relpath/file.py``, ``module`` and + ``package.module``. + + With a *function* argument, set a break at the first executable statement within + that function. *function* can be any expression that evaluates to a function + in the current namespace. If a second argument is present, it is an expression which must evaluate to true before the breakpoint is honored. @@ -342,6 +346,9 @@ can be overridden by the local file. of times that breakpoint has been hit, the current ignore count, and the associated condition if any. + Each breakpoint is assigned a number to which all the other + breakpoint commands refer. + .. pdbcommand:: tbreak [([filename:]lineno | function) [, condition]] Temporary breakpoint, which is removed automatically when it is first hit. diff --git a/Lib/pdb.py b/Lib/pdb.py index d4138b95d3c332..fa2e1ec94be235 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -2007,17 +2007,23 @@ def lookupmodule(self, filename): lookupmodule() translates (possibly incomplete) file or module name into an absolute file name. + + filename could be in format of: + * an absolute path like '/path/to/file.py' + * a relative path like 'file.py' or 'dir/file.py' + * a module name like 'module' or 'package.module' + + files and modules will be searched in sys.path. """ - if os.path.isabs(filename) and os.path.exists(filename): - return filename - f = os.path.join(sys.path[0], filename) - if os.path.exists(f) and self.canonic(f) == self.mainpyfile: - return f - root, ext = os.path.splitext(filename) - if ext == '': - filename = filename + '.py' + if not filename.endswith('.py'): + # A module is passed in so convert it to equivalent file + filename = filename.replace('.', os.sep) + '.py' + if os.path.isabs(filename): - return filename + if os.path.exists(filename): + return filename + return None + for dirname in sys.path: while os.path.islink(dirname): dirname = os.readlink(dirname) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 2d057e2647f13c..5635d17c99d2a3 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -354,6 +354,46 @@ def test_pdb_breakpoint_commands(): 4 """ +def test_pdb_breakpoint_with_filename(): + """Breakpoints with filename:lineno + + >>> def test_function(): + ... # inspect_fodder2 is a great module as the line number is stable + ... from test.test_inspect import inspect_fodder2 as mod2 + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... mod2.func88() + ... mod2.func114() + ... # Be a good citizen and clean up the mess + ... reset_Breakpoint() + + First, need to clear bdb state that might be left over from previous tests. + Otherwise, the new breakpoints might get assigned different numbers. + + >>> reset_Breakpoint() + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + ... 'break test.test_inspect.inspect_fodder2:90', + ... 'continue', # will stop at func88 + ... 'break test/test_inspect/inspect_fodder2.py:115', + ... 'continue', # will stop at func114 + ... 'continue', + ... ]): + ... test_function() + > (5)test_function() + -> mod2.func88() + (Pdb) break test.test_inspect.inspect_fodder2:90 + Breakpoint 1 at ...inspect_fodder2.py:90 + (Pdb) continue + > ...inspect_fodder2.py(90)func88() + -> return 90 + (Pdb) break test/test_inspect/inspect_fodder2.py:115 + Breakpoint 2 at ...inspect_fodder2.py:115 + (Pdb) continue + > ...inspect_fodder2.py(115)func114() + -> return 115 + (Pdb) continue + """ + def test_pdb_breakpoints_preserved_across_interactive_sessions(): """Breakpoints are remembered between interactive sessions diff --git a/Misc/NEWS.d/next/Library/2024-04-08-03-23-22.gh-issue-117618.-4DCUw.rst b/Misc/NEWS.d/next/Library/2024-04-08-03-23-22.gh-issue-117618.-4DCUw.rst new file mode 100644 index 00000000000000..569c5d57effdcb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-08-03-23-22.gh-issue-117618.-4DCUw.rst @@ -0,0 +1 @@ +Support ``package.module`` as ``filename`` for ``break`` command of :mod:`pdb` From f7747f73a9d9b9b1661c1a69cd8d934d56bbd3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 30 Apr 2024 20:25:25 +0200 Subject: [PATCH 116/217] gh-109975: Document crypt_r as a possible replacement of crypt (#118439) --- Doc/whatsnew/3.13.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 98349a5984bb7e..a9a7e485dd2bac 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1437,7 +1437,10 @@ PEP 594: dead batteries * :pypi:`argon2-cffi`: The secure Argon2 password hashing algorithm. * :pypi:`legacycrypt`: - Wrapper to the POSIX crypt library call and associated functionality. + :mod:`ctypes` wrapper to the POSIX crypt library call and associated functionality. + * :pypi:`crypt_r`: + Fork of the :mod:`!crypt` module, wrapper to the :manpage:`crypt_r(3)` library + call and associated functionality. (Contributed by Victor Stinner in :gh:`104773`.) From 1f16b4ce569f222af74fcbb7b2ef98eee2398d20 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 30 Apr 2024 19:32:25 +0100 Subject: [PATCH 117/217] gh-118272: Clear generator frame's locals when the generator is closed (#118277) Co-authored-by: Thomas Grainger --- Include/internal/pycore_frame.h | 3 +++ Lib/test/test_generators.py | 20 +++++++++++++++++++ ...-04-25-12-55-47.gh-issue-118272.5ptjk_.rst | 2 ++ Objects/genobject.c | 1 + Python/frame.c | 17 +++++++++++----- 5 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-25-12-55-47.gh-issue-118272.5ptjk_.rst diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index f913928f38bd05..37ae5ae850389b 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -227,6 +227,9 @@ _PyFrame_GetFrameObject(_PyInterpreterFrame *frame) return _PyFrame_MakeAndSetFrameObject(frame); } +void +_PyFrame_ClearLocals(_PyInterpreterFrame *frame); + /* Clears all references in the frame. * If take is non-zero, then the _PyInterpreterFrame frame * may be transferred to the frame object it references diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index d48f0d47ba1962..6d36df2c7413e0 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -532,6 +532,26 @@ def f(): with self.assertRaises(RuntimeError): gen.close() + def test_close_releases_frame_locals(self): + # See gh-118272 + + class Foo: + pass + + f = Foo() + f_wr = weakref.ref(f) + + def genfn(): + a = f + yield + + g = genfn() + next(g) + del f + g.close() + support.gc_collect() + self.assertIsNone(f_wr()) + class GeneratorThrowTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-25-12-55-47.gh-issue-118272.5ptjk_.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-25-12-55-47.gh-issue-118272.5ptjk_.rst new file mode 100644 index 00000000000000..32043440fd0365 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-25-12-55-47.gh-issue-118272.5ptjk_.rst @@ -0,0 +1,2 @@ +Fix bug where ``generator.close`` does not free the generator frame's +locals. diff --git a/Objects/genobject.c b/Objects/genobject.c index 5d7da49cfca166..04736a04562e14 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -380,6 +380,7 @@ gen_close(PyGenObject *gen, PyObject *args) // RESUME after YIELD_VALUE and exception depth is 1 assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START); gen->gi_frame_state = FRAME_COMPLETED; + _PyFrame_ClearLocals((_PyInterpreterFrame *)gen->gi_iframe); Py_RETURN_NONE; } } diff --git a/Python/frame.c b/Python/frame.c index db9d13359a23ca..ec390e7426ad47 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -94,6 +94,17 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) } } +void +_PyFrame_ClearLocals(_PyInterpreterFrame *frame) +{ + assert(frame->stacktop >= 0); + for (int i = 0; i < frame->stacktop; i++) { + Py_XDECREF(frame->localsplus[i]); + } + frame->stacktop = 0; + Py_CLEAR(frame->f_locals); +} + void _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame) { @@ -114,11 +125,7 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame) } Py_DECREF(f); } - assert(frame->stacktop >= 0); - for (int i = 0; i < frame->stacktop; i++) { - Py_XDECREF(frame->localsplus[i]); - } - Py_XDECREF(frame->f_locals); + _PyFrame_ClearLocals(frame); Py_DECREF(frame->f_funcobj); } From 4a1cf66c5c0afa36d7a51d5f9d3874cda10df79c Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Tue, 30 Apr 2024 11:38:05 -0700 Subject: [PATCH 118/217] gh-117657: Fix small issues with instrumentation and TSAN (#118064) Small TSAN fixups for instrumentation --- Include/internal/pycore_pyatomic_ft_wrappers.h | 11 +++++++++-- Objects/genobject.c | 8 +++++--- Python/bytecodes.c | 4 ++-- Python/ceval.c | 2 +- Python/ceval_macros.h | 2 +- Python/generated_cases.c.h | 2 +- Python/instrumentation.c | 9 ++++++--- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Include/internal/pycore_pyatomic_ft_wrappers.h b/Include/internal/pycore_pyatomic_ft_wrappers.h index bbfc462a733d0e..83f02c92495b3f 100644 --- a/Include/internal/pycore_pyatomic_ft_wrappers.h +++ b/Include/internal/pycore_pyatomic_ft_wrappers.h @@ -39,6 +39,10 @@ extern "C" { _Py_atomic_load_uint8(&value) #define FT_ATOMIC_STORE_UINT8(value, new_value) \ _Py_atomic_store_uint8(&value, new_value) +#define FT_ATOMIC_LOAD_UINT8_RELAXED(value) \ + _Py_atomic_load_uint8_relaxed(&value) +#define FT_ATOMIC_LOAD_UINT16_RELAXED(value) \ + _Py_atomic_load_uint16_relaxed(&value) #define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) \ _Py_atomic_store_ptr_relaxed(&value, new_value) #define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) \ @@ -49,7 +53,8 @@ extern "C" { _Py_atomic_store_ssize_relaxed(&value, new_value) #define FT_ATOMIC_STORE_UINT8_RELAXED(value, new_value) \ _Py_atomic_store_uint8_relaxed(&value, new_value) - +#define FT_ATOMIC_STORE_UINT16_RELAXED(value, new_value) \ + _Py_atomic_store_uint16_relaxed(&value, new_value) #else #define FT_ATOMIC_LOAD_PTR(value) value #define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value @@ -62,12 +67,14 @@ extern "C" { #define FT_ATOMIC_LOAD_PTR_RELAXED(value) value #define FT_ATOMIC_LOAD_UINT8(value) value #define FT_ATOMIC_STORE_UINT8(value, new_value) value = new_value +#define FT_ATOMIC_LOAD_UINT8_RELAXED(value) value +#define FT_ATOMIC_LOAD_UINT16_RELAXED(value) value #define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) value = new_value #define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) value = new_value #define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) value = new_value #define FT_ATOMIC_STORE_SSIZE_RELAXED(value, new_value) value = new_value #define FT_ATOMIC_STORE_UINT8_RELAXED(value, new_value) value = new_value - +#define FT_ATOMIC_STORE_UINT16_RELAXED(value, new_value) value = new_value #endif #ifdef __cplusplus diff --git a/Objects/genobject.c b/Objects/genobject.c index 04736a04562e14..a1ed1cbd2bfb58 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -11,6 +11,7 @@ #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_opcode_utils.h" // RESUME_AFTER_YIELD_FROM +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pystate.h" // _PyThreadState_GET() @@ -329,10 +330,11 @@ gen_close_iter(PyObject *yf) static inline bool is_resume(_Py_CODEUNIT *instr) { + uint8_t code = FT_ATOMIC_LOAD_UINT8_RELAXED(instr->op.code); return ( - instr->op.code == RESUME || - instr->op.code == RESUME_CHECK || - instr->op.code == INSTRUMENTED_RESUME + code == RESUME || + code == RESUME_CHECK || + code == INSTRUMENTED_RESUME ); } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index eee8b328a65b8c..5bb7e1211385a5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -20,7 +20,7 @@ #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_opcode_metadata.h" // uop names #include "pycore_opcode_utils.h" // MAKE_FUNCTION_* -#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_PTR_ACQUIRE +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* #include "pycore_pyerrors.h" // _PyErr_GetRaisedException() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_range.h" // _PyRangeIterObject @@ -163,7 +163,7 @@ dummy_func( if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { CHECK_EVAL_BREAKER(); } - this_instr->op.code = RESUME_CHECK; + FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); } } diff --git a/Python/ceval.c b/Python/ceval.c index d130c734a67144..8b23bc6bcbeb39 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -20,7 +20,7 @@ #include "pycore_opcode_metadata.h" // EXTRA_CASES #include "pycore_optimizer.h" // _PyUOpExecutor_Type #include "pycore_opcode_utils.h" // MAKE_FUNCTION_* -#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_PTR_ACQUIRE +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* #include "pycore_pyerrors.h" // _PyErr_GetRaisedException() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_range.h" // _PyRangeIterObject diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 1a8554ab72269f..c88a07c1f5e951 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -162,7 +162,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { /* The integer overflow is checked by an assertion below. */ #define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(_PyFrame_GetCode(frame)))) #define NEXTOPARG() do { \ - _Py_CODEUNIT word = *next_instr; \ + _Py_CODEUNIT word = {.cache = FT_ATOMIC_LOAD_UINT16_RELAXED(*(uint16_t*)next_instr)}; \ opcode = word.op.code; \ oparg = word.op.arg; \ } while (0) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 7c1cc147de4e1a..a5bb29385844d8 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4955,7 +4955,7 @@ if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { CHECK_EVAL_BREAKER(); } - this_instr->op.code = RESUME_CHECK; + FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); } DISPATCH(); } diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 328a3b1733d604..ce97c3add7d0ea 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -626,9 +626,10 @@ de_instrument(PyCodeObject *code, int i, int event) return; } CHECK(_PyOpcode_Deopt[deinstrumented] == deinstrumented); - *opcode_ptr = deinstrumented; + FT_ATOMIC_STORE_UINT8_RELAXED(*opcode_ptr, deinstrumented); if (_PyOpcode_Caches[deinstrumented]) { - instr[1].counter = adaptive_counter_warmup(); + FT_ATOMIC_STORE_UINT16_RELAXED(instr[1].counter.as_counter, + adaptive_counter_warmup().as_counter); } } @@ -703,8 +704,10 @@ instrument(PyCodeObject *code, int i) int deopt = _PyOpcode_Deopt[opcode]; int instrumented = INSTRUMENTED_OPCODES[deopt]; assert(instrumented); - *opcode_ptr = instrumented; + FT_ATOMIC_STORE_UINT8_RELAXED(*opcode_ptr, instrumented); if (_PyOpcode_Caches[deopt]) { + FT_ATOMIC_STORE_UINT16_RELAXED(instr[1].counter.as_counter, + adaptive_counter_warmup().as_counter); instr[1].counter = adaptive_counter_warmup(); } } From b2c3b70c7102197e4505e6cd69722dc508527d22 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 30 Apr 2024 15:01:28 -0400 Subject: [PATCH 119/217] gh-118332: Fix deadlock involving stop the world (#118412) Avoid detaching thread state when stopping the world. When re-attaching the thread state, the thread would attempt to resume the top-most critical section, which might now be held by a thread paused for our stop-the-world request. --- Include/internal/pycore_lock.h | 6 +- .../test_critical_sections.c | 85 +++++++++++++++++++ Modules/_threadmodule.c | 3 +- Python/lock.c | 6 +- Python/pystate.c | 3 +- 5 files changed, 96 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_lock.h b/Include/internal/pycore_lock.h index f993c95ecbf75a..a5b28e4bd4744e 100644 --- a/Include/internal/pycore_lock.h +++ b/Include/internal/pycore_lock.h @@ -150,8 +150,10 @@ PyAPI_FUNC(void) PyEvent_Wait(PyEvent *evt); // Wait for the event to be set, or until the timeout expires. If the event is // already set, then this returns immediately. Returns 1 if the event was set, -// and 0 if the timeout expired or thread was interrupted. -PyAPI_FUNC(int) PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns); +// and 0 if the timeout expired or thread was interrupted. If `detach` is +// true, then the thread will detach/release the GIL while waiting. +PyAPI_FUNC(int) +PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns, int detach); // _PyRawMutex implements a word-sized mutex that that does not depend on the // parking lot API, and therefore can be used in the parking lot diff --git a/Modules/_testinternalcapi/test_critical_sections.c b/Modules/_testinternalcapi/test_critical_sections.c index 94da0468fcf149..cdf8a70fc79ff3 100644 --- a/Modules/_testinternalcapi/test_critical_sections.c +++ b/Modules/_testinternalcapi/test_critical_sections.c @@ -204,6 +204,90 @@ test_critical_sections_threads(PyObject *self, PyObject *Py_UNUSED(args)) Py_DECREF(test_data.obj1); Py_RETURN_NONE; } + +static void +pysleep(int ms) +{ +#ifdef MS_WINDOWS + Sleep(ms); +#else + usleep(ms * 1000); +#endif +} + +struct test_data_gc { + PyObject *obj; + Py_ssize_t num_threads; + Py_ssize_t id; + Py_ssize_t countdown; + PyEvent done_event; + PyEvent ready; +}; + +static void +thread_gc(void *arg) +{ + struct test_data_gc *test_data = arg; + PyGILState_STATE gil = PyGILState_Ensure(); + + Py_ssize_t id = _Py_atomic_add_ssize(&test_data->id, 1); + if (id == test_data->num_threads - 1) { + _PyEvent_Notify(&test_data->ready); + } + else { + // wait for all test threads to more reliably reproduce the issue. + PyEvent_Wait(&test_data->ready); + } + + if (id == 0) { + Py_BEGIN_CRITICAL_SECTION(test_data->obj); + // pause long enough that the lock would be handed off directly to + // a waiting thread. + pysleep(5); + PyGC_Collect(); + Py_END_CRITICAL_SECTION(); + } + else if (id == 1) { + pysleep(1); + Py_BEGIN_CRITICAL_SECTION(test_data->obj); + pysleep(1); + Py_END_CRITICAL_SECTION(); + } + else if (id == 2) { + // sleep long enough so that thread 0 is waiting to stop the world + pysleep(6); + Py_BEGIN_CRITICAL_SECTION(test_data->obj); + pysleep(1); + Py_END_CRITICAL_SECTION(); + } + + PyGILState_Release(gil); + if (_Py_atomic_add_ssize(&test_data->countdown, -1) == 1) { + // last thread to finish sets done_event + _PyEvent_Notify(&test_data->done_event); + } +} + +static PyObject * +test_critical_sections_gc(PyObject *self, PyObject *Py_UNUSED(args)) +{ + // gh-118332: Contended critical sections should not deadlock with GC + const Py_ssize_t NUM_THREADS = 3; + struct test_data_gc test_data = { + .obj = PyDict_New(), + .countdown = NUM_THREADS, + .num_threads = NUM_THREADS, + }; + assert(test_data.obj != NULL); + + for (int i = 0; i < NUM_THREADS; i++) { + PyThread_start_new_thread(&thread_gc, &test_data); + } + PyEvent_Wait(&test_data.done_event); + Py_DECREF(test_data.obj); + Py_RETURN_NONE; +} + #endif static PyMethodDef test_methods[] = { @@ -212,6 +296,7 @@ static PyMethodDef test_methods[] = { {"test_critical_sections_suspend", test_critical_sections_suspend, METH_NOARGS}, #ifdef Py_CAN_START_THREADS {"test_critical_sections_threads", test_critical_sections_threads, METH_NOARGS}, + {"test_critical_sections_gc", test_critical_sections_gc, METH_NOARGS}, #endif {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 5aa719c3834e61..f5e3b42600675e 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -501,7 +501,8 @@ ThreadHandle_join(ThreadHandle *self, PyTime_t timeout_ns) // Wait until the deadline for the thread to exit. PyTime_t deadline = timeout_ns != -1 ? _PyDeadline_Init(timeout_ns) : 0; - while (!PyEvent_WaitTimed(&self->thread_is_exiting, timeout_ns)) { + int detach = 1; + while (!PyEvent_WaitTimed(&self->thread_is_exiting, timeout_ns, detach)) { if (deadline) { // _PyDeadline_Get will return a negative value if the deadline has // been exceeded. diff --git a/Python/lock.c b/Python/lock.c index 91c66df8fd9093..5ed95fcaf4188c 100644 --- a/Python/lock.c +++ b/Python/lock.c @@ -277,12 +277,12 @@ _PyEvent_Notify(PyEvent *evt) void PyEvent_Wait(PyEvent *evt) { - while (!PyEvent_WaitTimed(evt, -1)) + while (!PyEvent_WaitTimed(evt, -1, /*detach=*/1)) ; } int -PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns) +PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns, int detach) { for (;;) { uint8_t v = _Py_atomic_load_uint8(&evt->v); @@ -298,7 +298,7 @@ PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns) uint8_t expected = _Py_HAS_PARKED; (void) _PyParkingLot_Park(&evt->v, &expected, sizeof(evt->v), - timeout_ns, NULL, 1); + timeout_ns, NULL, detach); return _Py_atomic_load_uint8(&evt->v) == _Py_LOCKED; } diff --git a/Python/pystate.c b/Python/pystate.c index 78b39c9a577404..9d7b73b3a071e1 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2238,7 +2238,8 @@ stop_the_world(struct _stoptheworld_state *stw) } PyTime_t wait_ns = 1000*1000; // 1ms (arbitrary, may need tuning) - if (PyEvent_WaitTimed(&stw->stop_event, wait_ns)) { + int detach = 0; + if (PyEvent_WaitTimed(&stw->stop_event, wait_ns, detach)) { assert(stw->thread_countdown == 0); break; } From 19d468a3ab1d57c07a4283d67d439908574aa0cc Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Tue, 30 Apr 2024 12:37:38 -0700 Subject: [PATCH 120/217] [gh-117657] Fix some issues with TSAN in typeobject (#118249) Fix some racing reads in typebobject.c --- Include/internal/pycore_pyatomic_ft_wrappers.h | 8 ++++++++ Objects/typeobject.c | 17 +++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Include/internal/pycore_pyatomic_ft_wrappers.h b/Include/internal/pycore_pyatomic_ft_wrappers.h index 83f02c92495b3f..bc6aba56cf9fc7 100644 --- a/Include/internal/pycore_pyatomic_ft_wrappers.h +++ b/Include/internal/pycore_pyatomic_ft_wrappers.h @@ -43,6 +43,8 @@ extern "C" { _Py_atomic_load_uint8_relaxed(&value) #define FT_ATOMIC_LOAD_UINT16_RELAXED(value) \ _Py_atomic_load_uint16_relaxed(&value) +#define FT_ATOMIC_LOAD_UINT32_RELAXED(value) \ + _Py_atomic_load_uint32_relaxed(&value) #define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) \ _Py_atomic_store_ptr_relaxed(&value, new_value) #define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) \ @@ -55,6 +57,9 @@ extern "C" { _Py_atomic_store_uint8_relaxed(&value, new_value) #define FT_ATOMIC_STORE_UINT16_RELAXED(value, new_value) \ _Py_atomic_store_uint16_relaxed(&value, new_value) +#define FT_ATOMIC_STORE_UINT32_RELAXED(value, new_value) \ + _Py_atomic_store_uint32_relaxed(&value, new_value) + #else #define FT_ATOMIC_LOAD_PTR(value) value #define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value @@ -69,12 +74,15 @@ extern "C" { #define FT_ATOMIC_STORE_UINT8(value, new_value) value = new_value #define FT_ATOMIC_LOAD_UINT8_RELAXED(value) value #define FT_ATOMIC_LOAD_UINT16_RELAXED(value) value +#define FT_ATOMIC_LOAD_UINT32_RELAXED(value) value #define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) value = new_value #define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) value = new_value #define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) value = new_value #define FT_ATOMIC_STORE_SSIZE_RELAXED(value, new_value) value = new_value #define FT_ATOMIC_STORE_UINT8_RELAXED(value, new_value) value = new_value #define FT_ATOMIC_STORE_UINT16_RELAXED(value, new_value) value = new_value +#define FT_ATOMIC_STORE_UINT32_RELAXED(value, new_value) value = new_value + #endif #ifdef __cplusplus diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 50efbb6e182df0..ec19a3d461f623 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -43,7 +43,8 @@ class object "PyObject *" "&PyBaseObject_Type" & ((1 << MCACHE_SIZE_EXP) - 1)) #define MCACHE_HASH_METHOD(type, name) \ - MCACHE_HASH((type)->tp_version_tag, ((Py_ssize_t)(name)) >> 3) + MCACHE_HASH(FT_ATOMIC_LOAD_UINT32_RELAXED((type)->tp_version_tag), \ + ((Py_ssize_t)(name)) >> 3) #define MCACHE_CACHEABLE_NAME(name) \ PyUnicode_CheckExact(name) && \ PyUnicode_IS_READY(name) && \ @@ -907,7 +908,7 @@ type_modified_unlocked(PyTypeObject *type) } type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; - type->tp_version_tag = 0; /* 0 is not a valid version tag */ + FT_ATOMIC_STORE_UINT32_RELAXED(type->tp_version_tag, 0); /* 0 is not a valid version tag */ if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { // This field *must* be invalidated if the type is modified (see the // comment on struct _specialization_cache): @@ -984,7 +985,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { clear: assert(!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN)); type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; - type->tp_version_tag = 0; /* 0 is not a valid version tag */ + FT_ATOMIC_STORE_UINT32_RELAXED(type->tp_version_tag, 0); /* 0 is not a valid version tag */ if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { // This field *must* be invalidated if the type is modified (see the // comment on struct _specialization_cache): @@ -1020,7 +1021,8 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type) /* We have run out of version numbers */ return 0; } - type->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++; + FT_ATOMIC_STORE_UINT32_RELAXED(type->tp_version_tag, + NEXT_GLOBAL_VERSION_TAG++); assert (type->tp_version_tag <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG); } else { @@ -1029,7 +1031,8 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type) /* We have run out of version numbers */ return 0; } - type->tp_version_tag = NEXT_VERSION_TAG(interp)++; + FT_ATOMIC_STORE_UINT32_RELAXED(type->tp_version_tag, + NEXT_VERSION_TAG(interp)++); assert (type->tp_version_tag != 0); } @@ -5085,7 +5088,9 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name) // synchronize-with other writing threads by doing an acquire load on the sequence while (1) { int sequence = _PySeqLock_BeginRead(&entry->sequence); - if (_Py_atomic_load_uint32_relaxed(&entry->version) == type->tp_version_tag && + uint32_t entry_version = _Py_atomic_load_uint32_relaxed(&entry->version); + uint32_t type_version = _Py_atomic_load_uint32_relaxed(&type->tp_version_tag); + if (entry_version == type_version && _Py_atomic_load_ptr_relaxed(&entry->name) == name) { assert(_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)); OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_name(name)); From b568c2c1ff5c0b1922a6402dc95c588d7f9aa914 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 30 Apr 2024 21:58:22 +0200 Subject: [PATCH 121/217] gh-118406: Add signature for sqlite3.Connection objects (#118428) --- Lib/test/test_sqlite3/test_dbapi.py | 5 +++++ .../Library/2024-04-30-15-18-19.gh-issue-118406.y-GnMo.rst | 1 + Modules/_sqlite/connection.c | 7 +++++++ 3 files changed, 13 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-04-30-15-18-19.gh-issue-118406.y-GnMo.rst diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 6d8744ca5f7969..51ce095df41fc1 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -590,6 +590,11 @@ def test_connection_resource_warning(self): del cx gc_collect() + def test_connection_signature(self): + from inspect import signature + sig = signature(self.cx) + self.assertEqual(str(sig), "(sql, /)") + class UninitialisedConnectionTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2024-04-30-15-18-19.gh-issue-118406.y-GnMo.rst b/Misc/NEWS.d/next/Library/2024-04-30-15-18-19.gh-issue-118406.y-GnMo.rst new file mode 100644 index 00000000000000..c60ddf9e00498e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-30-15-18-19.gh-issue-118406.y-GnMo.rst @@ -0,0 +1 @@ +Add signature for :class:`sqlite3.Connection` objects. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 74984ca5365743..fc03e4a085c179 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -2561,6 +2561,12 @@ set_autocommit(pysqlite_Connection *self, PyObject *val, void *Py_UNUSED(ctx)) return 0; } +static PyObject * +get_sig(PyObject *self, void *Py_UNUSED(ctx)) +{ + return PyUnicode_FromString("(sql, /)"); +} + static const char connection_doc[] = PyDoc_STR("SQLite database connection object."); @@ -2570,6 +2576,7 @@ static PyGetSetDef connection_getset[] = { {"total_changes", (getter)pysqlite_connection_get_total_changes, (setter)0}, {"in_transaction", (getter)pysqlite_connection_get_in_transaction, (setter)0}, {"autocommit", (getter)get_autocommit, (setter)set_autocommit}, + {"__text_signature__", get_sig, (setter)0}, {NULL} }; From 6999d68d2878871493d85dc63599f3d44eada104 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 30 Apr 2024 13:16:52 -0700 Subject: [PATCH 122/217] gh-118218: Reuse return tuple in itertools.pairwise (GH-118219) --- Lib/test/test_itertools.py | 7 ++++ ...-04-24-07-45-08.gh-issue-118218.m1OHbN.rst | 1 + Modules/itertoolsmodule.c | 34 +++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-24-07-45-08.gh-issue-118218.m1OHbN.rst diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 95e67911db6a7f..e243da309f03d8 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -1821,6 +1821,13 @@ def test_zip_longest_result_gc(self): gc.collect() self.assertTrue(gc.is_tracked(next(it))) + @support.cpython_only + def test_pairwise_result_gc(self): + # Ditto for pairwise. + it = pairwise([None, None]) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + @support.cpython_only def test_immutable_types(self): from itertools import _grouper, _tee, _tee_dataobject diff --git a/Misc/NEWS.d/next/Library/2024-04-24-07-45-08.gh-issue-118218.m1OHbN.rst b/Misc/NEWS.d/next/Library/2024-04-24-07-45-08.gh-issue-118218.m1OHbN.rst new file mode 100644 index 00000000000000..6d3c54cb485655 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-24-07-45-08.gh-issue-118218.m1OHbN.rst @@ -0,0 +1 @@ +Speed up :func:`itertools.pairwise` in the common case by up to 1.8x. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 6ee447ef6a8cd6..21ce3ecfad0354 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -270,6 +270,7 @@ typedef struct { PyObject_HEAD PyObject *it; PyObject *old; + PyObject *result; } pairwiseobject; /*[clinic input] @@ -301,6 +302,11 @@ pairwise_new_impl(PyTypeObject *type, PyObject *iterable) } po->it = it; po->old = NULL; + po->result = PyTuple_Pack(2, Py_None, Py_None); + if (po->result == NULL) { + Py_DECREF(po); + return NULL; + } return (PyObject *)po; } @@ -311,6 +317,7 @@ pairwise_dealloc(pairwiseobject *po) PyObject_GC_UnTrack(po); Py_XDECREF(po->it); Py_XDECREF(po->old); + Py_XDECREF(po->result); tp->tp_free(po); Py_DECREF(tp); } @@ -321,6 +328,7 @@ pairwise_traverse(pairwiseobject *po, visitproc visit, void *arg) Py_VISIT(Py_TYPE(po)); Py_VISIT(po->it); Py_VISIT(po->old); + Py_VISIT(po->result); return 0; } @@ -355,8 +363,30 @@ pairwise_next(pairwiseobject *po) Py_DECREF(old); return NULL; } - /* Future optimization: Reuse the result tuple as we do in enumerate() */ - result = PyTuple_Pack(2, old, new); + + result = po->result; + if (Py_REFCNT(result) == 1) { + Py_INCREF(result); + PyObject *last_old = PyTuple_GET_ITEM(result, 0); + PyObject *last_new = PyTuple_GET_ITEM(result, 1); + PyTuple_SET_ITEM(result, 0, Py_NewRef(old)); + PyTuple_SET_ITEM(result, 1, Py_NewRef(new)); + Py_DECREF(last_old); + Py_DECREF(last_new); + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } + } + else { + result = PyTuple_New(2); + if (result != NULL) { + PyTuple_SET_ITEM(result, 0, Py_NewRef(old)); + PyTuple_SET_ITEM(result, 1, Py_NewRef(new)); + } + } + Py_XSETREF(po->old, new); Py_DECREF(old); return result; From 587388ff22dc7cfa4b66722daf0d33cd804af9f2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 30 Apr 2024 22:29:48 +0200 Subject: [PATCH 123/217] gh-118124: Use static_assert() in Py_BUILD_ASSERT() on C11 (#118398) Use static_assert() in Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR() on C11 and newer and C++11 and newer. Add tests to test_cext and test_cppext. --- Include/pymacro.h | 49 +++++++++++++------ Lib/test/test_cext/extension.c | 5 ++ Lib/test/test_cppext/extension.cpp | 4 ++ ...-04-29-17-44-15.gh-issue-118124.czQQ9G.rst | 3 ++ 4 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-04-29-17-44-15.gh-issue-118124.czQQ9G.rst diff --git a/Include/pymacro.h b/Include/pymacro.h index cd6fc4eba9c2ed..b388c2a4a663ce 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -46,24 +46,41 @@ /* Argument must be a char or an int in [-128, 127] or [0, 255]. */ #define Py_CHARMASK(c) ((unsigned char)((c) & 0xff)) -/* Assert a build-time dependency, as an expression. - - Your compile will fail if the condition isn't true, or can't be evaluated - by the compiler. This can be used in an expression: its value is 0. - - Example: - - #define foo_to_char(foo) \ - ((char *)(foo) \ - + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) - - Written by Rusty Russell, public domain, http://ccodearchive.net/ */ -#define Py_BUILD_ASSERT_EXPR(cond) \ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +# define Py_BUILD_ASSERT_EXPR(cond) \ + ((void)sizeof(struct { int dummy; _Static_assert(cond, #cond); }), \ + 0) +#else + /* Assert a build-time dependency, as an expression. + * + * Your compile will fail if the condition isn't true, or can't be evaluated + * by the compiler. This can be used in an expression: its value is 0. + * + * Example: + * + * #define foo_to_char(foo) \ + * ((char *)(foo) \ + * + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) + * + * Written by Rusty Russell, public domain, http://ccodearchive.net/ + */ +# define Py_BUILD_ASSERT_EXPR(cond) \ (sizeof(char [1 - 2*!(cond)]) - 1) +#endif -#define Py_BUILD_ASSERT(cond) do { \ - (void)Py_BUILD_ASSERT_EXPR(cond); \ - } while(0) +#if ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \ + || (defined(__cplusplus) && __cplusplus >= 201103L)) + // Use static_assert() on C11 and newer +# define Py_BUILD_ASSERT(cond) \ + do { \ + static_assert((cond), #cond); \ + } while (0) +#else +# define Py_BUILD_ASSERT(cond) \ + do { \ + (void)Py_BUILD_ASSERT_EXPR(cond); \ + } while(0) +#endif /* Get the number of elements in a visible array diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index 662abd4005e8d2..eb23dbe20353ba 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -44,6 +44,11 @@ _testcext_exec(PyObject *module) return -1; } #endif + + // test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR() + Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int)); + assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0); + return 0; } diff --git a/Lib/test/test_cppext/extension.cpp b/Lib/test/test_cppext/extension.cpp index a569c2251d1ad7..ab485b629b7788 100644 --- a/Lib/test/test_cppext/extension.cpp +++ b/Lib/test/test_cppext/extension.cpp @@ -225,6 +225,10 @@ _testcppext_exec(PyObject *module) if (!result) return -1; Py_DECREF(result); + // test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR() + Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int)); + assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0); + return 0; } diff --git a/Misc/NEWS.d/next/C API/2024-04-29-17-44-15.gh-issue-118124.czQQ9G.rst b/Misc/NEWS.d/next/C API/2024-04-29-17-44-15.gh-issue-118124.czQQ9G.rst new file mode 100644 index 00000000000000..3deeb517eb9fce --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-04-29-17-44-15.gh-issue-118124.czQQ9G.rst @@ -0,0 +1,3 @@ +Fix :c:macro:`Py_BUILD_ASSERT` and :c:macro:`Py_BUILD_ASSERT_EXPR` for +non-constant expressions: use ``static_assert()`` on C11 and newer. +Patch by Victor Stinner. From e93c39b47ea623dfaf61f80775ad4747b163efe5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 30 Apr 2024 22:32:55 +0200 Subject: [PATCH 124/217] gh-118422: Fix run_fileexflags() test (#118429) Don't test the undefined behavior of fileno() on a closed file, but use fstat() as a reliable test if the file was closed or not. --- Include/internal/pycore_fileutils.h | 3 ++ Modules/_testcapi/run.c | 13 ++++--- Python/fileutils.c | 49 +++++++++++++++++++++++++ Python/pylifecycle.c | 55 +++-------------------------- 4 files changed, 62 insertions(+), 58 deletions(-) diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index bc8100b58e8ea3..13f86b01bbfe8f 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -326,6 +326,9 @@ extern int _PyFile_Flush(PyObject *); extern int _Py_GetTicksPerSecond(long *ticks_per_second); #endif +// Export for '_testcapi' shared extension +PyAPI_FUNC(int) _Py_IsValidFD(int fd); + #ifdef __cplusplus } #endif diff --git a/Modules/_testcapi/run.c b/Modules/_testcapi/run.c index 4fd98b82d762ff..21244d02967ebf 100644 --- a/Modules/_testcapi/run.c +++ b/Modules/_testcapi/run.c @@ -1,5 +1,7 @@ +#define PYTESTCAPI_NEED_INTERNAL_API #include "parts.h" #include "util.h" +#include "pycore_fileutils.h" // _Py_IsValidFD() #include #include @@ -71,21 +73,18 @@ run_fileexflags(PyObject *mod, PyObject *pos_args) PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename); return NULL; } + int fd = fileno(fp); result = PyRun_FileExFlags(fp, filename, start, globals, locals, closeit, pflags); -#if defined(__linux__) || defined(MS_WINDOWS) || defined(__APPLE__) - /* The behavior of fileno() after fclose() is undefined, but it is - * the only practical way to check whether the file was closed. - * Only test this on the known platforms. */ - if (closeit && result && fileno(fp) >= 0) { + if (closeit && result && _Py_IsValidFD(fd)) { PyErr_SetString(PyExc_AssertionError, "File was not closed after excution"); Py_DECREF(result); fclose(fp); return NULL; } -#endif - if (!closeit && fileno(fp) < 0) { + + if (!closeit && !_Py_IsValidFD(fd)) { PyErr_SetString(PyExc_AssertionError, "Bad file descriptor after excution"); Py_XDECREF(result); return NULL; diff --git a/Python/fileutils.c b/Python/fileutils.c index 54853ba2f75d9d..e6a5391a3a28b5 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -3050,3 +3050,52 @@ _Py_GetTicksPerSecond(long *ticks_per_second) return 0; } #endif + + +/* Check if a file descriptor is valid or not. + Return 0 if the file descriptor is invalid, return non-zero otherwise. */ +int +_Py_IsValidFD(int fd) +{ +/* dup() is faster than fstat(): fstat() can require input/output operations, + whereas dup() doesn't. There is a low risk of EMFILE/ENFILE at Python + startup. Problem: dup() doesn't check if the file descriptor is valid on + some platforms. + + fcntl(fd, F_GETFD) is even faster, because it only checks the process table. + It is preferred over dup() when available, since it cannot fail with the + "too many open files" error (EMFILE). + + bpo-30225: On macOS Tiger, when stdout is redirected to a pipe and the other + side of the pipe is closed, dup(1) succeed, whereas fstat(1, &st) fails with + EBADF. FreeBSD has similar issue (bpo-32849). + + Only use dup() on Linux where dup() is enough to detect invalid FD + (bpo-32849). +*/ + if (fd < 0) { + return 0; + } +#if defined(F_GETFD) && ( \ + defined(__linux__) || \ + defined(__APPLE__) || \ + (defined(__wasm__) && !defined(__wasi__))) + return fcntl(fd, F_GETFD) >= 0; +#elif defined(__linux__) + int fd2 = dup(fd); + if (fd2 >= 0) { + close(fd2); + } + return (fd2 >= 0); +#elif defined(MS_WINDOWS) + HANDLE hfile; + _Py_BEGIN_SUPPRESS_IPH + hfile = (HANDLE)_get_osfhandle(fd); + _Py_END_SUPPRESS_IPH + return (hfile != INVALID_HANDLE_VALUE + && GetFileType(hfile) != FILE_TYPE_UNKNOWN); +#else + struct stat st; + return (fstat(fd, &st) == 0); +#endif +} diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index a672d8c7915ccd..790398e944f58e 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2410,54 +2410,6 @@ init_import_site(void) return _PyStatus_OK(); } -/* Check if a file descriptor is valid or not. - Return 0 if the file descriptor is invalid, return non-zero otherwise. */ -static int -is_valid_fd(int fd) -{ -/* dup() is faster than fstat(): fstat() can require input/output operations, - whereas dup() doesn't. There is a low risk of EMFILE/ENFILE at Python - startup. Problem: dup() doesn't check if the file descriptor is valid on - some platforms. - - fcntl(fd, F_GETFD) is even faster, because it only checks the process table. - It is preferred over dup() when available, since it cannot fail with the - "too many open files" error (EMFILE). - - bpo-30225: On macOS Tiger, when stdout is redirected to a pipe and the other - side of the pipe is closed, dup(1) succeed, whereas fstat(1, &st) fails with - EBADF. FreeBSD has similar issue (bpo-32849). - - Only use dup() on Linux where dup() is enough to detect invalid FD - (bpo-32849). -*/ - if (fd < 0) { - return 0; - } -#if defined(F_GETFD) && ( \ - defined(__linux__) || \ - defined(__APPLE__) || \ - defined(__wasm__)) - return fcntl(fd, F_GETFD) >= 0; -#elif defined(__linux__) - int fd2 = dup(fd); - if (fd2 >= 0) { - close(fd2); - } - return (fd2 >= 0); -#elif defined(MS_WINDOWS) - HANDLE hfile; - _Py_BEGIN_SUPPRESS_IPH - hfile = (HANDLE)_get_osfhandle(fd); - _Py_END_SUPPRESS_IPH - return (hfile != INVALID_HANDLE_VALUE - && GetFileType(hfile) != FILE_TYPE_UNKNOWN); -#else - struct stat st; - return (fstat(fd, &st) == 0); -#endif -} - /* returns Py_None if the fd is not valid */ static PyObject* create_stdio(const PyConfig *config, PyObject* io, @@ -2471,8 +2423,9 @@ create_stdio(const PyConfig *config, PyObject* io, int buffering, isatty; const int buffered_stdio = config->buffered_stdio; - if (!is_valid_fd(fd)) + if (!_Py_IsValidFD(fd)) { Py_RETURN_NONE; + } /* stdin is always opened in buffered mode, first because it shouldn't make a difference in common use cases, second because TextIOWrapper @@ -2588,9 +2541,9 @@ create_stdio(const PyConfig *config, PyObject* io, Py_XDECREF(text); Py_XDECREF(raw); - if (PyErr_ExceptionMatches(PyExc_OSError) && !is_valid_fd(fd)) { + if (PyErr_ExceptionMatches(PyExc_OSError) && !_Py_IsValidFD(fd)) { /* Issue #24891: the file descriptor was closed after the first - is_valid_fd() check was called. Ignore the OSError and set the + _Py_IsValidFD() check was called. Ignore the OSError and set the stream to None. */ PyErr_Clear(); Py_RETURN_NONE; From dc6b12d1b2ea26bb0323d932fa7b1a337eaca562 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 1 May 2024 04:46:13 +0800 Subject: [PATCH 125/217] gh-117139: Add header for tagged pointers (GH-118330) --------- Co-authored-by: Sam Gross <655866+colesbury@users.noreply.github.com> --- Include/internal/pycore_stackref.h | 195 +++++++++++++++++++++++++++++ Makefile.pre.in | 1 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 + 4 files changed, 200 insertions(+) create mode 100644 Include/internal/pycore_stackref.h diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h new file mode 100644 index 00000000000000..fd929cd4873a8b --- /dev/null +++ b/Include/internal/pycore_stackref.h @@ -0,0 +1,195 @@ +#ifndef Py_INTERNAL_STACKREF_H +#define Py_INTERNAL_STACKREF_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include + +typedef union { + uintptr_t bits; +} _PyStackRef; + +static const _PyStackRef Py_STACKREF_NULL = { .bits = 0 }; + +#define Py_TAG_DEFERRED (1) + +// Gets a PyObject * from a _PyStackRef +#if defined(Py_GIL_DISABLED) +static inline PyObject * +PyStackRef_Get(_PyStackRef tagged) +{ + PyObject *cleared = ((PyObject *)((tagged).bits & (~Py_TAG_DEFERRED))); + return cleared; +} +#else +# define PyStackRef_Get(tagged) ((PyObject *)((tagged).bits)) +#endif + +// Converts a PyObject * to a PyStackRef, stealing the reference. +#if defined(Py_GIL_DISABLED) +static inline _PyStackRef +_PyStackRef_StealRef(PyObject *obj) +{ + // Make sure we don't take an already tagged value. + assert(((uintptr_t)obj & Py_TAG_DEFERRED) == 0); + return ((_PyStackRef){.bits = ((uintptr_t)(obj))}); +} +# define PyStackRef_StealRef(obj) _PyStackRef_StealRef(_PyObject_CAST(obj)) +#else +# define PyStackRef_StealRef(obj) ((_PyStackRef){.bits = ((uintptr_t)(obj))}) +#endif + +// Converts a PyObject * to a PyStackRef, with a new reference +#if defined(Py_GIL_DISABLED) +static inline _PyStackRef +_PyStackRef_NewRefDeferred(PyObject *obj) +{ + // Make sure we don't take an already tagged value. + assert(((uintptr_t)obj & Py_TAG_DEFERRED) == 0); + assert(obj != NULL); + if (_PyObject_HasDeferredRefcount(obj)) { + return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_DEFERRED }; + } + else { + return (_PyStackRef){ .bits = (uintptr_t)Py_NewRef(obj) }; + } +} +# define PyStackRef_NewRefDeferred(obj) _PyStackRef_NewRefDeferred(_PyObject_CAST(obj)) +#else +# define PyStackRef_NewRefDeferred(obj) PyStackRef_NewRef(((_PyStackRef){.bits = ((uintptr_t)(obj))})) +#endif + +#if defined(Py_GIL_DISABLED) +static inline _PyStackRef +_PyStackRef_XNewRefDeferred(PyObject *obj) +{ + // Make sure we don't take an already tagged value. + assert(((uintptr_t)obj & Py_TAG_DEFERRED) == 0); + if (obj == NULL) { + return Py_STACKREF_NULL; + } + return _PyStackRef_NewRefDeferred(obj); +} +# define PyStackRef_XNewRefDeferred(obj) _PyStackRef_XNewRefDeferred(_PyObject_CAST(obj)) +#else +# define PyStackRef_XNewRefDeferred(obj) PyStackRef_XNewRef(((_PyStackRef){.bits = ((uintptr_t)(obj))})) +#endif + +// Converts a PyStackRef back to a PyObject *. +#if defined(Py_GIL_DISABLED) +static inline PyObject * +PyStackRef_StealObject(_PyStackRef tagged) +{ + if ((tagged.bits & Py_TAG_DEFERRED) == Py_TAG_DEFERRED) { + assert(_PyObject_HasDeferredRefcount(PyStackRef_Get(tagged))); + return Py_NewRef(PyStackRef_Get(tagged)); + } + return PyStackRef_Get(tagged); +} +#else +# define PyStackRef_StealObject(tagged) PyStackRef_Get(tagged) +#endif + +static inline void +_Py_untag_stack_borrowed(PyObject **dst, const _PyStackRef *src, size_t length) +{ + for (size_t i = 0; i < length; i++) { + dst[i] = PyStackRef_Get(src[i]); + } +} + +static inline void +_Py_untag_stack_steal(PyObject **dst, const _PyStackRef *src, size_t length) +{ + for (size_t i = 0; i < length; i++) { + dst[i] = PyStackRef_StealObject(src[i]); + } +} + + +#define PyStackRef_XSETREF(dst, src) \ + do { \ + _PyStackRef *_tmp_dst_ptr = &(dst) \ + _PyStackRef _tmp_old_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = (src); \ + PyStackRef_XDECREF(_tmp_old_dst); \ + } while (0) + +#define PyStackRef_SETREF(dst, src) \ + do { \ + _PyStackRef *_tmp_dst_ptr = &(dst); \ + _PyStackRef _tmp_old_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = (src); \ + PyStackRef_DECREF(_tmp_old_dst); \ + } while (0) + +#define PyStackRef_CLEAR(op) \ + do { \ + _PyStackRef *_tmp_op_ptr = &(op); \ + _PyStackRef _tmp_old_op = (*_tmp_op_ptr); \ + if (_tmp_old_op.bits != Py_STACKREF_NULL.bits) { \ + *_tmp_op_ptr = Py_STACKREF_NULL; \ + PyStackRef_DECREF(_tmp_old_op); \ + } \ + } while (0) + +#if defined(Py_GIL_DISABLED) +static inline void +PyStackRef_DECREF(_PyStackRef tagged) +{ + if ((tagged.bits & Py_TAG_DEFERRED) == Py_TAG_DEFERRED) { + return; + } + Py_DECREF(PyStackRef_Get(tagged)); +} +#else +# define PyStackRef_DECREF(op) Py_DECREF(PyStackRef_Get(op)) +#endif + +#if defined(Py_GIL_DISABLED) +static inline void +PyStackRef_INCREF(_PyStackRef tagged) +{ + if ((tagged.bits & Py_TAG_DEFERRED) == Py_TAG_DEFERRED) { + assert(_PyObject_HasDeferredRefcount(PyStackRef_Get(tagged))); + return; + } + Py_INCREF(PyStackRef_Get(tagged)); +} +#else +# define PyStackRef_INCREF(op) Py_INCREF(PyStackRef_Get(op)) +#endif + +static inline void +PyStackRef_XDECREF(_PyStackRef op) +{ + if (op.bits != Py_STACKREF_NULL.bits) { + PyStackRef_DECREF(op); + } +} + +static inline _PyStackRef +PyStackRef_NewRef(_PyStackRef obj) +{ + PyStackRef_INCREF(obj); + return obj; +} + +static inline _PyStackRef +PyStackRef_XNewRef(_PyStackRef obj) +{ + if (obj.bits == Py_STACKREF_NULL.bits) { + return obj; + } + return PyStackRef_NewRef(obj); +} + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_STACKREF_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index 4e4d01d14ee3cb..e69d1fe6e2dd14 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1225,6 +1225,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_structseq.h \ $(srcdir)/Include/internal/pycore_symtable.h \ $(srcdir)/Include/internal/pycore_sysmodule.h \ + $(srcdir)/Include/internal/pycore_stackref.h \ $(srcdir)/Include/internal/pycore_time.h \ $(srcdir)/Include/internal/pycore_token.h \ $(srcdir)/Include/internal/pycore_traceback.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 25d52945c1c330..4cb3e0d3227092 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -291,6 +291,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 4b1f9aa6538562..0b858cf1eb46a8 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -789,6 +789,9 @@ Include\internal + + Include\internal + Include\internal From 7fabcc727dee52a3e0dfe4f903ad414e93cf2dc9 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 1 May 2024 05:51:59 +0800 Subject: [PATCH 126/217] gh-117657: Don't specialize RESUME_CHECK when specialization is disabled (GH-118349) --- Python/bytecodes.c | 2 ++ Python/generated_cases.c.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 5bb7e1211385a5..18837aef74d78e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -163,7 +163,9 @@ dummy_func( if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { CHECK_EVAL_BREAKER(); } + #if ENABLE_SPECIALIZATION FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); + #endif /* ENABLE_SPECIALIZATION */ } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a5bb29385844d8..1444f5cdebba4b 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4955,7 +4955,9 @@ if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { CHECK_EVAL_BREAKER(); } + #if ENABLE_SPECIALIZATION FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); + #endif /* ENABLE_SPECIALIZATION */ } DISPATCH(); } From 9c468e2c5dffb6fa9811fd16e70fa0463bdfce5f Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 1 May 2024 07:32:37 +0800 Subject: [PATCH 127/217] gh-118201 - Disable the flaky POSIX test_confstr test on iOS (GH-118452) --- Lib/test/test_posix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 1d22869046fd12..7e5f04c22bd6d3 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -564,6 +564,7 @@ def test_dup(self): @unittest.skipUnless(hasattr(posix, 'confstr'), 'test needs posix.confstr()') + @unittest.skipIf(support.is_apple_mobile, "gh-118201: Test is flaky on iOS") def test_confstr(self): self.assertRaises(ValueError, posix.confstr, "CS_garbage") self.assertEqual(len(posix.confstr("CS_PATH")) > 0, True) From 7d83f7bcc484145596bae1ff015fed0762da345d Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 30 Apr 2024 18:26:34 -0700 Subject: [PATCH 128/217] gh-118335: Configure Tier 2 interpreter at build time (#118339) The code for Tier 2 is now only compiled when configured with `--enable-experimental-jit[=yes|interpreter]`. We drop support for `PYTHON_UOPS` and -`Xuops`, but you can disable the interpreter or JIT at runtime by setting `PYTHON_JIT=0`. You can also build it without enabling it by default using `--enable-experimental-jit=yes-off`; enable with `PYTHON_JIT=1`. On Windows, the `build.bat` script supports `--experimental-jit`, `--experimental-jit-off`, `--experimental-interpreter`. In the C code, `_Py_JIT` is defined as before when the JIT is enabled; the new variable `_Py_TIER2` is defined when the JIT *or* the interpreter is enabled. It is actually a bitmask: 1: JIT; 2: default-off; 4: interpreter. --- Doc/whatsnew/3.13.rst | 30 ++++++++++++++----- Include/internal/pycore_opcode_metadata.h | 4 +-- Include/internal/pycore_uop_metadata.h | 2 +- Lib/dis.py | 2 +- Lib/test/support/__init__.py | 8 ++--- Lib/test/test_capi/test_opt.py | 8 +++++ Lib/test/test_monitoring.py | 10 ++++--- Lib/test/test_opcache.py | 2 ++ Lib/test/test_optimizer.py | 2 ++ Lib/test/test_weakref.py | 2 ++ ...-04-26-14-06-18.gh-issue-118335.SRFsxO.rst | 4 +++ Modules/_opcode.c | 6 ++++ Modules/_testinternalcapi.c | 13 +++++++- Objects/codeobject.c | 6 ++++ Objects/object.c | 4 +++ PCbuild/_testinternalcapi.vcxproj | 6 ++++ PCbuild/build.bat | 11 +++++-- PCbuild/pythoncore.vcxproj | 1 + Python/bytecodes.c | 6 ++++ Python/ceval.c | 5 +++- Python/generated_cases.c.h | 6 ++++ Python/instrumentation.c | 6 ++++ Python/optimizer.c | 4 +++ Python/optimizer_analysis.c | 4 +++ Python/optimizer_symbols.c | 3 ++ Python/pylifecycle.c | 28 +++++++++-------- Python/pystate.c | 6 ++++ Python/sysmodule.c | 2 ++ Tools/cases_generator/analyzer.py | 1 + Tools/jit/README.md | 6 ++-- configure | 12 ++++++-- configure.ac | 13 ++++++-- 32 files changed, 181 insertions(+), 42 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-26-14-06-18.gh-issue-118335.SRFsxO.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index a9a7e485dd2bac..ee50effd662f12 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -888,7 +888,7 @@ Experimental JIT Compiler ========================= When CPython is configured using the ``--enable-experimental-jit`` option, -a just-in-time compiler is added which can speed up some Python programs. +a just-in-time compiler is added which may speed up some Python programs. The internal architecture is roughly as follows. @@ -905,19 +905,35 @@ The internal architecture is roughly as follows. before it is interpreted or translated to machine code. * There is a Tier 2 interpreter, but it is mostly intended for debugging - the earlier stages of the optimization pipeline. If the JIT is not - enabled, the Tier 2 interpreter can be invoked by passing Python the - ``-X uops`` option or by setting the ``PYTHON_UOPS`` environment - variable to ``1``. + the earlier stages of the optimization pipeline. + The Tier 2 interpreter can be enabled by configuring Python + with ``--enable-experimental-jit=interpreter``. -* When the ``--enable-experimental-jit`` option is used, the optimized +* When the JIT is enabled, the optimized Tier 2 IR is translated to machine code, which is then executed. - This does not require additional runtime options. * The machine code translation process uses an architecture called *copy-and-patch*. It has no runtime dependencies, but there is a new build-time dependency on LLVM. +The ``--enable-experimental-jit`` flag has the following optional values: + +* ``no`` (default) -- Disable the entire Tier 2 and JIT pipeline. + +* ``yes`` (default if the flag is present without optional value) + -- Enable the JIT. To disable the JIT at runtime, + pass the environment variable ``PYTHON_JIT=0``. + +* ``yes-off`` -- Build the JIT but disable it by default. + To enable the JIT at runtime, pass the environment variable + ``PYTHON_JIT=1``. + +* ``interpreter`` -- Enable the Tier 2 interpreter but disable the JIT. + The interpreter can be disabled by running with + ``PYTHON_JIT=0``. + +(On Windows, use ``PCbuild/build.bat --enable-jit`` to enable the JIT.) + See :pep:`744` for more details. (JIT by Brandt Bucher, inspired by a paper by Haoran Xu and Fredrik Kjolstad. diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 2ccc548ca6c5fd..808badea90d8ed 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -981,7 +981,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [BUILD_SLICE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, [BUILD_STRING] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, [BUILD_TUPLE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, - [CACHE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, + [CACHE] = { true, INSTR_FMT_IX, 0 }, [CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_ALLOC_AND_ENTER_INIT] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, @@ -1121,7 +1121,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [PUSH_NULL] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [RAISE_VARARGS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [RERAISE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [RESERVED] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, + [RESERVED] = { true, INSTR_FMT_IX, 0 }, [RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index a84212c1ec0b69..5697b13d1fc51d 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -249,7 +249,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_COLD_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_DYNAMIC_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_START_EXECUTOR] = HAS_DEOPT_FLAG, - [_FATAL_ERROR] = HAS_ESCAPES_FLAG, + [_FATAL_ERROR] = 0, [_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG, [_DEOPT] = 0, [_SIDE_EXIT] = 0, diff --git a/Lib/dis.py b/Lib/dis.py index 111d624fc259c5..76934eb00e63f0 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -216,7 +216,7 @@ def _get_code_array(co, adaptive): if op == ENTER_EXECUTOR: try: ex = get_executor(co, i) - except ValueError: + except (ValueError, RuntimeError): ex = None if ex: diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 15f654302b1793..52573e665a1273 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2539,17 +2539,17 @@ def exceeds_recursion_limit(): # Decorator to disable optimizer while a function run def without_optimizer(func): try: - import _testinternalcapi + from _testinternalcapi import get_optimizer, set_optimizer except ImportError: return func @functools.wraps(func) def wrapper(*args, **kwargs): - save_opt = _testinternalcapi.get_optimizer() + save_opt = get_optimizer() try: - _testinternalcapi.set_optimizer(None) + set_optimizer(None) return func(*args, **kwargs) finally: - _testinternalcapi.set_optimizer(save_opt) + set_optimizer(save_opt) return wrapper diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index c798b343626677..6e5b626e93291a 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -34,6 +34,8 @@ def clear_executors(func): @requires_specialization +@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), + "Requires optimizer infrastructure") class TestOptimizerAPI(unittest.TestCase): def test_new_counter_optimizer_dealloc(self): @@ -136,6 +138,8 @@ def get_opnames(ex): @requires_specialization +@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), + "Requires optimizer infrastructure") class TestExecutorInvalidation(unittest.TestCase): def setUp(self): @@ -215,6 +219,8 @@ def f(): @requires_specialization +@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), + "Requires optimizer infrastructure") @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") class TestUops(unittest.TestCase): @@ -579,6 +585,8 @@ def testfunc(n): @requires_specialization +@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), + "Requires optimizer infrastructure") @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") class TestUopsOptimization(unittest.TestCase): diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 11c61bc2e0688d..a9140d4d3dd743 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -1831,15 +1831,17 @@ class TestOptimizer(MonitoringTestBase, unittest.TestCase): def setUp(self): _testinternalcapi = import_module("_testinternalcapi") - self.old_opt = _testinternalcapi.get_optimizer() - opt = _testinternalcapi.new_counter_optimizer() - _testinternalcapi.set_optimizer(opt) + if hasattr(_testinternalcapi, "get_optimizer"): + self.old_opt = _testinternalcapi.get_optimizer() + opt = _testinternalcapi.new_counter_optimizer() + _testinternalcapi.set_optimizer(opt) super(TestOptimizer, self).setUp() def tearDown(self): super(TestOptimizer, self).tearDown() import _testinternalcapi - _testinternalcapi.set_optimizer(self.old_opt) + if hasattr(_testinternalcapi, "get_optimizer"): + _testinternalcapi.set_optimizer(self.old_opt) def test_for_loop(self): def test_func(x): diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index f4e954fd02148d..92a34113bc0383 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -16,6 +16,8 @@ def disabling_optimizer(func): def wrapper(*args, **kwargs): + if not hasattr(_testinternalcapi, "get_optimizer"): + return func(*args, **kwargs) old_opt = _testinternalcapi.get_optimizer() _testinternalcapi.set_optimizer(None) try: diff --git a/Lib/test/test_optimizer.py b/Lib/test/test_optimizer.py index 899a4507317334..fac4d1a4ab44c4 100644 --- a/Lib/test/test_optimizer.py +++ b/Lib/test/test_optimizer.py @@ -80,6 +80,8 @@ def func(x=0): class TestOptimizerSymbols(unittest.TestCase): + @unittest.skipUnless(hasattr(_testinternalcapi, "uop_symbols_test"), + "requires _testinternalcapi.uop_symbols_test") def test_optimizer_symbols(self): _testinternalcapi.uop_symbols_test() diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index df90647baee31e..16da24d7805b56 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -17,6 +17,7 @@ from test.support import gc_collect from test.support import import_helper from test.support import threading_helper +from test.support import is_wasi, Py_DEBUG # Used in ReferencesTestCase.test_ref_created_during_del() . ref_from_del = None @@ -960,6 +961,7 @@ def test_hashing(self): self.assertEqual(hash(a), hash(42)) self.assertRaises(TypeError, hash, b) + @unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack") def test_trashcan_16602(self): # Issue #16602: when a weakref's target was part of a long # deallocation chain, the trashcan mechanism could delay clearing diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-26-14-06-18.gh-issue-118335.SRFsxO.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-26-14-06-18.gh-issue-118335.SRFsxO.rst new file mode 100644 index 00000000000000..e13edbbbe528eb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-26-14-06-18.gh-issue-118335.SRFsxO.rst @@ -0,0 +1,4 @@ +Change how to use the tier 2 interpreter. Instead of running Python with +``-X uops`` or setting the environment variable ``PYTHON_UOPS=1``, this +choice is now made at build time by configuring with +``--enable-experimental-jit=interpreter``. diff --git a/Modules/_opcode.c b/Modules/_opcode.c index 5350adb456b859..85e0ffec900e89 100644 --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -367,7 +367,13 @@ _opcode_get_executor_impl(PyObject *module, PyObject *code, int offset) Py_TYPE(code)->tp_name); return NULL; } +#ifdef _Py_TIER2 return (PyObject *)PyUnstable_GetExecutor((PyCodeObject *)code, offset); +#else + PyErr_Format(PyExc_RuntimeError, + "Executors are not available in this build"); + return NULL; +#endif } static PyMethodDef diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 99e80baa223e8f..f7952a119f0bd9 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -985,6 +985,8 @@ get_co_framesize(PyObject *self, PyObject *arg) return PyLong_FromLong(code->co_framesize); } +#ifdef _Py_TIER2 + static PyObject * new_counter_optimizer(PyObject *self, PyObject *arg) { @@ -1012,7 +1014,10 @@ set_optimizer(PyObject *self, PyObject *opt) static PyObject * get_optimizer(PyObject *self, PyObject *Py_UNUSED(ignored)) { - PyObject *opt = (PyObject *)PyUnstable_GetOptimizer(); + PyObject *opt = NULL; +#ifdef _Py_TIER2 + opt = (PyObject *)PyUnstable_GetOptimizer(); +#endif if (opt == NULL) { Py_RETURN_NONE; } @@ -1045,6 +1050,8 @@ invalidate_executors(PyObject *self, PyObject *obj) Py_RETURN_NONE; } +#endif + static int _pending_callback(void *arg) { /* we assume the argument is callable object to which we own a reference */ @@ -2020,12 +2027,14 @@ static PyMethodDef module_functions[] = { {"iframe_getline", iframe_getline, METH_O, NULL}, {"iframe_getlasti", iframe_getlasti, METH_O, NULL}, {"get_co_framesize", get_co_framesize, METH_O, NULL}, +#ifdef _Py_TIER2 {"get_optimizer", get_optimizer, METH_NOARGS, NULL}, {"set_optimizer", set_optimizer, METH_O, NULL}, {"new_counter_optimizer", new_counter_optimizer, METH_NOARGS, NULL}, {"new_uop_optimizer", new_uop_optimizer, METH_NOARGS, NULL}, {"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL}, {"invalidate_executors", invalidate_executors, METH_O, NULL}, +#endif {"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc), METH_VARARGS | METH_KEYWORDS}, {"pending_identify", pending_identify, METH_VARARGS, NULL}, @@ -2072,7 +2081,9 @@ static PyMethodDef module_functions[] = { {"py_thread_id", get_py_thread_id, METH_NOARGS}, #endif {"set_immortalize_deferred", set_immortalize_deferred, METH_VARARGS}, +#ifdef _Py_TIER2 {"uop_symbols_test", _Py_uop_symbols_test, METH_NOARGS}, +#endif {NULL, NULL} /* sentinel */ }; diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 014632962bfcf3..605167c5c0bd3a 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1496,6 +1496,8 @@ PyCode_GetFreevars(PyCodeObject *code) return _PyCode_GetFreevars(code); } +#ifdef _Py_TIER2 + static void clear_executors(PyCodeObject *co) { @@ -1515,6 +1517,8 @@ _PyCode_Clear_Executors(PyCodeObject *code) clear_executors(code); } +#endif + static void deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions) { @@ -1739,9 +1743,11 @@ code_dealloc(PyCodeObject *co) PyMem_Free(co_extra); } +#ifdef _Py_TIER2 if (co->co_executors != NULL) { clear_executors(co); } +#endif Py_XDECREF(co->co_consts); Py_XDECREF(co->co_names); diff --git a/Objects/object.c b/Objects/object.c index 8d856939254080..45310a6c22d677 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2281,9 +2281,11 @@ static PyTypeObject* static_types[] = { &_PyBufferWrapper_Type, &_PyContextTokenMissing_Type, &_PyCoroWrapper_Type, +#ifdef _Py_TIER2 &_PyCounterExecutor_Type, &_PyCounterOptimizer_Type, &_PyDefaultOptimizer_Type, +#endif &_Py_GenericAliasIterType, &_PyHamtItems_Type, &_PyHamtKeys_Type, @@ -2304,8 +2306,10 @@ static PyTypeObject* static_types[] = { &_PyPositionsIterator, &_PyUnicodeASCIIIter_Type, &_PyUnion_Type, +#ifdef _Py_TIER2 &_PyUOpExecutor_Type, &_PyUOpOptimizer_Type, +#endif &_PyWeakref_CallableProxyType, &_PyWeakref_ProxyType, &_PyWeakref_RefType, diff --git a/PCbuild/_testinternalcapi.vcxproj b/PCbuild/_testinternalcapi.vcxproj index a825cac9138674..d4cd8ad1a46f24 100644 --- a/PCbuild/_testinternalcapi.vcxproj +++ b/PCbuild/_testinternalcapi.vcxproj @@ -108,6 +108,12 @@ false + + + _Py_JIT;%(PreprocessorDefinitions) + _Py_TIER2=$(UseTIER2);%(PreprocessorDefinitions) + + diff --git a/PCbuild/build.bat b/PCbuild/build.bat index 83b50db4467033..43a99aa684483f 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -36,7 +36,9 @@ echo. overrides -c and -d echo. --disable-gil Enable experimental support for running without the GIL. echo. --test-marker Enable the test marker within the build. echo. --regen Regenerate all opcodes, grammar and tokens. -echo. --experimental-jit Enable the experimental just-in-time compiler. +echo. --experimental-jit Enable the experimental just-in-time compiler. +echo. --experimental-jit-off Ditto but off by default (PYTHON_JIT=1 enables). +echo. --experimental-interpreter Enable the experimental Tier 2 interpreter. echo. echo.Available flags to avoid building certain modules. echo.These flags have no effect if '-e' is not given: @@ -66,6 +68,7 @@ set verbose=/nologo /v:m /clp:summary set kill= set do_pgo= set pgo_job=-m test --pgo +set UseTIER2=0 :CheckOpts if "%~1"=="-h" goto Usage @@ -86,7 +89,10 @@ if "%~1"=="--disable-gil" (set UseDisableGil=true) & shift & goto CheckOpts if "%~1"=="--test-marker" (set UseTestMarker=true) & shift & goto CheckOpts if "%~1"=="-V" shift & goto Version if "%~1"=="--regen" (set Regen=true) & shift & goto CheckOpts -if "%~1"=="--experimental-jit" (set UseJIT=true) & shift & goto CheckOpts +if "%~1"=="--experimental-jit" (set UseJIT=true) & (set UseTIER2=1) & shift & goto CheckOpts +if "%~1"=="--experimental-jit-off" (set UseJIT=true) & (set UseTIER2=3) & shift & goto CheckOpts +if "%~1"=="--experimental-interpreter" (set UseTIER2=4) & shift & goto CheckOpts +if "%~1"=="--experimental-interpreter-off" (set UseTIER2=6) & shift & goto CheckOpts rem These use the actual property names used by MSBuild. We could just let rem them in through the environment, but we specify them on the command line rem anyway for visibility so set defaults after this @@ -179,6 +185,7 @@ echo on /p:DisableGil=%UseDisableGil%^ /p:UseTestMarker=%UseTestMarker% %GITProperty%^ /p:UseJIT=%UseJIT%^ + /p:UseTIER2=%UseTIER2%^ %1 %2 %3 %4 %5 %6 %7 %8 %9 @echo off diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 4cb3e0d3227092..a24667dc74064a 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -105,6 +105,7 @@ _USRDLL;Py_BUILD_CORE;Py_BUILD_CORE_BUILTIN;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions) _Py_HAVE_ZLIB;%(PreprocessorDefinitions) _Py_JIT;%(PreprocessorDefinitions) + _Py_TIER2=$(UseTIER2);%(PreprocessorDefinitions) version.lib;ws2_32.lib;pathcch.lib;bcrypt.lib;%(AdditionalDependencies) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 18837aef74d78e..28766d6d97cc5f 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2363,6 +2363,7 @@ dummy_func( CHECK_EVAL_BREAKER(); assert(oparg <= INSTR_OFFSET()); JUMPBY(-oparg); + #ifdef _Py_TIER2 #if ENABLE_SPECIALIZATION _Py_BackoffCounter counter = this_instr[1].counter; if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) { @@ -2388,6 +2389,7 @@ dummy_func( ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); } #endif /* ENABLE_SPECIALIZATION */ + #endif /* _Py_TIER2 */ } pseudo(JUMP) = { @@ -2401,6 +2403,7 @@ dummy_func( }; tier1 inst(ENTER_EXECUTOR, (--)) { + #ifdef _Py_TIER2 int prevoparg = oparg; CHECK_EVAL_BREAKER(); if (this_instr->op.code != ENTER_EXECUTOR || @@ -2418,6 +2421,9 @@ dummy_func( tstate->previous_executor = Py_None; Py_INCREF(executor); GOTO_TIER_TWO(executor); + #else + Py_FatalError("ENTER_EXECUTOR is not supported in this build"); + #endif /* _Py_TIER2 */ } replaced op(_POP_JUMP_IF_FALSE, (cond -- )) { diff --git a/Python/ceval.c b/Python/ceval.c index 8b23bc6bcbeb39..59498bc826e941 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -755,7 +755,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _Py_CODEUNIT *next_instr; PyObject **stack_pointer; -#ifndef _Py_JIT +#if defined(_Py_TIER2) && !defined(_Py_JIT) /* Tier 2 interpreter state */ _PyExecutorObject *current_executor = NULL; const _PyUOpInstruction *next_uop = NULL; @@ -959,6 +959,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int goto error; +#ifdef _Py_TIER2 // Tier 2 is also here! enter_tier_two: @@ -1113,6 +1114,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif // _Py_JIT +#endif // _Py_TIER2 + } #if defined(__GNUC__) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 1444f5cdebba4b..602cce4beb1e28 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2492,6 +2492,7 @@ (void)this_instr; next_instr += 1; INSTRUCTION_STATS(ENTER_EXECUTOR); + #ifdef _Py_TIER2 int prevoparg = oparg; CHECK_EVAL_BREAKER(); if (this_instr->op.code != ENTER_EXECUTOR || @@ -2508,6 +2509,9 @@ tstate->previous_executor = Py_None; Py_INCREF(executor); GOTO_TIER_TWO(executor); + #else + Py_FatalError("ENTER_EXECUTOR is not supported in this build"); + #endif /* _Py_TIER2 */ DISPATCH(); } @@ -3432,6 +3436,7 @@ CHECK_EVAL_BREAKER(); assert(oparg <= INSTR_OFFSET()); JUMPBY(-oparg); + #ifdef _Py_TIER2 #if ENABLE_SPECIALIZATION _Py_BackoffCounter counter = this_instr[1].counter; if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) { @@ -3457,6 +3462,7 @@ ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); } #endif /* ENABLE_SPECIALIZATION */ + #endif /* _Py_TIER2 */ DISPATCH(); } diff --git a/Python/instrumentation.c b/Python/instrumentation.c index ce97c3add7d0ea..5614f4867a390d 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1705,10 +1705,12 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp) ); return 0; } +#ifdef _Py_TIER2 if (code->co_executors != NULL) { _PyCode_Clear_Executors(code); } _Py_Executors_InvalidateDependency(interp, code, 1); +#endif int code_len = (int)Py_SIZE(code); /* Exit early to avoid creating instrumentation * data for potential statically allocated code @@ -1946,7 +1948,9 @@ _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events) goto done; } set_global_version(tstate, new_version); +#ifdef _Py_TIER2 _Py_Executors_InvalidateAll(interp, 1); +#endif res = instrument_all_executing_code_objects(interp); done: _PyEval_StartTheWorld(interp); @@ -1986,7 +1990,9 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent code->_co_instrumentation_version -= MONITORING_VERSION_INCREMENT; } +#ifdef _Py_TIER2 _Py_Executors_InvalidateDependency(interp, code, 1); +#endif res = instrument_lock_held(code, interp); diff --git a/Python/optimizer.c b/Python/optimizer.c index a9a35fc902018a..a5e7430c46450e 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1,3 +1,5 @@ +#ifdef _Py_TIER2 + #include "Python.h" #include "opcode.h" #include "pycore_interp.h" @@ -1622,3 +1624,5 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation) } } } + +#endif /* _Py_TIER2 */ diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 9315d7228b5732..842b2e489239af 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -1,3 +1,5 @@ +#ifdef _Py_TIER2 + /* * This file contains the support code for CPython's uops optimizer. * It also performs some simple optimizations. @@ -603,3 +605,5 @@ _Py_uop_analyze_and_optimize( OPT_STAT_INC(optimizer_successes); return length; } + +#endif /* _Py_TIER2 */ diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c index 204599b08766c3..d52f490853c006 100644 --- a/Python/optimizer_symbols.c +++ b/Python/optimizer_symbols.c @@ -1,3 +1,4 @@ +#ifdef _Py_TIER2 #include "Python.h" @@ -506,3 +507,5 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) Py_XDECREF(val_43); return NULL; } + +#endif /* _Py_TIER2 */ diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 790398e944f58e..7726ccc979dbcc 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -624,9 +624,11 @@ static int builtins_dict_watcher(PyDict_WatchEvent event, PyObject *dict, PyObject *key, PyObject *new_value) { PyInterpreterState *interp = _PyInterpreterState_GET(); +#ifdef _Py_TIER2 if (interp->rare_events.builtin_dict < _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) { _Py_Executors_InvalidateAll(interp, 1); } +#endif RARE_EVENT_INTERP_INC(interp, builtin_dict); return 0; } @@ -1272,30 +1274,30 @@ init_interp_main(PyThreadState *tstate) } // Turn on experimental tier 2 (uops-based) optimizer + // This is also needed when the JIT is enabled +#ifdef _Py_TIER2 if (is_main_interp) { -#ifndef _Py_JIT - // No JIT, maybe use the tier two interpreter: - char *envvar = Py_GETENV("PYTHON_UOPS"); - int enabled = envvar != NULL && *envvar > '0'; - if (_Py_get_xoption(&config->xoptions, L"uops") != NULL) { - enabled = 1; + int enabled = 1; +#if _Py_TIER2 & 2 + enabled = 0; +#endif + char *env = Py_GETENV("PYTHON_JIT"); + if (env && *env != '\0') { + // PYTHON_JIT=0|1 overrides the default + enabled = *env != '0'; } if (enabled) { -#else - // Always enable tier two for JIT builds (ignoring the environment - // variable and command-line option above): - if (true) { -#endif PyObject *opt = PyUnstable_Optimizer_NewUOpOptimizer(); if (opt == NULL) { return _PyStatus_ERR("can't initialize optimizer"); } if (PyUnstable_SetOptimizer((_PyOptimizerObject *)opt)) { - return _PyStatus_ERR("can't initialize optimizer"); + return _PyStatus_ERR("can't install optimizer"); } Py_DECREF(opt); } } +#endif if (!is_main_interp) { // The main interpreter is handled in Py_Main(), for now. @@ -1655,10 +1657,12 @@ finalize_modules(PyThreadState *tstate) { PyInterpreterState *interp = tstate->interp; +#ifdef _Py_TIER2 // Invalidate all executors and turn off tier 2 optimizer _Py_Executors_InvalidateAll(interp, 0); _PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL); Py_XDECREF(old); +#endif // Stop watching __builtin__ modifications PyDict_Unwatch(0, interp->builtins); diff --git a/Python/pystate.c b/Python/pystate.c index 9d7b73b3a071e1..3d6e76e88bd731 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -653,8 +653,10 @@ init_interpreter(PyInterpreterState *interp, } interp->sys_profile_initialized = false; interp->sys_trace_initialized = false; +#ifdef _Py_TIER2 (void)_Py_SetOptimizer(interp, NULL); interp->executor_list_head = NULL; +#endif if (interp != &runtime->_main_interpreter) { /* Fix the self-referential, statically initialized fields. */ interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp); @@ -806,9 +808,11 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) tstate->_status.cleared = 0; } +#ifdef _Py_TIER2 _PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL); assert(old != NULL); Py_DECREF(old); +#endif /* It is possible that any of the objects below have a finalizer that runs Python code or otherwise relies on a thread state @@ -2821,9 +2825,11 @@ _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, if (eval_frame == interp->eval_frame) { return; } +#ifdef _Py_TIER2 if (eval_frame != NULL) { _Py_Executors_InvalidateAll(interp, 1); } +#endif RARE_EVENT_INC(set_eval_frame_func); interp->eval_frame = eval_frame; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 7af363678e8e86..726051521cf574 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2165,8 +2165,10 @@ static PyObject * sys__clear_internal_caches_impl(PyObject *module) /*[clinic end generated code: output=0ee128670a4966d6 input=253e741ca744f6e8]*/ { +#ifdef _Py_TIER2 PyInterpreterState *interp = _PyInterpreterState_GET(); _Py_Executors_InvalidateAll(interp, 0); +#endif PyType_ClearCache(); Py_RETURN_NONE; } diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 18cefa08328804..fdb635486b9531 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -411,6 +411,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "PyCell_New", "PyFloat_AS_DOUBLE", "_PyFrame_PushUnchecked", + "Py_FatalError", ) ESCAPING_FUNCTIONS = ( diff --git a/Tools/jit/README.md b/Tools/jit/README.md index 7b33f99d23f75d..0f5aa9ce656bc9 100644 --- a/Tools/jit/README.md +++ b/Tools/jit/README.md @@ -7,18 +7,18 @@ This version of CPython can be built with an experimental just-in-time compiler. The JIT compiler does not require end users to install any third-party dependencies, but part of it must be *built* using LLVM[^why-llvm]. You are *not* required to build the rest of CPython using LLVM, or even the same version of LLVM (in fact, this is uncommon). -LLVM version 16 is required. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-16`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code. +LLVM version 18 is required. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-18`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code. It's easy to install all of the required tools: ### Linux -Install LLVM 16 on Ubuntu/Debian: +Install LLVM 18 on Ubuntu/Debian: ```sh wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh -sudo ./llvm.sh 16 +sudo ./llvm.sh 18 ``` ### macOS diff --git a/configure b/configure index e1d0f36463c44b..01c00d2198548f 100755 --- a/configure +++ b/configure @@ -1818,7 +1818,7 @@ Optional Features: --disable-gil enable experimental support for running without the GIL (default is no) --enable-pystats enable internal statistics gathering (default is no) - --enable-experimental-jit + --enable-experimental-jit[=no|yes|yes-off|interpreter] build the experimental just-in-time compiler (default is no) --enable-optimizations enable expensive, stable optimizations (PGO, etc.) @@ -8229,11 +8229,19 @@ else $as_nop enable_experimental_jit=no fi +case $enable_experimental_jit in + no) enable_experimental_jit=no ;; + yes) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=1" ;; + yes-off) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=3" ;; + interpreter) enable_experimental_jit="-D_Py_TIER2=4" ;; + interpreter-off) enable_experimental_jit="-D_Py_TIER2=6" ;; # Secret option + *) as_fn_error $? "invalid argument: --enable-experimental-jit=$enable_experimental_jit; expected no|yes|yes-off|interpreter" "$LINENO" 5 ;; +esac if test "x$enable_experimental_jit" = xno then : else $as_nop - as_fn_append CFLAGS_NODIST " -D_Py_JIT" + as_fn_append CFLAGS_NODIST " $enable_experimental_jit" REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host" JIT_STENCILS_H="jit_stencils.h" if test "x$Py_DEBUG" = xtrue diff --git a/configure.ac b/configure.ac index 7681ea3d3fe3c6..ae65efeac55366 100644 --- a/configure.ac +++ b/configure.ac @@ -1768,14 +1768,23 @@ fi # Check for --enable-experimental-jit: AC_MSG_CHECKING([for --enable-experimental-jit]) AC_ARG_ENABLE([experimental-jit], - [AS_HELP_STRING([--enable-experimental-jit], + [AS_HELP_STRING([--enable-experimental-jit@<:@=no|yes|yes-off|interpreter@:>@], [build the experimental just-in-time compiler (default is no)])], [], [enable_experimental_jit=no]) +case $enable_experimental_jit in + no) enable_experimental_jit=no ;; + yes) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=1" ;; + yes-off) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=3" ;; + interpreter) enable_experimental_jit="-D_Py_TIER2=4" ;; + interpreter-off) enable_experimental_jit="-D_Py_TIER2=6" ;; # Secret option + *) AC_MSG_ERROR( + [invalid argument: --enable-experimental-jit=$enable_experimental_jit; expected no|yes|yes-off|interpreter]) ;; +esac AS_VAR_IF([enable_experimental_jit], [no], [], - [AS_VAR_APPEND([CFLAGS_NODIST], [" -D_Py_JIT"]) + [AS_VAR_APPEND([CFLAGS_NODIST], [" $enable_experimental_jit"]) AS_VAR_SET([REGEN_JIT_COMMAND], ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host"]) AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"]) From 21336aa12762ffe33bac83c7bd7992fcf42eee10 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 1 May 2024 10:31:00 +0800 Subject: [PATCH 129/217] gh-118201: Accomodate flaky behavior of `os.sysconf` on iOS (GH-118453) --- Lib/test/support/os_helper.py | 3 ++- Lib/test/test_os.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 8071c248b9b67e..891405943b78c5 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -632,7 +632,8 @@ def fd_count(): if hasattr(os, 'sysconf'): try: MAXFD = os.sysconf("SC_OPEN_MAX") - except OSError: + except (OSError, ValueError): + # gh-118201: ValueError is raised intermittently on iOS pass old_modes = None diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 6a34f48f7873ee..eaa676673f8af0 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2365,6 +2365,7 @@ def test_fchown(self): support.is_emscripten or support.is_wasi, "musl libc issue on Emscripten/WASI, bpo-46390" ) + @unittest.skipIf(support.is_apple_mobile, "gh-118201: Test is flaky on iOS") def test_fpathconf(self): self.check(os.pathconf, "PC_NAME_MAX") self.check(os.fpathconf, "PC_NAME_MAX") From 2520eed0a529be3815f70c43e1a5006deeee5596 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Wed, 1 May 2024 07:36:45 +0100 Subject: [PATCH 130/217] gh-116622: Add Android testbed (GH-117878) Add code and config for a minimal Android app, and instructions to build and run it. Improve Android build instructions in general. Add a tool subcommand to download the Gradle wrapper (with its binary blob). Android studio must be downloaded manually (due to the license). --- .github/CODEOWNERS | 12 ++ Android/README.md | 43 ++++- Android/android.py | 45 +++++- Android/testbed/.gitignore | 21 +++ Android/testbed/app/.gitignore | 1 + Android/testbed/app/build.gradle.kts | 129 +++++++++++++++ .../testbed/app/src/main/AndroidManifest.xml | 20 +++ Android/testbed/app/src/main/c/CMakeLists.txt | 9 ++ .../testbed/app/src/main/c/main_activity.c | 147 ++++++++++++++++++ .../java/org/python/testbed/MainActivity.kt | 61 ++++++++ Android/testbed/app/src/main/python/main.py | 17 ++ .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 3110 bytes .../app/src/main/res/layout/activity_main.xml | 19 +++ .../app/src/main/res/values/strings.xml | 3 + Android/testbed/build.gradle.kts | 5 + Android/testbed/gradle.properties | 23 +++ .../gradle/wrapper/gradle-wrapper.properties | 6 + Android/testbed/settings.gradle.kts | 18 +++ ...-04-14-19-35-35.gh-issue-116622.8lpX-7.rst | 1 + 19 files changed, 570 insertions(+), 10 deletions(-) create mode 100644 Android/testbed/.gitignore create mode 100644 Android/testbed/app/.gitignore create mode 100644 Android/testbed/app/build.gradle.kts create mode 100644 Android/testbed/app/src/main/AndroidManifest.xml create mode 100644 Android/testbed/app/src/main/c/CMakeLists.txt create mode 100644 Android/testbed/app/src/main/c/main_activity.c create mode 100644 Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt create mode 100644 Android/testbed/app/src/main/python/main.py create mode 100644 Android/testbed/app/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 Android/testbed/app/src/main/res/layout/activity_main.xml create mode 100644 Android/testbed/app/src/main/res/values/strings.xml create mode 100644 Android/testbed/build.gradle.kts create mode 100644 Android/testbed/gradle.properties create mode 100644 Android/testbed/gradle/wrapper/gradle-wrapper.properties create mode 100644 Android/testbed/settings.gradle.kts create mode 100644 Misc/NEWS.d/next/Build/2024-04-14-19-35-35.gh-issue-116622.8lpX-7.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 235bc78599400e..1f5f7e57dc4859 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -243,6 +243,18 @@ Lib/test/support/interpreters/ @ericsnowcurrently Modules/_xx*interp*module.c @ericsnowcurrently Lib/test/test_interpreters/ @ericsnowcurrently +# Android +**/*Android* @mhsmith +**/*android* @mhsmith + +# iOS (but not termios) +**/iOS* @freakboy3742 +**/ios* @freakboy3742 +**/*_iOS* @freakboy3742 +**/*_ios* @freakboy3742 +**/*-iOS* @freakboy3742 +**/*-ios* @freakboy3742 + # WebAssembly /Tools/wasm/ @brettcannon diff --git a/Android/README.md b/Android/README.md index 5ed186e06e3951..f5f463ca116589 100644 --- a/Android/README.md +++ b/Android/README.md @@ -22,12 +22,25 @@ you don't already have the SDK, here's how to install it: `android-sdk/cmdline-tools/latest`. * `export ANDROID_HOME=/path/to/android-sdk` +The `android.py` script also requires the following commands to be on the `PATH`: + +* `curl` +* `java` +* `tar` +* `unzip` + ## Building -Building for Android requires doing a cross-build where you have a "build" -Python to help produce an Android build of CPython. This procedure has been -tested on Linux and macOS. +Python can be built for Android on any POSIX platform supported by the Android +development tools, which currently means Linux or macOS. This involves doing a +cross-build where you use a "build" Python (for your development machine) to +help produce a "host" Python for Android. + +First, make sure you have all the usual tools and libraries needed to build +Python for your development machine. The only Android tool you need to install +is the command line tools package above: the build script will download the +rest. The easiest way to do a build is to use the `android.py` script. You can either have it perform the entire build process from start to finish in one step, or @@ -43,9 +56,10 @@ The discrete steps for building via `android.py` are: ./android.py make-host HOST ``` -To see the possible values of HOST, run `./android.py configure-host --help`. +`HOST` identifies which architecture to build. To see the possible values, run +`./android.py configure-host --help`. -Or to do it all in a single command, run: +To do all steps in a single command, run: ```sh ./android.py build HOST @@ -62,3 +76,22 @@ call. For example, if you want a pydebug build that also caches the results from ```sh ./android.py build HOST -- -C --with-pydebug ``` + + +## Testing + +To run the Python test suite on Android: + +* Install Android Studio, if you don't already have it. +* Follow the instructions in the previous section to build all supported + architectures. +* Run `./android.py setup-testbed` to download the Gradle wrapper. +* Open the `testbed` directory in Android Studio. +* In the *Device Manager* dock, connect a device or start an emulator. + Then select it from the drop-down list in the toolbar. +* Click the "Run" button in the toolbar. +* The testbed app displays nothing on screen while running. To see its output, + open the [Logcat window](https://developer.android.com/studio/debug/logcat). + +To run specific tests, or pass any other arguments to the test suite, edit the +command line in testbed/app/src/main/python/main.py. diff --git a/Android/android.py b/Android/android.py index 5c57e53c415d2b..0a1393e61ddb0e 100755 --- a/Android/android.py +++ b/Android/android.py @@ -7,8 +7,9 @@ import subprocess import sys import sysconfig -from os.path import relpath +from os.path import basename, relpath from pathlib import Path +from tempfile import TemporaryDirectory SCRIPT_NAME = Path(__file__).name CHECKOUT = Path(__file__).resolve().parent.parent @@ -102,11 +103,17 @@ def unpack_deps(host): for name_ver in ["bzip2-1.0.8-1", "libffi-3.4.4-2", "openssl-3.0.13-1", "sqlite-3.45.1-0", "xz-5.4.6-0"]: filename = f"{name_ver}-{host}.tar.gz" - run(["wget", f"{deps_url}/{name_ver}/{filename}"]) + download(f"{deps_url}/{name_ver}/{filename}") run(["tar", "-xf", filename]) os.remove(filename) +def download(url, target_dir="."): + out_path = f"{target_dir}/{basename(url)}" + run(["curl", "-Lf", "-o", out_path, url]) + return out_path + + def configure_host_python(context): host_dir = subdir(context.host, clean=context.clean) @@ -160,6 +167,30 @@ def clean_all(context): delete_if_exists(CROSS_BUILD_DIR) +# To avoid distributing compiled artifacts without corresponding source code, +# the Gradle wrapper is not included in the CPython repository. Instead, we +# extract it from the Gradle release. +def setup_testbed(context): + ver_long = "8.7.0" + ver_short = ver_long.removesuffix(".0") + testbed_dir = CHECKOUT / "Android/testbed" + + for filename in ["gradlew", "gradlew.bat"]: + out_path = download( + f"https://raw.githubusercontent.com/gradle/gradle/v{ver_long}/{filename}", + testbed_dir) + os.chmod(out_path, 0o755) + + with TemporaryDirectory(prefix=SCRIPT_NAME) as temp_dir: + os.chdir(temp_dir) + bin_zip = download( + f"https://services.gradle.org/distributions/gradle-{ver_short}-bin.zip") + outer_jar = f"gradle-{ver_short}/lib/plugins/gradle-wrapper-{ver_short}.jar" + run(["unzip", bin_zip, outer_jar]) + run(["unzip", "-o", "-d", f"{testbed_dir}/gradle/wrapper", outer_jar, + "gradle-wrapper.jar"]) + + def main(): parser = argparse.ArgumentParser() subcommands = parser.add_subparsers(dest="subcommand") @@ -173,8 +204,11 @@ def main(): help="Run `configure` for Android") make_host = subcommands.add_parser("make-host", help="Run `make` for Android") - clean = subcommands.add_parser("clean", help="Delete files and directories " - "created by this script") + subcommands.add_parser( + "clean", help="Delete the cross-build directory") + subcommands.add_parser( + "setup-testbed", help="Download the testbed Gradle wrapper") + for subcommand in build, configure_build, configure_host: subcommand.add_argument( "--clean", action="store_true", default=False, dest="clean", @@ -194,7 +228,8 @@ def main(): "configure-host": configure_host_python, "make-host": make_host_python, "build": build_all, - "clean": clean_all} + "clean": clean_all, + "setup-testbed": setup_testbed} dispatch[context.subcommand](context) diff --git a/Android/testbed/.gitignore b/Android/testbed/.gitignore new file mode 100644 index 00000000000000..b9a7d611c943cf --- /dev/null +++ b/Android/testbed/.gitignore @@ -0,0 +1,21 @@ +# The Gradle wrapper should be downloaded by running `../android.py setup-testbed`. +/gradlew +/gradlew.bat +/gradle/wrapper/gradle-wrapper.jar + +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/deploymentTargetDropdown.xml +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/Android/testbed/app/.gitignore b/Android/testbed/app/.gitignore new file mode 100644 index 00000000000000..42afabfd2abebf --- /dev/null +++ b/Android/testbed/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Android/testbed/app/build.gradle.kts b/Android/testbed/app/build.gradle.kts new file mode 100644 index 00000000000000..7690d3fd86b2fd --- /dev/null +++ b/Android/testbed/app/build.gradle.kts @@ -0,0 +1,129 @@ +import com.android.build.api.variant.* + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +val PYTHON_DIR = File(projectDir, "../../..").canonicalPath +val PYTHON_CROSS_DIR = "$PYTHON_DIR/cross-build" +val ABIS = mapOf( + "arm64-v8a" to "aarch64-linux-android", + "x86_64" to "x86_64-linux-android", +) + +val PYTHON_VERSION = File("$PYTHON_DIR/Include/patchlevel.h").useLines { + for (line in it) { + val match = """#define PY_VERSION\s+"(\d+\.\d+)""".toRegex().find(line) + if (match != null) { + return@useLines match.groupValues[1] + } + } + throw GradleException("Failed to find Python version") +} + + +android { + namespace = "org.python.testbed" + compileSdk = 34 + + defaultConfig { + applicationId = "org.python.testbed" + minSdk = 21 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + ndk.abiFilters.addAll(ABIS.keys) + externalNativeBuild.cmake.arguments( + "-DPYTHON_CROSS_DIR=$PYTHON_CROSS_DIR", + "-DPYTHON_VERSION=$PYTHON_VERSION") + } + + externalNativeBuild.cmake { + path("src/main/c/CMakeLists.txt") + } + + // Set this property to something non-empty, otherwise it'll use the default + // list, which ignores asset directories beginning with an underscore. + aaptOptions.ignoreAssetsPattern = ".git" + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.11.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") +} + + +// Create some custom tasks to copy Python and its standard library from +// elsewhere in the repository. +androidComponents.onVariants { variant -> + generateTask(variant, variant.sources.assets!!) { + into("python") { + for (triplet in ABIS.values) { + for (subDir in listOf("include", "lib")) { + into(subDir) { + from("$PYTHON_CROSS_DIR/$triplet/prefix/$subDir") + include("python$PYTHON_VERSION/**") + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + } + into("lib/python$PYTHON_VERSION") { + // Uncomment this to pick up edits from the source directory + // without having to rerun `make install`. + // from("$PYTHON_DIR/Lib") + // duplicatesStrategy = DuplicatesStrategy.INCLUDE + + into("site-packages") { + from("$projectDir/src/main/python") + } + } + } + exclude("**/__pycache__") + } + + generateTask(variant, variant.sources.jniLibs!!) { + for ((abi, triplet) in ABIS.entries) { + into(abi) { + from("$PYTHON_CROSS_DIR/$triplet/prefix/lib") + include("libpython*.*.so") + include("lib*_python.so") + } + } + } +} + + +fun generateTask( + variant: ApplicationVariant, directories: SourceDirectories, + configure: GenerateTask.() -> Unit +) { + val taskName = "generate" + + listOf(variant.name, "Python", directories.name) + .map { it.replaceFirstChar(Char::uppercase) } + .joinToString("") + + directories.addGeneratedSourceDirectory( + tasks.register(taskName) { + into(outputDir) + configure() + }, + GenerateTask::outputDir) +} + + +// addGeneratedSourceDirectory requires the task to have a DirectoryProperty. +abstract class GenerateTask: Sync() { + @get:OutputDirectory + abstract val outputDir: DirectoryProperty +} diff --git a/Android/testbed/app/src/main/AndroidManifest.xml b/Android/testbed/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000000000..2be8a82d426099 --- /dev/null +++ b/Android/testbed/app/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/testbed/app/src/main/c/CMakeLists.txt b/Android/testbed/app/src/main/c/CMakeLists.txt new file mode 100644 index 00000000000000..1d5df9a73465b6 --- /dev/null +++ b/Android/testbed/app/src/main/c/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.4.1) +project(testbed) + +set(PREFIX_DIR ${PYTHON_CROSS_DIR}/${CMAKE_LIBRARY_ARCHITECTURE}/prefix) +include_directories(${PREFIX_DIR}/include/python${PYTHON_VERSION}) +link_directories(${PREFIX_DIR}/lib) +link_libraries(log python${PYTHON_VERSION}) + +add_library(main_activity SHARED main_activity.c) diff --git a/Android/testbed/app/src/main/c/main_activity.c b/Android/testbed/app/src/main/c/main_activity.c new file mode 100644 index 00000000000000..73aba4164d000f --- /dev/null +++ b/Android/testbed/app/src/main/c/main_activity.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +static void throw_runtime_exception(JNIEnv *env, const char *message) { + (*env)->ThrowNew( + env, + (*env)->FindClass(env, "java/lang/RuntimeException"), + message); +} + + +// --- Stdio redirection ------------------------------------------------------ + +// Most apps won't need this, because the Python-level sys.stdout and sys.stderr +// are redirected to the Android logcat by Python itself. However, in the +// testbed it's useful to redirect the native streams as well, to debug problems +// in the Python startup or redirection process. +// +// Based on +// https://github.com/beeware/briefcase-android-gradle-template/blob/v0.3.11/%7B%7B%20cookiecutter.safe_formal_name%20%7D%7D/app/src/main/cpp/native-lib.cpp + +typedef struct { + FILE *file; + int fd; + android_LogPriority priority; + char *tag; + int pipe[2]; +} StreamInfo; + +static StreamInfo STREAMS[] = { + {stdout, STDOUT_FILENO, ANDROID_LOG_INFO, "native.stdout", {-1, -1}}, + {stderr, STDERR_FILENO, ANDROID_LOG_WARN, "native.stderr", {-1, -1}}, + {NULL, -1, ANDROID_LOG_UNKNOWN, NULL, {-1, -1}}, +}; + +// The maximum length of a log message in bytes, including the level marker and +// tag, is defined as LOGGER_ENTRY_MAX_PAYLOAD in +// platform/system/logging/liblog/include/log/log.h. As of API level 30, messages +// longer than this will be be truncated by logcat. This limit has already been +// reduced at least once in the history of Android (from 4076 to 4068 between API +// level 23 and 26), so leave some headroom. +static const int MAX_BYTES_PER_WRITE = 4000; + +static void *redirection_thread(void *arg) { + StreamInfo *si = (StreamInfo*)arg; + ssize_t read_size; + char buf[MAX_BYTES_PER_WRITE]; + while ((read_size = read(si->pipe[0], buf, sizeof buf - 1)) > 0) { + buf[read_size] = '\0'; /* add null-terminator */ + __android_log_write(si->priority, si->tag, buf); + } + return 0; +} + +static char *redirect_stream(StreamInfo *si) { + /* make the FILE unbuffered, to ensure messages are never lost */ + if (setvbuf(si->file, 0, _IONBF, 0)) { + return "setvbuf"; + } + + /* create the pipe and redirect the file descriptor */ + if (pipe(si->pipe)) { + return "pipe"; + } + if (dup2(si->pipe[1], si->fd) == -1) { + return "dup2"; + } + + /* start the logging thread */ + pthread_t thr; + if ((errno = pthread_create(&thr, 0, redirection_thread, si))) { + return "pthread_create"; + } + if ((errno = pthread_detach(thr))) { + return "pthread_detach"; + } + return 0; +} + +JNIEXPORT void JNICALL Java_org_python_testbed_MainActivity_redirectStdioToLogcat( + JNIEnv *env, jobject obj +) { + for (StreamInfo *si = STREAMS; si->file; si++) { + char *error_prefix; + if ((error_prefix = redirect_stream(si))) { + char error_message[1024]; + snprintf(error_message, sizeof(error_message), + "%s: %s", error_prefix, strerror(errno)); + throw_runtime_exception(env, error_message); + return; + } + } +} + + +// --- Python intialization ---------------------------------------------------- + +static PyStatus set_config_string( + JNIEnv *env, PyConfig *config, wchar_t **config_str, jstring value +) { + const char *value_utf8 = (*env)->GetStringUTFChars(env, value, NULL); + PyStatus status = PyConfig_SetBytesString(config, config_str, value_utf8); + (*env)->ReleaseStringUTFChars(env, value, value_utf8); + return status; +} + +static void throw_status(JNIEnv *env, PyStatus status) { + throw_runtime_exception(env, status.err_msg ? status.err_msg : ""); +} + +JNIEXPORT void JNICALL Java_org_python_testbed_MainActivity_runPython( + JNIEnv *env, jobject obj, jstring home, jstring runModule +) { + PyConfig config; + PyStatus status; + PyConfig_InitIsolatedConfig(&config); + + status = set_config_string(env, &config, &config.home, home); + if (PyStatus_Exception(status)) { + throw_status(env, status); + return; + } + + status = set_config_string(env, &config, &config.run_module, runModule); + if (PyStatus_Exception(status)) { + throw_status(env, status); + return; + } + + // Some tests generate SIGPIPE and SIGXFSZ, which should be ignored. + config.install_signal_handlers = 1; + + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + throw_status(env, status); + return; + } + + Py_RunMain(); +} diff --git a/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt b/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt new file mode 100644 index 00000000000000..5a590d5d04e954 --- /dev/null +++ b/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt @@ -0,0 +1,61 @@ +package org.python.testbed + +import android.os.* +import android.system.Os +import android.widget.TextView +import androidx.appcompat.app.* +import java.io.* + +class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + // Python needs this variable to help it find the temporary directory, + // but Android only sets it on API level 33 and later. + Os.setenv("TMPDIR", cacheDir.toString(), false) + + val pythonHome = extractAssets() + System.loadLibrary("main_activity") + redirectStdioToLogcat() + runPython(pythonHome.toString(), "main") + findViewById(R.id.tvHello).text = "Python complete" + } + + private fun extractAssets() : File { + val pythonHome = File(filesDir, "python") + if (pythonHome.exists() && !pythonHome.deleteRecursively()) { + throw RuntimeException("Failed to delete $pythonHome") + } + extractAssetDir("python", filesDir) + return pythonHome + } + + private fun extractAssetDir(path: String, targetDir: File) { + val names = assets.list(path) + ?: throw RuntimeException("Failed to list $path") + val targetSubdir = File(targetDir, path) + if (!targetSubdir.mkdirs()) { + throw RuntimeException("Failed to create $targetSubdir") + } + + for (name in names) { + val subPath = "$path/$name" + val input: InputStream + try { + input = assets.open(subPath) + } catch (e: FileNotFoundException) { + extractAssetDir(subPath, targetDir) + continue + } + input.use { + File(targetSubdir, name).outputStream().use { output -> + input.copyTo(output) + } + } + } + } + + private external fun redirectStdioToLogcat() + private external fun runPython(home: String, runModule: String) +} \ No newline at end of file diff --git a/Android/testbed/app/src/main/python/main.py b/Android/testbed/app/src/main/python/main.py new file mode 100644 index 00000000000000..a1b6def34ede81 --- /dev/null +++ b/Android/testbed/app/src/main/python/main.py @@ -0,0 +1,17 @@ +import runpy +import signal +import sys + +# Some tests use SIGUSR1, but that's blocked by default in an Android app in +# order to make it available to `sigwait` in the "Signal Catcher" thread. That +# thread's functionality is only relevant to the JVM ("forcing GC (no HPROF) and +# profile save"), so disabling it should not weaken the tests. +signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGUSR1]) + +# To run specific tests, or pass any other arguments to the test suite, edit +# this command line. +sys.argv[1:] = [ + "--use", "all,-cpu", + "--verbose3", +] +runpy.run_module("test") diff --git a/Android/testbed/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/Android/testbed/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..741d6580d60e05080e71e845240863f6b273333c GIT binary patch literal 3110 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 literal 0 HcmV?d00001 diff --git a/Android/testbed/app/src/main/res/layout/activity_main.xml b/Android/testbed/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000000000..21398609ec9c78 --- /dev/null +++ b/Android/testbed/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/Android/testbed/app/src/main/res/values/strings.xml b/Android/testbed/app/src/main/res/values/strings.xml new file mode 100644 index 00000000000000..352d2f9e885a2a --- /dev/null +++ b/Android/testbed/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Python testbed + \ No newline at end of file diff --git a/Android/testbed/build.gradle.kts b/Android/testbed/build.gradle.kts new file mode 100644 index 00000000000000..53f4a67287fcc5 --- /dev/null +++ b/Android/testbed/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.application") version "8.2.2" apply false + id("org.jetbrains.kotlin.android") version "1.9.22" apply false +} \ No newline at end of file diff --git a/Android/testbed/gradle.properties b/Android/testbed/gradle.properties new file mode 100644 index 00000000000000..3c5031eb7d63f7 --- /dev/null +++ b/Android/testbed/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Android/testbed/gradle/wrapper/gradle-wrapper.properties b/Android/testbed/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000000..2dc3339a3ef213 --- /dev/null +++ b/Android/testbed/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Feb 19 20:29:06 GMT 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Android/testbed/settings.gradle.kts b/Android/testbed/settings.gradle.kts new file mode 100644 index 00000000000000..5e08773e02450f --- /dev/null +++ b/Android/testbed/settings.gradle.kts @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "Python testbed" +include(":app") + \ No newline at end of file diff --git a/Misc/NEWS.d/next/Build/2024-04-14-19-35-35.gh-issue-116622.8lpX-7.rst b/Misc/NEWS.d/next/Build/2024-04-14-19-35-35.gh-issue-116622.8lpX-7.rst new file mode 100644 index 00000000000000..c270e59cd54c18 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-04-14-19-35-35.gh-issue-116622.8lpX-7.rst @@ -0,0 +1 @@ +A testbed project was added to run the test suite on Android. From fc7e1aa3c001bbce25973261fba457035719a559 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 1 May 2024 07:44:01 +0100 Subject: [PATCH 131/217] GH-117881: fix athrow().throw()/asend().throw() concurrent access (GH-117882) --- Lib/test/test_asyncgen.py | 199 +++++++++++++++++- ...-04-15-07-37-09.gh-issue-117881.07H0wI.rst | 1 + Objects/genobject.c | 37 ++++ 3 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-15-07-37-09.gh-issue-117881.07H0wI.rst diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index a1e9e1b89c6a2c..1985ede656e7a0 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -393,6 +393,151 @@ async def gen(): r'anext\(\): asynchronous generator is already running'): an.__next__() + with self.assertRaisesRegex(RuntimeError, + r"cannot reuse already awaited __anext__\(\)/asend\(\)"): + an.send(None) + + def test_async_gen_asend_throw_concurrent_with_send(self): + import types + + @types.coroutine + def _async_yield(v): + return (yield v) + + class MyExc(Exception): + pass + + async def agenfn(): + while True: + try: + await _async_yield(None) + except MyExc: + pass + return + yield + + + agen = agenfn() + gen = agen.asend(None) + gen.send(None) + gen2 = agen.asend(None) + + with self.assertRaisesRegex(RuntimeError, + r'anext\(\): asynchronous generator is already running'): + gen2.throw(MyExc) + + with self.assertRaisesRegex(RuntimeError, + r"cannot reuse already awaited __anext__\(\)/asend\(\)"): + gen2.send(None) + + def test_async_gen_athrow_throw_concurrent_with_send(self): + import types + + @types.coroutine + def _async_yield(v): + return (yield v) + + class MyExc(Exception): + pass + + async def agenfn(): + while True: + try: + await _async_yield(None) + except MyExc: + pass + return + yield + + + agen = agenfn() + gen = agen.asend(None) + gen.send(None) + gen2 = agen.athrow(MyExc) + + with self.assertRaisesRegex(RuntimeError, + r'athrow\(\): asynchronous generator is already running'): + gen2.throw(MyExc) + + with self.assertRaisesRegex(RuntimeError, + r"cannot reuse already awaited aclose\(\)/athrow\(\)"): + gen2.send(None) + + def test_async_gen_asend_throw_concurrent_with_throw(self): + import types + + @types.coroutine + def _async_yield(v): + return (yield v) + + class MyExc(Exception): + pass + + async def agenfn(): + try: + yield + except MyExc: + pass + while True: + try: + await _async_yield(None) + except MyExc: + pass + + + agen = agenfn() + with self.assertRaises(StopIteration): + agen.asend(None).send(None) + + gen = agen.athrow(MyExc) + gen.throw(MyExc) + gen2 = agen.asend(MyExc) + + with self.assertRaisesRegex(RuntimeError, + r'anext\(\): asynchronous generator is already running'): + gen2.throw(MyExc) + + with self.assertRaisesRegex(RuntimeError, + r"cannot reuse already awaited __anext__\(\)/asend\(\)"): + gen2.send(None) + + def test_async_gen_athrow_throw_concurrent_with_throw(self): + import types + + @types.coroutine + def _async_yield(v): + return (yield v) + + class MyExc(Exception): + pass + + async def agenfn(): + try: + yield + except MyExc: + pass + while True: + try: + await _async_yield(None) + except MyExc: + pass + + agen = agenfn() + with self.assertRaises(StopIteration): + agen.asend(None).send(None) + + gen = agen.athrow(MyExc) + gen.throw(MyExc) + gen2 = agen.athrow(None) + + with self.assertRaisesRegex(RuntimeError, + r'athrow\(\): asynchronous generator is already running'): + gen2.throw(MyExc) + + with self.assertRaisesRegex(RuntimeError, + r"cannot reuse already awaited aclose\(\)/athrow\(\)"): + gen2.send(None) + def test_async_gen_3_arg_deprecation_warning(self): async def gen(): yield 123 @@ -1571,6 +1716,8 @@ async def main(): self.assertIsInstance(message['exception'], ZeroDivisionError) self.assertIn('unhandled exception during asyncio.run() shutdown', message['message']) + del message, messages + gc_collect() def test_async_gen_expression_01(self): async def arange(n): @@ -1624,6 +1771,7 @@ async def main(): asyncio.run(main()) self.assertEqual([], messages) + gc_collect() def test_async_gen_await_same_anext_coro_twice(self): async def async_iterate(): @@ -1809,9 +1957,56 @@ class MyException(Exception): g = gen() with self.assertRaises(MyException): g.aclose().throw(MyException) - del g - gc_collect() + del g + gc_collect() # does not warn unawaited + + def test_asend_send_already_running(self): + @types.coroutine + def _async_yield(v): + return (yield v) + + async def agenfn(): + while True: + await _async_yield(1) + return + yield + + agen = agenfn() + gen = agen.asend(None) + gen.send(None) + gen2 = agen.asend(None) + + with self.assertRaisesRegex(RuntimeError, + r'anext\(\): asynchronous generator is already running'): + gen2.send(None) + + del gen2 + gc_collect() # does not warn unawaited + + + def test_athrow_send_already_running(self): + @types.coroutine + def _async_yield(v): + return (yield v) + + async def agenfn(): + while True: + await _async_yield(1) + return + yield + + agen = agenfn() + gen = agen.asend(None) + gen.send(None) + gen2 = agen.athrow(Exception) + + with self.assertRaisesRegex(RuntimeError, + r'athrow\(\): asynchronous generator is already running'): + gen2.send(None) + + del gen2 + gc_collect() # does not warn unawaited if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-15-07-37-09.gh-issue-117881.07H0wI.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-15-07-37-09.gh-issue-117881.07H0wI.rst new file mode 100644 index 00000000000000..75b34269695693 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-15-07-37-09.gh-issue-117881.07H0wI.rst @@ -0,0 +1 @@ +prevent concurrent access to an async generator via athrow().throw() or asend().throw() diff --git a/Objects/genobject.c b/Objects/genobject.c index a1ed1cbd2bfb58..89bb21a8674b9f 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1774,6 +1774,7 @@ async_gen_asend_send(PyAsyncGenASend *o, PyObject *arg) if (o->ags_state == AWAITABLE_STATE_INIT) { if (o->ags_gen->ag_running_async) { + o->ags_state = AWAITABLE_STATE_CLOSED; PyErr_SetString( PyExc_RuntimeError, "anext(): asynchronous generator is already running"); @@ -1817,10 +1818,24 @@ async_gen_asend_throw(PyAsyncGenASend *o, PyObject *const *args, Py_ssize_t narg return NULL; } + if (o->ags_state == AWAITABLE_STATE_INIT) { + if (o->ags_gen->ag_running_async) { + o->ags_state = AWAITABLE_STATE_CLOSED; + PyErr_SetString( + PyExc_RuntimeError, + "anext(): asynchronous generator is already running"); + return NULL; + } + + o->ags_state = AWAITABLE_STATE_ITER; + o->ags_gen->ag_running_async = 1; + } + result = gen_throw((PyGenObject*)o->ags_gen, args, nargs); result = async_gen_unwrap_value(o->ags_gen, result); if (result == NULL) { + o->ags_gen->ag_running_async = 0; o->ags_state = AWAITABLE_STATE_CLOSED; } @@ -2209,10 +2224,31 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na return NULL; } + if (o->agt_state == AWAITABLE_STATE_INIT) { + if (o->agt_gen->ag_running_async) { + o->agt_state = AWAITABLE_STATE_CLOSED; + if (o->agt_args == NULL) { + PyErr_SetString( + PyExc_RuntimeError, + "aclose(): asynchronous generator is already running"); + } + else { + PyErr_SetString( + PyExc_RuntimeError, + "athrow(): asynchronous generator is already running"); + } + return NULL; + } + + o->agt_state = AWAITABLE_STATE_ITER; + o->agt_gen->ag_running_async = 1; + } + retval = gen_throw((PyGenObject*)o->agt_gen, args, nargs); if (o->agt_args) { retval = async_gen_unwrap_value(o->agt_gen, retval); if (retval == NULL) { + o->agt_gen->ag_running_async = 0; o->agt_state = AWAITABLE_STATE_CLOSED; } return retval; @@ -2226,6 +2262,7 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na return NULL; } if (retval == NULL) { + o->agt_gen->ag_running_async = 0; o->agt_state = AWAITABLE_STATE_CLOSED; } if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) || From 21c09d9f8195433f34b72ddfa25dd1bda3019ed7 Mon Sep 17 00:00:00 2001 From: da-woods Date: Wed, 1 May 2024 09:33:28 +0100 Subject: [PATCH 132/217] Test syntax error on comma-less tuple-style sequence patterns (#115485) Adds a test that length-1 tuple-style sequence patterns must end in a comma, since there isn't currently one. Spotted while reviewing Cython's proposed implementation of the pattern matching syntax (https://github.com/cython/cython/pull/4897#discussion_r1489177169) where there was a bug my the reimplementation that wasn't caught against the CPython tests here. --- Lib/test/test_patma.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index 298e78ccee3875..1bdab125dc6ef0 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -2957,6 +2957,14 @@ def test_invalid_syntax_3(self): pass """) + def test_len1_tuple_sequence_pattern_comma(self): + # correct syntax would be `case(*x,):` + self.assert_syntax_error(""" + match ...: + case (*x): + pass + """) + def test_mapping_pattern_keys_may_only_match_literals_and_attribute_lookups(self): self.assert_syntax_error(""" match ...: From f6fab21721c8aedc5dca97dbeb6292a067c19bf1 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 1 May 2024 11:34:50 +0100 Subject: [PATCH 133/217] GH-118095: Make invalidating and clearing executors memory safe (GH-118459) --- Include/cpython/optimizer.h | 3 +- Objects/codeobject.c | 3 +- Python/bytecodes.c | 12 +++- Python/generated_cases.c.h | 12 +++- Python/optimizer.c | 115 +++++++++++++++++++++++++----------- 5 files changed, 103 insertions(+), 42 deletions(-) diff --git a/Include/cpython/optimizer.h b/Include/cpython/optimizer.h index 819251a25bb242..a169280b26a6ad 100644 --- a/Include/cpython/optimizer.h +++ b/Include/cpython/optimizer.h @@ -24,6 +24,7 @@ typedef struct { uint8_t opcode; uint8_t oparg; uint8_t valid; + uint8_t linked; int index; // Index of ENTER_EXECUTOR (if code isn't NULL, below). _PyBloomFilter bloom; _PyExecutorLinkListNode links; @@ -135,7 +136,7 @@ PyAPI_FUNC(_PyOptimizerObject *) PyUnstable_GetOptimizer(void); PyAPI_FUNC(_PyExecutorObject *) PyUnstable_GetExecutor(PyCodeObject *code, int offset); void _Py_ExecutorInit(_PyExecutorObject *, const _PyBloomFilter *); -void _Py_ExecutorClear(_PyExecutorObject *); +void _Py_ExecutorDetach(_PyExecutorObject *); void _Py_BloomFilter_Init(_PyBloomFilter *); void _Py_BloomFilter_Add(_PyBloomFilter *bloom, void *obj); PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj); diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 605167c5c0bd3a..810f847485acda 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1504,7 +1504,8 @@ clear_executors(PyCodeObject *co) assert(co->co_executors); for (int i = 0; i < co->co_executors->size; i++) { if (co->co_executors->executors[i]) { - _Py_ExecutorClear(co->co_executors->executors[i]); + _Py_ExecutorDetach(co->co_executors->executors[i]); + assert(co->co_executors->executors[i] == NULL); } } PyMem_Free(co->co_executors); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 28766d6d97cc5f..002b5a3529c127 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -163,9 +163,15 @@ dummy_func( if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { CHECK_EVAL_BREAKER(); } - #if ENABLE_SPECIALIZATION - FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); - #endif /* ENABLE_SPECIALIZATION */ + assert(this_instr->op.code == RESUME || + this_instr->op.code == RESUME_CHECK || + this_instr->op.code == INSTRUMENTED_RESUME || + this_instr->op.code == ENTER_EXECUTOR); + if (this_instr->op.code == RESUME) { + #if ENABLE_SPECIALIZATION + FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); + #endif /* ENABLE_SPECIALIZATION */ + } } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 602cce4beb1e28..32b485ecb9d8cc 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4961,9 +4961,15 @@ if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { CHECK_EVAL_BREAKER(); } - #if ENABLE_SPECIALIZATION - FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); - #endif /* ENABLE_SPECIALIZATION */ + assert(this_instr->op.code == RESUME || + this_instr->op.code == RESUME_CHECK || + this_instr->op.code == INSTRUMENTED_RESUME || + this_instr->op.code == ENTER_EXECUTOR); + if (this_instr->op.code == RESUME) { + #if ENABLE_SPECIALIZATION + FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); + #endif /* ENABLE_SPECIALIZATION */ + } } DISPATCH(); } diff --git a/Python/optimizer.c b/Python/optimizer.c index a5e7430c46450e..6576aa1cddc033 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -75,7 +75,7 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorO Py_INCREF(executor); if (instr->op.code == ENTER_EXECUTOR) { assert(index == instr->op.arg); - _Py_ExecutorClear(code->co_executors->executors[index]); + _Py_ExecutorDetach(code->co_executors->executors[index]); } else { assert(code->co_executors->size == index); @@ -270,10 +270,14 @@ static PyMethodDef executor_methods[] = { ///////////////////// Experimental UOp Optimizer ///////////////////// +static int executor_clear(_PyExecutorObject *executor); +static void unlink_executor(_PyExecutorObject *executor); + static void uop_dealloc(_PyExecutorObject *self) { _PyObject_GC_UNTRACK(self); - _Py_ExecutorClear(self); + assert(self->vm_data.code == NULL); + unlink_executor(self); #ifdef _Py_JIT _PyJIT_Free(self); #endif @@ -379,13 +383,6 @@ PySequenceMethods uop_as_sequence = { .sq_item = (ssizeargfunc)uop_item, }; -static int -executor_clear(PyObject *o) -{ - _Py_ExecutorClear((_PyExecutorObject *)o); - return 0; -} - static int executor_traverse(PyObject *o, visitproc visit, void *arg) { @@ -412,7 +409,7 @@ PyTypeObject _PyUOpExecutor_Type = { .tp_as_sequence = &uop_as_sequence, .tp_methods = executor_methods, .tp_traverse = executor_traverse, - .tp_clear = executor_clear, + .tp_clear = (inquiry)executor_clear, .tp_is_gc = executor_is_gc, }; @@ -1190,6 +1187,7 @@ init_cold_exit_executor(_PyExecutorObject *executor, int oparg) inst->opcode = _COLD_EXIT; inst->oparg = oparg; executor->vm_data.valid = true; + executor->vm_data.linked = false; for (int i = 0; i < BLOOM_FILTER_WORDS; i++) { assert(executor->vm_data.bloom.bits[i] == 0); } @@ -1328,7 +1326,7 @@ PyTypeObject _PyCounterExecutor_Type = { .tp_dealloc = (destructor)counter_dealloc, .tp_methods = executor_methods, .tp_traverse = executor_traverse, - .tp_clear = executor_clear, + .tp_clear = (inquiry)executor_clear, }; static int @@ -1503,15 +1501,13 @@ link_executor(_PyExecutorObject *executor) links->next = NULL; } else { - _PyExecutorObject *next = head->vm_data.links.next; - links->previous = head; - links->next = next; - if (next != NULL) { - next->vm_data.links.previous = executor; - } - head->vm_data.links.next = executor; + assert(head->vm_data.links.previous == NULL); + links->previous = NULL; + links->next = head; + head->vm_data.links.previous = executor; + interp->executor_list_head = executor; } - executor->vm_data.valid = true; + executor->vm_data.linked = true; /* executor_list_head must be first in list */ assert(interp->executor_list_head->vm_data.links.previous == NULL); } @@ -1519,7 +1515,11 @@ link_executor(_PyExecutorObject *executor) static void unlink_executor(_PyExecutorObject *executor) { + if (!executor->vm_data.linked) { + return; + } _PyExecutorLinkListNode *links = &executor->vm_data.links; + assert(executor->vm_data.valid); _PyExecutorObject *next = links->next; _PyExecutorObject *prev = links->previous; if (next != NULL) { @@ -1534,7 +1534,7 @@ unlink_executor(_PyExecutorObject *executor) assert(interp->executor_list_head == executor); interp->executor_list_head = next; } - executor->vm_data.valid = false; + executor->vm_data.linked = false; } /* This must be called by optimizers before using the executor */ @@ -1548,23 +1548,15 @@ _Py_ExecutorInit(_PyExecutorObject *executor, const _PyBloomFilter *dependency_s link_executor(executor); } -/* This must be called by executors during dealloc */ +/* Detaches the executor from the code object (if any) that + * holds a reference to it */ void -_Py_ExecutorClear(_PyExecutorObject *executor) +_Py_ExecutorDetach(_PyExecutorObject *executor) { - if (!executor->vm_data.valid) { - return; - } - unlink_executor(executor); PyCodeObject *code = executor->vm_data.code; if (code == NULL) { return; } - for (uint32_t i = 0; i < executor->exit_count; i++) { - Py_DECREF(executor->exits[i].executor); - executor->exits[i].executor = &COLD_EXITS[i]; - executor->exits[i].temperature = initial_unreachable_backoff_counter(); - } _Py_CODEUNIT *instruction = &_PyCode_CODE(code)[executor->vm_data.index]; assert(instruction->op.code == ENTER_EXECUTOR); int index = instruction->op.arg; @@ -1572,7 +1564,36 @@ _Py_ExecutorClear(_PyExecutorObject *executor) instruction->op.code = executor->vm_data.opcode; instruction->op.arg = executor->vm_data.oparg; executor->vm_data.code = NULL; - Py_CLEAR(code->co_executors->executors[index]); + code->co_executors->executors[index] = NULL; + Py_DECREF(executor); +} + +static int +executor_clear(_PyExecutorObject *executor) +{ + if (!executor->vm_data.valid) { + return 0; + } + assert(executor->vm_data.valid == 1); + unlink_executor(executor); + executor->vm_data.valid = 0; + /* It is possible for an executor to form a reference + * cycle with itself, so decref'ing a side exit could + * free the executor unless we hold a strong reference to it + */ + Py_INCREF(executor); + for (uint32_t i = 0; i < executor->exit_count; i++) { + const _PyExecutorObject *cold = &COLD_EXITS[i]; + const _PyExecutorObject *side = executor->exits[i].executor; + executor->exits[i].temperature = initial_unreachable_backoff_counter(); + if (side != cold) { + executor->exits[i].executor = cold; + Py_DECREF(side); + } + } + _Py_ExecutorDetach(executor); + Py_DECREF(executor); + return 0; } void @@ -1593,17 +1614,42 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is _Py_BloomFilter_Add(&obj_filter, obj); /* Walk the list of executors */ /* TO DO -- Use a tree to avoid traversing as many objects */ + bool no_memory = false; + PyObject *invalidate = PyList_New(0); + if (invalidate == NULL) { + PyErr_Clear(); + no_memory = true; + } + /* Clearing an executor can deallocate others, so we need to make a list of + * executors to invalidate first */ for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) { assert(exec->vm_data.valid); _PyExecutorObject *next = exec->vm_data.links.next; if (bloom_filter_may_contain(&exec->vm_data.bloom, &obj_filter)) { - _Py_ExecutorClear(exec); + unlink_executor(exec); + if (no_memory) { + exec->vm_data.valid = 0; + } else { + if (PyList_Append(invalidate, (PyObject *)exec) < 0) { + PyErr_Clear(); + no_memory = true; + exec->vm_data.valid = 0; + } + } if (is_invalidation) { OPT_STAT_INC(executors_invalidated); } } exec = next; } + if (invalidate != NULL) { + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) { + _PyExecutorObject *exec = (_PyExecutorObject *)PyList_GET_ITEM(invalidate, i); + executor_clear(exec); + } + Py_DECREF(invalidate); + } + return; } /* Invalidate all executors */ @@ -1612,12 +1658,13 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation) { while (interp->executor_list_head) { _PyExecutorObject *executor = interp->executor_list_head; + assert(executor->vm_data.valid == 1 && executor->vm_data.linked == 1); if (executor->vm_data.code) { // Clear the entire code object so its co_executors array be freed: _PyCode_Clear_Executors(executor->vm_data.code); } else { - _Py_ExecutorClear(executor); + executor_clear(executor); } if (is_invalidation) { OPT_STAT_INC(executors_invalidated); From c1bf4874c1e9db2beda1d62c8c241229783c789b Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 1 May 2024 12:01:16 +0100 Subject: [PATCH 134/217] gh-116767: fix crash on 'async with' with many context managers (GH-118348) Account for `add_stopiteration_handler` pushing a block for `async with`. To allow generator functions that previously almost hit the `CO_MAXBLOCKS` limit by nesting non-async blocks, the limit is increased by 1. This increase allows one more block in non-generator functions. --- Include/cpython/code.h | 2 +- Lib/test/test_syntax.py | 36 ++++++++++++++++--- ...-04-27-16-23-29.gh-issue-116767.z9UFpr.rst | 1 + Python/compile.c | 18 ++++++++-- 4 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst diff --git a/Include/cpython/code.h b/Include/cpython/code.h index b0e226e0e1971a..ef8f9304ccab56 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -226,7 +226,7 @@ struct PyCodeObject _PyCode_DEF(1); */ #define PY_PARSER_REQUIRES_FUTURE_KEYWORD -#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */ +#define CO_MAXBLOCKS 21 /* Max static block nesting within a function */ PyAPI_DATA(PyTypeObject) PyCode_Type; diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index e9bec3317811dd..de783f714509a3 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2392,13 +2392,40 @@ def bug(): code += "): yield a" return code - CO_MAXBLOCKS = 20 # static nesting limit of the compiler + CO_MAXBLOCKS = 21 # static nesting limit of the compiler + MAX_MANAGERS = CO_MAXBLOCKS - 1 # One for the StopIteration block - for n in range(CO_MAXBLOCKS): + for n in range(MAX_MANAGERS): with self.subTest(f"within range: {n=}"): compile(get_code(n), "", "exec") - for n in range(CO_MAXBLOCKS, CO_MAXBLOCKS + 5): + for n in range(MAX_MANAGERS, MAX_MANAGERS + 5): + with self.subTest(f"out of range: {n=}"): + self._check_error(get_code(n), "too many statically nested blocks") + + @support.cpython_only + def test_async_with_statement_many_context_managers(self): + # See gh-116767 + + def get_code(n): + code = [ textwrap.dedent(""" + async def bug(): + async with ( + a + """) ] + for i in range(n): + code.append(f" as a{i}, a\n") + code.append("): yield a") + return "".join(code) + + CO_MAXBLOCKS = 21 # static nesting limit of the compiler + MAX_MANAGERS = CO_MAXBLOCKS - 1 # One for the StopIteration block + + for n in range(MAX_MANAGERS): + with self.subTest(f"within range: {n=}"): + compile(get_code(n), "", "exec") + + for n in range(MAX_MANAGERS, MAX_MANAGERS + 5): with self.subTest(f"out of range: {n=}"): self._check_error(get_code(n), "too many statically nested blocks") @@ -2536,7 +2563,8 @@ def test_syntax_error_on_deeply_nested_blocks(self): while 20: while 21: while 22: - break + while 23: + break """ self._check_error(source, "too many statically nested blocks") diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst new file mode 100644 index 00000000000000..cec2041d12ee15 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst @@ -0,0 +1 @@ +Fix crash in compiler on 'async with' that has many context managers. diff --git a/Python/compile.c b/Python/compile.c index ca5551a8e64ab0..4e94f9297a32d1 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -113,7 +113,8 @@ compiler IR. enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END, WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER, - EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR }; + EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR, + STOP_ITERATION }; struct fblockinfo { enum fblocktype fb_type; @@ -1503,6 +1504,7 @@ compiler_unwind_fblock(struct compiler *c, location *ploc, case EXCEPTION_HANDLER: case EXCEPTION_GROUP_HANDLER: case ASYNC_COMPREHENSION_GENERATOR: + case STOP_ITERATION: return SUCCESS; case FOR_LOOP: @@ -2232,14 +2234,26 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f c->u->u_metadata.u_argcount = asdl_seq_LEN(args->args); c->u->u_metadata.u_posonlyargcount = asdl_seq_LEN(args->posonlyargs); c->u->u_metadata.u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs); + + NEW_JUMP_TARGET_LABEL(c, start); + USE_LABEL(c, start); + bool add_stopiteration_handler = c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator; + if (add_stopiteration_handler) { + /* wrap_in_stopiteration_handler will push a block, so we need to account for that */ + RETURN_IF_ERROR( + compiler_push_fblock(c, NO_LOCATION, STOP_ITERATION, + start, NO_LABEL, NULL)); + } + for (Py_ssize_t i = first_instr; i < asdl_seq_LEN(body); i++) { VISIT_IN_SCOPE(c, stmt, (stmt_ty)asdl_seq_GET(body, i)); } - if (c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator) { + if (add_stopiteration_handler) { if (wrap_in_stopiteration_handler(c) < 0) { compiler_exit_scope(c); return ERROR; } + compiler_pop_fblock(c, STOP_ITERATION, start); } PyCodeObject *co = optimize_and_assemble(c, 1); compiler_exit_scope(c); From 4a08a75cf4c490f7c43ede69bdf6e5a79c6a3af3 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 1 May 2024 14:42:10 +0100 Subject: [PATCH 135/217] gh-99180: Remove traceback anchors in return and assign statements that cover all the displayed range (#112670) --- Lib/test/test_traceback.py | 233 +++++++++++++++--- Lib/traceback.py | 38 ++- ...3-12-03-18-21-59.gh-issue-99180.5m0V0q.rst | 2 + 3 files changed, 240 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-03-18-21-59.gh-issue-99180.5m0V0q.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 8927fccc289320..41f82512480d4f 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -695,7 +695,6 @@ def f_with_multiline(): ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n' ' return compile(code, "?", "exec")\n' - ' ~~~~~~~^^^^^^^^^^^^^^^^^^^\n' ' File "?", line 7\n' ' foo(a, z\n' ' ^' @@ -785,8 +784,8 @@ def f_with_binary_operator(): def test_caret_for_binary_operators_with_spaces_and_parenthesis(self): def f_with_binary_operator(): a = 1 - b = "" - return ( a ) +b + b = c = "" + return ( a ) +b + c lineno_f = f_with_binary_operator.__code__.co_firstlineno expected_error = ( @@ -795,7 +794,7 @@ def f_with_binary_operator(): ' callable()\n' ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' - ' return ( a ) +b\n' + ' return ( a ) +b + c\n' ' ~~~~~~~~~~^~\n' ) result_lines = self.get_exception(f_with_binary_operator) @@ -983,7 +982,7 @@ def f1(a): def f2(b): raise RuntimeError("fail") return f2 - return f1("x")("y") + return f1("x")("y")("z") lineno_f = f_with_call.__code__.co_firstlineno expected_error = ( @@ -992,7 +991,7 @@ def f2(b): ' callable()\n' ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+5}, in f_with_call\n' - ' return f1("x")("y")\n' + ' return f1("x")("y")("z")\n' ' ~~~~~~~^^^^^\n' f' File "{__file__}", line {lineno_f+3}, in f2\n' ' raise RuntimeError("fail")\n' @@ -1507,6 +1506,184 @@ def f(): ' raise MemoryError()'] self.assertEqual(actual, expected) + def test_anchors_for_simple_return_statements_are_elided(self): + def g(): + 1/0 + + def f(): + return g() + + result_lines = self.get_exception(f) + expected = ['Traceback (most recent call last):', + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + " callable()", + " ~~~~~~~~^^", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + " return g()", + f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g", + " 1/0", + " ~^~" + ] + self.assertEqual(result_lines, expected) + + def g(): + 1/0 + + def f(): + return g() + 1 + + result_lines = self.get_exception(f) + expected = ['Traceback (most recent call last):', + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + " callable()", + " ~~~~~~~~^^", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + " return g() + 1", + " ~^^", + f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g", + " 1/0", + " ~^~" + ] + self.assertEqual(result_lines, expected) + + def g(*args): + 1/0 + + def f(): + return g(1, + 2, 4, + 5) + + result_lines = self.get_exception(f) + expected = ['Traceback (most recent call last):', + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + " callable()", + " ~~~~~~~~^^", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + " return g(1,", + " 2, 4,", + " 5)", + f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g", + " 1/0", + " ~^~" + ] + self.assertEqual(result_lines, expected) + + def g(*args): + 1/0 + + def f(): + return g(1, + 2, 4, + 5) + 1 + + result_lines = self.get_exception(f) + expected = ['Traceback (most recent call last):', + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + " callable()", + " ~~~~~~~~^^", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + " return g(1,", + " ~^^^", + " 2, 4,", + " ^^^^^", + " 5) + 1", + " ^^", + f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g", + " 1/0", + " ~^~" + ] + self.assertEqual(result_lines, expected) + + def test_anchors_for_simple_assign_statements_are_elided(self): + def g(): + 1/0 + + def f(): + x = g() + + result_lines = self.get_exception(f) + expected = ['Traceback (most recent call last):', + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + " callable()", + " ~~~~~~~~^^", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + " x = g()", + f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g", + " 1/0", + " ~^~" + ] + self.assertEqual(result_lines, expected) + + def g(*args): + 1/0 + + def f(): + x = g(1, + 2, 3, + 4) + + result_lines = self.get_exception(f) + expected = ['Traceback (most recent call last):', + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + " callable()", + " ~~~~~~~~^^", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + " x = g(1,", + " 2, 3,", + " 4)", + f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g", + " 1/0", + " ~^~" + ] + self.assertEqual(result_lines, expected) + + def g(): + 1/0 + + def f(): + x = y = g() + + result_lines = self.get_exception(f) + expected = ['Traceback (most recent call last):', + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + " callable()", + " ~~~~~~~~^^", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + " x = y = g()", + " ~^^", + f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g", + " 1/0", + " ~^~" + ] + self.assertEqual(result_lines, expected) + + def g(*args): + 1/0 + + def f(): + x = y = g(1, + 2, 3, + 4) + + result_lines = self.get_exception(f) + expected = ['Traceback (most recent call last):', + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + " callable()", + " ~~~~~~~~^^", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + " x = y = g(1,", + " ~^^^", + " 2, 3,", + " ^^^^^", + " 4)", + " ^^", + f" File \"{__file__}\", line {g.__code__.co_firstlineno + 1}, in g", + " 1/0", + " ~^~" + ] + self.assertEqual(result_lines, expected) + @requires_debug_ranges() class PurePythonTracebackErrorCaretTests( @@ -1701,7 +1878,7 @@ def f(): # Check a known (limited) number of recursive invocations def g(count=10): if count: - return g(count-1) + return g(count-1) + 1 raise ValueError with captured_output("stderr") as stderr_g: @@ -1715,13 +1892,13 @@ def g(count=10): lineno_g = g.__code__.co_firstlineno result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' ' [Previous line repeated 7 more times]\n' f' File "{__file__}", line {lineno_g+3}, in g\n' @@ -1760,13 +1937,10 @@ def h(count=10): ' ~^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ~^^^^^^^^^\n' ' [Previous line repeated 7 more times]\n' f' File "{__file__}", line {lineno_h+3}, in h\n' ' g()\n' @@ -1786,13 +1960,13 @@ def h(count=10): self.fail("no error raised") result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' @@ -1800,7 +1974,7 @@ def h(count=10): ) tb_line = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_g+80}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_g+77}, in _check_recursive_traceback_display\n' ' g(traceback._RECURSIVE_CUTOFF)\n' ' ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' ) @@ -1818,13 +1992,13 @@ def h(count=10): self.fail("no error raised") result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' - ' return g(count-1)\n' + ' return g(count-1) + 1\n' ' ~^^^^^^^^^\n' ' [Previous line repeated 1 more time]\n' f' File "{__file__}", line {lineno_g+3}, in g\n' @@ -1833,7 +2007,7 @@ def h(count=10): ) tb_line = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_g+112}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_g+109}, in _check_recursive_traceback_display\n' ' g(traceback._RECURSIVE_CUTOFF + 1)\n' ' ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' ) @@ -4287,11 +4461,14 @@ def foo(*args): x = {'a':{'b': None}} y = x['a']['b']['c'] - def baz(*args): - return foo(1,2,3,4) + def baz2(*args): + return (lambda *args: foo(*args))(1,2,3,4) + + def baz1(*args): + return baz2(1,2,3,4) def bar(): - return baz(1, + return baz1(1, 2,3 ,4) try: @@ -4305,10 +4482,10 @@ def bar(): boldr = traceback._ANSIColors.BOLD_RED reset = traceback._ANSIColors.RESET self.assertIn("y = " + red + "x['a']['b']" + reset + boldr + "['c']" + reset, lines) - self.assertIn("return " + red + "foo" + reset + boldr + "(1,2,3,4)" + reset, lines) - self.assertIn("return " + red + "baz" + reset + boldr + "(1," + reset, lines) - self.assertIn(boldr + "2,3" + reset, lines) - self.assertIn(boldr + ",4)" + reset, lines) + self.assertIn("return " + red + "(lambda *args: foo(*args))" + reset + boldr + "(1,2,3,4)" + reset, lines) + self.assertIn("return (lambda *args: " + red + "foo" + reset + boldr + "(*args)" + reset + ")(1,2,3,4)", lines) + self.assertIn("return baz2(1,2,3,4)", lines) + self.assertIn("return baz1(1,\n 2,3\n ,4)", lines) self.assertIn(red + "bar" + reset + boldr + "()" + reset, lines) def test_colorized_syntax_error(self): diff --git a/Lib/traceback.py b/Lib/traceback.py index fccec0c71c3695..6ce745f32d1324 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -617,13 +617,10 @@ def format_frame_summary(self, frame_summary, **kwargs): # attempt to parse for anchors anchors = None + show_carets = False with suppress(Exception): anchors = _extract_caret_anchors_from_line_segment(segment) - - # only use carets if there are anchors or the carets do not span all lines - show_carets = False - if anchors or all_lines[0][:start_offset].lstrip() or all_lines[-1][end_offset:].rstrip(): - show_carets = True + show_carets = self.should_show_carets(start_offset, end_offset, all_lines, anchors) result = [] @@ -737,6 +734,37 @@ def output_line(lineno): return ''.join(row) + def should_show_carets(self, start_offset, end_offset, all_lines, anchors): + with suppress(SyntaxError, ImportError): + import ast + tree = ast.parse('\n'.join(all_lines)) + statement = tree.body[0] + value = None + def _spawns_full_line(value): + return ( + value.lineno == 1 + and value.end_lineno == len(all_lines) + and value.col_offset == start_offset + and value.end_col_offset == end_offset + ) + match statement: + case ast.Return(value=ast.Call()): + if isinstance(statement.value.func, ast.Name): + value = statement.value + case ast.Assign(value=ast.Call()): + if ( + len(statement.targets) == 1 and + isinstance(statement.targets[0], ast.Name) + ): + value = statement.value + if value is not None and _spawns_full_line(value): + return False + if anchors: + return True + if all_lines[0][:start_offset].lstrip() or all_lines[-1][end_offset:].rstrip(): + return True + return False + def format(self, **kwargs): """Format the stack ready for printing. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-03-18-21-59.gh-issue-99180.5m0V0q.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-18-21-59.gh-issue-99180.5m0V0q.rst new file mode 100644 index 00000000000000..576626b702e75c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-18-21-59.gh-issue-99180.5m0V0q.rst @@ -0,0 +1,2 @@ +Elide uninformative traceback indicators in ``return`` and simple +``assignment`` statements. Patch by Pablo Galindo. From beb653cc24275025708758d444835db2ddbb74e4 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 2 May 2024 00:11:14 +1000 Subject: [PATCH 136/217] gh-117958: Expose JIT code via method in UOpExecutor (#117959) --- ...-04-18-03-49-41.gh-issue-117958.-EsfUs.rst | 2 ++ Python/optimizer.c | 25 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-18-03-49-41.gh-issue-117958.-EsfUs.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-18-03-49-41.gh-issue-117958.-EsfUs.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-18-03-49-41.gh-issue-117958.-EsfUs.rst new file mode 100644 index 00000000000000..c127786bc129b1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-18-03-49-41.gh-issue-117958.-EsfUs.rst @@ -0,0 +1,2 @@ +Added a ``get_jit_code()`` method to access JIT compiled machine code from the UOp Executor when the experimental JIT is enabled. Patch +by Anthony Shaw. diff --git a/Python/optimizer.c b/Python/optimizer.c index 6576aa1cddc033..9ba8d84a47dcd9 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -393,6 +393,29 @@ executor_traverse(PyObject *o, visitproc visit, void *arg) return 0; } +static PyObject * +get_jit_code(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ +#ifndef _Py_JIT + PyErr_SetString(PyExc_RuntimeError, "JIT support not enabled."); + return NULL; +#else + _PyExecutorObject *executor = (_PyExecutorObject *)self; + if (executor->jit_code == NULL || executor->jit_size == 0) { + Py_RETURN_NONE; + } + return PyBytes_FromStringAndSize(executor->jit_code, executor->jit_size); +#endif +} + +static PyMethodDef uop_executor_methods[] = { + { "is_valid", is_valid, METH_NOARGS, NULL }, + { "get_jit_code", get_jit_code, METH_NOARGS, NULL}, + { "get_opcode", get_opcode, METH_NOARGS, NULL }, + { "get_oparg", get_oparg, METH_NOARGS, NULL }, + { NULL, NULL }, +}; + static int executor_is_gc(PyObject *o) { @@ -407,7 +430,7 @@ PyTypeObject _PyUOpExecutor_Type = { .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_HAVE_GC, .tp_dealloc = (destructor)uop_dealloc, .tp_as_sequence = &uop_as_sequence, - .tp_methods = executor_methods, + .tp_methods = uop_executor_methods, .tp_traverse = executor_traverse, .tp_clear = (inquiry)executor_clear, .tp_is_gc = executor_is_gc, From 49baa656cb994122869bc807a88ea2f3f0d7751b Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 1 May 2024 08:05:53 -0700 Subject: [PATCH 137/217] GH-115802: Use the GHC calling convention in JIT code (GH-118287) --- Include/cpython/optimizer.h | 1 + Lib/test/test_perf_profiler.py | 2 +- Python/jit.c | 52 ++++++++++++++++++++++---------- Python/optimizer.c | 2 ++ Tools/jit/_targets.py | 54 ++++++++++++++++++++++++++++------ Tools/jit/_writer.py | 4 +++ Tools/jit/template.c | 4 +-- Tools/jit/trampoline.c | 25 ++++++++++++++++ 8 files changed, 117 insertions(+), 27 deletions(-) create mode 100644 Tools/jit/trampoline.c diff --git a/Include/cpython/optimizer.h b/Include/cpython/optimizer.h index a169280b26a6ad..60b35747deb4f3 100644 --- a/Include/cpython/optimizer.h +++ b/Include/cpython/optimizer.h @@ -102,6 +102,7 @@ typedef struct _PyExecutorObject { uint32_t code_size; size_t jit_size; void *jit_code; + void *jit_side_entry; _PyExitData exits[1]; } _PyExecutorObject; diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py index 040be63da11447..e7c03b99086013 100644 --- a/Lib/test/test_perf_profiler.py +++ b/Lib/test/test_perf_profiler.py @@ -216,7 +216,7 @@ def is_unwinding_reliable(): cflags = sysconfig.get_config_var("PY_CORE_CFLAGS") if not cflags: return False - return "no-omit-frame-pointer" in cflags + return "no-omit-frame-pointer" in cflags and "_Py_JIT" not in cflags def perf_command_works(): diff --git a/Python/jit.c b/Python/jit.c index 75ec4fb9756eb7..df14e48c564447 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -385,8 +385,8 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size { // Loop once to find the total compiled size: size_t instruction_starts[UOP_MAX_TRACE_LENGTH]; - size_t code_size = 0; - size_t data_size = 0; + size_t code_size = trampoline.code.body_size; + size_t data_size = trampoline.data.body_size; for (size_t i = 0; i < length; i++) { _PyUOpInstruction *instruction = (_PyUOpInstruction *)&trace[i]; const StencilGroup *group = &stencil_groups[instruction->opcode]; @@ -408,11 +408,29 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size // Loop again to emit the code: unsigned char *code = memory; unsigned char *data = memory + code_size; + { + // Compile the trampoline, which handles converting between the native + // calling convention and the calling convention used by jitted code + // (which may be different for efficiency reasons). On platforms where + // we don't change calling conventions, the trampoline is empty and + // nothing is emitted here: + const StencilGroup *group = &trampoline; + // Think of patches as a dictionary mapping HoleValue to uintptr_t: + uintptr_t patches[] = GET_PATCHES(); + patches[HoleValue_CODE] = (uintptr_t)code; + patches[HoleValue_CONTINUE] = (uintptr_t)code + group->code.body_size; + patches[HoleValue_DATA] = (uintptr_t)data; + patches[HoleValue_EXECUTOR] = (uintptr_t)executor; + patches[HoleValue_TOP] = (uintptr_t)memory + trampoline.code.body_size; + patches[HoleValue_ZERO] = 0; + emit(group, patches); + code += group->code.body_size; + data += group->data.body_size; + } assert(trace[0].opcode == _START_EXECUTOR || trace[0].opcode == _COLD_EXIT); for (size_t i = 0; i < length; i++) { _PyUOpInstruction *instruction = (_PyUOpInstruction *)&trace[i]; const StencilGroup *group = &stencil_groups[instruction->opcode]; - // Think of patches as a dictionary mapping HoleValue to uintptr_t: uintptr_t patches[] = GET_PATCHES(); patches[HoleValue_CODE] = (uintptr_t)code; patches[HoleValue_CONTINUE] = (uintptr_t)code + group->code.body_size; @@ -454,18 +472,20 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size code += group->code.body_size; data += group->data.body_size; } - // Protect against accidental buffer overrun into data: - const StencilGroup *group = &stencil_groups[_FATAL_ERROR]; - uintptr_t patches[] = GET_PATCHES(); - patches[HoleValue_CODE] = (uintptr_t)code; - patches[HoleValue_CONTINUE] = (uintptr_t)code; - patches[HoleValue_DATA] = (uintptr_t)data; - patches[HoleValue_EXECUTOR] = (uintptr_t)executor; - patches[HoleValue_TOP] = (uintptr_t)code; - patches[HoleValue_ZERO] = 0; - emit(group, patches); - code += group->code.body_size; - data += group->data.body_size; + { + // Protect against accidental buffer overrun into data: + const StencilGroup *group = &stencil_groups[_FATAL_ERROR]; + uintptr_t patches[] = GET_PATCHES(); + patches[HoleValue_CODE] = (uintptr_t)code; + patches[HoleValue_CONTINUE] = (uintptr_t)code; + patches[HoleValue_DATA] = (uintptr_t)data; + patches[HoleValue_EXECUTOR] = (uintptr_t)executor; + patches[HoleValue_TOP] = (uintptr_t)code; + patches[HoleValue_ZERO] = 0; + emit(group, patches); + code += group->code.body_size; + data += group->data.body_size; + } assert(code == memory + code_size); assert(data == memory + code_size + data_size); if (mark_executable(memory, total_size)) { @@ -473,6 +493,7 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size return -1; } executor->jit_code = memory; + executor->jit_side_entry = memory + trampoline.code.body_size; executor->jit_size = total_size; return 0; } @@ -484,6 +505,7 @@ _PyJIT_Free(_PyExecutorObject *executor) size_t size = executor->jit_size; if (memory) { executor->jit_code = NULL; + executor->jit_side_entry = NULL; executor->jit_size = 0; if (jit_free(memory, size)) { PyErr_WriteUnraisable(NULL); diff --git a/Python/optimizer.c b/Python/optimizer.c index 9ba8d84a47dcd9..2389338531b0f3 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1188,6 +1188,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil #endif #ifdef _Py_JIT executor->jit_code = NULL; + executor->jit_side_entry = NULL; executor->jit_size = 0; if (_PyJIT_Compile(executor, executor->trace, length)) { Py_DECREF(executor); @@ -1219,6 +1220,7 @@ init_cold_exit_executor(_PyExecutorObject *executor, int oparg) #endif #ifdef _Py_JIT executor->jit_code = NULL; + executor->jit_side_entry = NULL; executor->jit_size = 0; if (_PyJIT_Compile(executor, executor->trace, 1)) { return -1; diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 91734b36b4ab1b..274d17bcf38deb 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -38,6 +38,7 @@ class _Target(typing.Generic[_S, _R]): _: dataclasses.KW_ONLY alignment: int = 1 args: typing.Sequence[str] = () + ghccc: bool = False prefix: str = "" debug: bool = False force: bool = False @@ -85,7 +86,11 @@ async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup: sections: list[dict[typing.Literal["Section"], _S]] = json.loads(output) for wrapped_section in sections: self._handle_section(wrapped_section["Section"], group) - assert group.symbols["_JIT_ENTRY"] == (_stencils.HoleValue.CODE, 0) + # The trampoline's entry point is just named "_ENTRY", since on some + # platforms we later assume that any function starting with "_JIT_" uses + # the GHC calling convention: + entry_symbol = "_JIT_ENTRY" if "_JIT_ENTRY" in group.symbols else "_ENTRY" + assert group.symbols[entry_symbol] == (_stencils.HoleValue.CODE, 0) if group.data.body: line = f"0: {str(bytes(group.data.body)).removeprefix('b')}" group.data.disassembly.append(line) @@ -103,6 +108,9 @@ def _handle_relocation( async def _compile( self, opname: str, c: pathlib.Path, tempdir: pathlib.Path ) -> _stencils.StencilGroup: + # "Compile" the trampoline to an empty stencil group if it's not needed: + if opname == "trampoline" and not self.ghccc: + return _stencils.StencilGroup() o = tempdir / f"{opname}.o" args = [ f"--target={self.triple}", @@ -130,13 +138,38 @@ async def _compile( "-fno-plt", # Don't call stack-smashing canaries that we can't find or patch: "-fno-stack-protector", - "-o", - f"{o}", "-std=c11", - f"{c}", *self.args, ] - await _llvm.run("clang", args, echo=self.verbose) + if self.ghccc: + # This is a bit of an ugly workaround, but it makes the code much + # smaller and faster, so it's worth it. We want to use the GHC + # calling convention, but Clang doesn't support it. So, we *first* + # compile the code to LLVM IR, perform some text replacements on the + # IR to change the calling convention(!), and then compile *that*. + # Once we have access to Clang 19, we can get rid of this and use + # __attribute__((preserve_none)) directly in the C code instead: + ll = tempdir / f"{opname}.ll" + args_ll = args + [ + # -fomit-frame-pointer is necessary because the GHC calling + # convention uses RBP to pass arguments: + "-S", "-emit-llvm", "-fomit-frame-pointer", "-o", f"{ll}", f"{c}" + ] + await _llvm.run("clang", args_ll, echo=self.verbose) + ir = ll.read_text() + # This handles declarations, definitions, and calls to named symbols + # starting with "_JIT_": + ir = re.sub(r"(((noalias|nonnull|noundef) )*ptr @_JIT_\w+\()", r"ghccc \1", ir) + # This handles calls to anonymous callees, since anything with + # "musttail" needs to use the same calling convention: + ir = ir.replace("musttail call", "musttail call ghccc") + # Sometimes *both* replacements happen at the same site, so fix it: + ir = ir.replace("ghccc ghccc", "ghccc") + ll.write_text(ir) + args_o = args + ["-Wno-unused-command-line-argument", "-o", f"{o}", f"{ll}"] + else: + args_o = args + ["-o", f"{o}", f"{c}"] + await _llvm.run("clang", args_o, echo=self.verbose) return await self._parse(o) async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: @@ -146,6 +179,8 @@ async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: with tempfile.TemporaryDirectory() as tempdir: work = pathlib.Path(tempdir).resolve() async with asyncio.TaskGroup() as group: + coro = self._compile("trampoline", TOOLS_JIT / "trampoline.c", work) + tasks.append(group.create_task(coro, name="trampoline")) for opname in opnames: coro = self._compile(opname, TOOLS_JIT_TEMPLATE_C, work) tasks.append(group.create_task(coro, name=opname)) @@ -445,6 +480,7 @@ def _handle_relocation( def get_target(host: str) -> _COFF | _ELF | _MachO: """Build a _Target for the given host "triple" and options.""" + # ghccc currently crashes Clang when combined with musttail on aarch64. :( if re.fullmatch(r"aarch64-apple-darwin.*", host): return _MachO(host, alignment=8, prefix="_") if re.fullmatch(r"aarch64-pc-windows-msvc", host): @@ -455,13 +491,13 @@ def get_target(host: str) -> _COFF | _ELF | _MachO: return _ELF(host, alignment=8, args=args) if re.fullmatch(r"i686-pc-windows-msvc", host): args = ["-DPy_NO_ENABLE_SHARED"] - return _COFF(host, args=args, prefix="_") + return _COFF(host, args=args, ghccc=True, prefix="_") if re.fullmatch(r"x86_64-apple-darwin.*", host): - return _MachO(host, prefix="_") + return _MachO(host, ghccc=True, prefix="_") if re.fullmatch(r"x86_64-pc-windows-msvc", host): args = ["-fms-runtime-lib=dll"] - return _COFF(host, args=args) + return _COFF(host, args=args, ghccc=True) if re.fullmatch(r"x86_64-.*-linux-gnu", host): args = ["-fpic"] - return _ELF(host, args=args) + return _ELF(host, args=args, ghccc=True) raise ValueError(host) diff --git a/Tools/jit/_writer.py b/Tools/jit/_writer.py index cbc1ed2fa6543a..6b36d8a9c66a3f 100644 --- a/Tools/jit/_writer.py +++ b/Tools/jit/_writer.py @@ -53,9 +53,13 @@ def _dump_footer(opnames: typing.Iterable[str]) -> typing.Iterator[str]: yield "" yield "static const StencilGroup stencil_groups[512] = {" for opname in opnames: + if opname == "trampoline": + continue yield f" [{opname}] = INIT_STENCIL_GROUP({opname})," yield "};" yield "" + yield "static const StencilGroup trampoline = INIT_STENCIL_GROUP(trampoline);" + yield "" yield "#define GET_PATCHES() { \\" for value in _stencils.HoleValue: yield f" [HoleValue_{value.name}] = (uintptr_t)0xBADBADBADBADBADB, \\" diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 3e81fd15bb8093..0dd0744f7aec9c 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -48,7 +48,7 @@ do { \ OPT_STAT_INC(traces_executed); \ __attribute__((musttail)) \ - return ((jit_func)((EXECUTOR)->jit_code))(frame, stack_pointer, tstate); \ + return ((jit_func)((EXECUTOR)->jit_side_entry))(frame, stack_pointer, tstate); \ } while (0) #undef GOTO_TIER_ONE @@ -65,7 +65,7 @@ do { \ #define PATCH_VALUE(TYPE, NAME, ALIAS) \ PyAPI_DATA(void) ALIAS; \ - TYPE NAME = (TYPE)(uint64_t)&ALIAS; + TYPE NAME = (TYPE)(uintptr_t)&ALIAS; #define PATCH_JUMP(ALIAS) \ do { \ diff --git a/Tools/jit/trampoline.c b/Tools/jit/trampoline.c new file mode 100644 index 00000000000000..01b3d63a6790ba --- /dev/null +++ b/Tools/jit/trampoline.c @@ -0,0 +1,25 @@ +#include "Python.h" + +#include "pycore_ceval.h" +#include "pycore_frame.h" +#include "pycore_jit.h" + +// This is where the calling convention changes, on platforms that require it. +// The actual change is patched in while the JIT compiler is being built, in +// Tools/jit/_targets.py. On other platforms, this function compiles to nothing. +_Py_CODEUNIT * +_ENTRY(_PyInterpreterFrame *frame, PyObject **stack_pointer, PyThreadState *tstate) +{ + // This is subtle. The actual trace will return to us once it exits, so we + // need to make sure that we stay alive until then. If our trace side-exits + // into another trace, and this trace is then invalidated, the code for + // *this function* will be freed and we'll crash upon return: + PyAPI_DATA(void) _JIT_EXECUTOR; + PyObject *executor = (PyObject *)(uintptr_t)&_JIT_EXECUTOR; + Py_INCREF(executor); + // Note that this is *not* a tail call: + PyAPI_DATA(void) _JIT_CONTINUE; + _Py_CODEUNIT *target = ((jit_func)&_JIT_CONTINUE)(frame, stack_pointer, tstate); + Py_SETREF(tstate->previous_executor, executor); + return target; +} From 759e8e7ab83848c527a53d7b2051bc14ac7b7c76 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Wed, 1 May 2024 18:01:47 +0200 Subject: [PATCH 138/217] gh-99730: urllib.request: Keep HEAD method on redirect (GH-99731) --- Lib/test/test_urllib2.py | 9 +++++++++ Lib/urllib/request.py | 1 + .../2022-11-23-17-16-31.gh-issue-99730.bDQdaX.rst | 1 + 3 files changed, 11 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-11-23-17-16-31.gh-issue-99730.bDQdaX.rst diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 6febb491788b42..eed0599642edfb 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1402,6 +1402,15 @@ def http_open(self, req): request = handler.last_buf self.assertTrue(request.startswith(expected), repr(request)) + def test_redirect_head_request(self): + from_url = "http://example.com/a.html" + to_url = "http://example.com/b.html" + h = urllib.request.HTTPRedirectHandler() + req = Request(from_url, method="HEAD") + fp = MockFile() + new_req = h.redirect_request(req, fp, 302, "Found", {}, to_url) + self.assertEqual(new_req.get_method(), "HEAD") + def test_proxy(self): u = "proxy.example.com:3128" for d in dict(http=u), dict(HTTP=u): diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index d22af6618d80f1..ac6719ce854182 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -650,6 +650,7 @@ def redirect_request(self, req, fp, code, msg, headers, newurl): newheaders = {k: v for k, v in req.headers.items() if k.lower() not in CONTENT_HEADERS} return Request(newurl, + method="HEAD" if m == "HEAD" else "GET", headers=newheaders, origin_req_host=req.origin_req_host, unverifiable=True) diff --git a/Misc/NEWS.d/next/Library/2022-11-23-17-16-31.gh-issue-99730.bDQdaX.rst b/Misc/NEWS.d/next/Library/2022-11-23-17-16-31.gh-issue-99730.bDQdaX.rst new file mode 100644 index 00000000000000..b5955879c21ce2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-23-17-16-31.gh-issue-99730.bDQdaX.rst @@ -0,0 +1 @@ +HEAD requests are no longer upgraded to GET request during redirects in urllib. From 2d161cb899a0b32e80f0dc4f1c24b1df443d10a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 17:06:54 +0100 Subject: [PATCH 139/217] Bump mypy from 1.9.0 to 1.10.0 in /Tools (#118461) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Tools/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/requirements-dev.txt b/Tools/requirements-dev.txt index 61e75cf396ccb4..e6864b16c560ee 100644 --- a/Tools/requirements-dev.txt +++ b/Tools/requirements-dev.txt @@ -1,6 +1,6 @@ # Requirements file for external linters and checks we run on # Tools/clinic, Tools/cases_generator/, and Tools/peg_generator/ in CI -mypy==1.9.0 +mypy==1.10.0 # needed for peg_generator: types-psutil==5.9.5.20240316 From 6d12f4469c5f9e24809df28b19900722d52af11b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 17:07:29 +0100 Subject: [PATCH 140/217] Bump types-setuptools from 69.2.0.20240317 to 69.5.0.20240423 in /Tools (#118463) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Tools/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/requirements-dev.txt b/Tools/requirements-dev.txt index e6864b16c560ee..ddeff674e92457 100644 --- a/Tools/requirements-dev.txt +++ b/Tools/requirements-dev.txt @@ -4,4 +4,4 @@ mypy==1.10.0 # needed for peg_generator: types-psutil==5.9.5.20240316 -types-setuptools==69.2.0.20240317 +types-setuptools==69.5.0.20240423 From 75955110a643875b5d096b6eda06dcc6e542e171 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Wed, 1 May 2024 17:47:54 +0100 Subject: [PATCH 141/217] gh-116622: Android sysconfig updates (#118352) --- Android/android-env.sh | 6 ++++++ Lib/sysconfig/__init__.py | 20 +++++++++++++++---- Lib/test/test_sysconfig.py | 20 +++++++++++++++++++ ...-04-27-20-34-56.gh-issue-116622.YlQgXv.rst | 2 ++ configure | 7 ++++++- configure.ac | 3 +++ 6 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-27-20-34-56.gh-issue-116622.YlQgXv.rst diff --git a/Android/android-env.sh b/Android/android-env.sh index 3ce3e035cfb8fe..545d559d93ab36 100644 --- a/Android/android-env.sh +++ b/Android/android-env.sh @@ -61,6 +61,12 @@ done export CFLAGS="" export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment" +# Unlike Linux, Android does not implicitly use a dlopened library to resolve +# relocations in subsequently-loaded libraries, even if RTLD_GLOBAL is used +# (https://github.com/android/ndk/issues/1244). So any library that fails to +# build with this flag, would also fail to load at runtime. +LDFLAGS="$LDFLAGS -Wl,--no-undefined" + # Many packages get away with omitting -lm on Linux, but Android is stricter. LDFLAGS="$LDFLAGS -lm" diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 70bdecf2138fd9..98a14e5d3a3187 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -601,10 +601,22 @@ def get_platform(): machine = machine.replace('/', '-') if osname[:5] == "linux": - # At least on Linux/Intel, 'machine' is the processor -- - # i386, etc. - # XXX what about Alpha, SPARC, etc? - return f"{osname}-{machine}" + if sys.platform == "android": + osname = "android" + release = get_config_var("ANDROID_API_LEVEL") + + # Wheel tags use the ABI names from Android's own tools. + machine = { + "x86_64": "x86_64", + "i686": "x86", + "aarch64": "arm64_v8a", + "armv7l": "armeabi_v7a", + }[machine] + else: + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return f"{osname}-{machine}" elif osname[:5] == "sunos": if release[0] >= "5": # SunOS 5 == Solaris 2 osname = "solaris" diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 61c6a5a42502e7..9233304c6a5327 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -232,6 +232,11 @@ def test_get_config_vars(self): self.assertTrue(cvars) def test_get_platform(self): + # Check the actual platform returns something reasonable. + actual_platform = get_platform() + self.assertIsInstance(actual_platform, str) + self.assertTrue(actual_platform) + # windows XP, 32bits os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' @@ -347,6 +352,21 @@ def test_get_platform(self): self.assertEqual(get_platform(), 'linux-i686') + # Android + os.name = 'posix' + sys.platform = 'android' + get_config_vars()['ANDROID_API_LEVEL'] = 9 + for machine, abi in { + 'x86_64': 'x86_64', + 'i686': 'x86', + 'aarch64': 'arm64_v8a', + 'armv7l': 'armeabi_v7a', + }.items(): + with self.subTest(machine): + self._set_uname(('Linux', 'localhost', '3.18.91+', + '#1 Tue Jan 9 20:35:43 UTC 2018', machine)) + self.assertEqual(get_platform(), f'android-9-{abi}') + # XXX more platforms to tests here @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") diff --git a/Misc/NEWS.d/next/Library/2024-04-27-20-34-56.gh-issue-116622.YlQgXv.rst b/Misc/NEWS.d/next/Library/2024-04-27-20-34-56.gh-issue-116622.YlQgXv.rst new file mode 100644 index 00000000000000..c7c57b6dbc515e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-27-20-34-56.gh-issue-116622.YlQgXv.rst @@ -0,0 +1,2 @@ +On Android, :any:`sysconfig.get_platform` now returns the format specified +by :pep:`738`. diff --git a/configure b/configure index 01c00d2198548f..24e7396e389fd1 100755 --- a/configure +++ b/configure @@ -7018,8 +7018,13 @@ case $host/$ac_cv_cc_name in #( PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( + aarch64-*-linux-android/clang) : + PY_SUPPORT_TIER=3 ;; #( + x86_64-*-linux-android/clang) : + PY_SUPPORT_TIER=3 ;; #( *) : - PY_SUPPORT_TIER=0 + + PY_SUPPORT_TIER=0 ;; esac diff --git a/configure.ac b/configure.ac index ae65efeac55366..0f1177872d9137 100644 --- a/configure.ac +++ b/configure.ac @@ -1148,6 +1148,9 @@ AS_CASE([$host/$ac_cv_cc_name], [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 + [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 + [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 + [PY_SUPPORT_TIER=0] ) From a8bcf3ec3284f042dab9cad676951092fb1f9890 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 May 2024 13:04:02 -0400 Subject: [PATCH 142/217] Expand the 'Extending' docs with an example. (#113187) * Expand the 'Extending' docs to provide a minimal example. Closes python/importlib_metadata#427. Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/library/importlib.metadata.rst | 78 ++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index 5039bc6e85ee20..674ce5807fdf11 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -406,6 +406,84 @@ metadata in locations other than the file system, subclass a custom finder, return instances of this derived ``Distribution`` in the ``find_distributions()`` method. +Example +------- + +Consider for example a custom finder that loads Python +modules from a database:: + + class DatabaseImporter(importlib.abc.MetaPathFinder): + def __init__(self, db): + self.db = db + + def find_spec(self, fullname, target=None) -> ModuleSpec: + return self.db.spec_from_name(fullname) + + sys.meta_path.append(DatabaseImporter(connect_db(...))) + +That importer now presumably provides importable modules from a +database, but it provides no metadata or entry points. For this +custom importer to provide metadata, it would also need to implement +``DistributionFinder``:: + + from importlib.metadata import DistributionFinder + + class DatabaseImporter(DistributionFinder): + ... + + def find_distributions(self, context=DistributionFinder.Context()): + query = dict(name=context.name) if context.name else {} + for dist_record in self.db.query_distributions(query): + yield DatabaseDistribution(dist_record) + +In this way, ``query_distributions`` would return records for +each distribution served by the database matching the query. For +example, if ``requests-1.0`` is in the database, ``find_distributions`` +would yield a ``DatabaseDistribution`` for ``Context(name='requests')`` +or ``Context(name=None)``. + +For the sake of simplicity, this example ignores ``context.path``\. The +``path`` attribute defaults to ``sys.path`` and is the set of import paths to +be considered in the search. A ``DatabaseImporter`` could potentially function +without any concern for a search path. Assuming the importer does no +partitioning, the "path" would be irrelevant. In order to illustrate the +purpose of ``path``, the example would need to illustrate a more complex +``DatabaseImporter`` whose behavior varied depending on +``sys.path``/``PYTHONPATH``. In that case, the ``find_distributions`` should +honor the ``context.path`` and only yield ``Distribution``\ s pertinent to that +path. + +``DatabaseDistribution``, then, would look something like:: + + class DatabaseDistribution(importlib.metadata.Distributon): + def __init__(self, record): + self.record = record + + def read_text(self, filename): + """ + Read a file like "METADATA" for the current distribution. + """ + if filename == "METADATA": + return f"""Name: {self.record.name} + Version: {self.record.version} + """ + if filename == "entry_points.txt": + return "\n".join( + f"""[{ep.group}]\n{ep.name}={ep.value}""" + for ep in self.record.entry_points) + + def locate_file(self, path): + raise RuntimeError("This distribution has no file system") + +This basic implementation should provide metadata and entry points for +packages served by the ``DatabaseImporter``, assuming that the +``record`` supplies suitable ``.name``, ``.version``, and +``.entry_points`` attributes. + +The ``DatabaseDistribution`` may also provide other metadata files, like +``RECORD`` (required for ``Distribution.files``) or override the +implementation of ``Distribution.files``. See the source for more inspiration. + .. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points .. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api From b52c753e0fb8b87c7a0e3f1c72acd18327d8d16b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 1 May 2024 20:05:01 +0200 Subject: [PATCH 143/217] gh-110850: Add PyTime_TimeRaw() function (#118394) Add "Raw" variant of PyTime functions: * PyTime_MonotonicRaw() * PyTime_PerfCounterRaw() * PyTime_TimeRaw() Changes: * Add documentation and tests. Tests release the GIL while calling raw clock functions. * py_get_system_clock() and py_get_monotonic_clock() now check that the GIL is hold by the caller if raise_exc is non-zero. * Reimplement "Unchecked" functions with raw clock functions. Co-authored-by: Petr Viktorin --- Doc/c-api/time.rst | 29 ++++++ Doc/whatsnew/3.13.rst | 12 ++- Include/cpython/pytime.h | 4 + Lib/test/test_capi/test_time.py | 19 ++-- ...-04-29-17-19-07.gh-issue-110850.vcpLn1.rst | 7 ++ Modules/_testcapi/time.c | 60 ++++++++++++ Python/pytime.c | 93 ++++++++++++++----- 7 files changed, 189 insertions(+), 35 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-04-29-17-19-07.gh-issue-110850.vcpLn1.rst diff --git a/Doc/c-api/time.rst b/Doc/c-api/time.rst index 7791cdb1781055..5cfdef71b3e191 100644 --- a/Doc/c-api/time.rst +++ b/Doc/c-api/time.rst @@ -72,6 +72,35 @@ with the :term:`GIL` held. See :func:`time.time` for details important on this clock. +Raw Clock Functions +------------------- + +Similar to clock functions, but don't set an exception on error and don't +require the caller to hold the GIL. + +On success, the functions return ``0``. + +On failure, they set ``*result`` to ``0`` and return ``-1``, *without* setting +an exception. To get the cause of the error, acquire the GIL and call the +regular (non-``Raw``) function. Note that the regular function may succeed after +the ``Raw`` one failed. + +.. c:function:: int PyTime_MonotonicRaw(PyTime_t *result) + + Similar to :c:func:`PyTime_Monotonic`, + but don't set an exception on error and don't require holding the GIL. + +.. c:function:: int PyTime_PerfCounterRaw(PyTime_t *result) + + Similar to :c:func:`PyTime_PerfCounter`, + but don't set an exception on error and don't require holding the GIL. + +.. c:function:: int PyTime_TimeRaw(PyTime_t *result) + + Similar to :c:func:`PyTime_Time`, + but don't set an exception on error and don't require holding the GIL. + + Conversion functions -------------------- diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index ee50effd662f12..5a169556066421 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1901,9 +1901,15 @@ New Features * :c:type:`PyTime_t` type. * :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants. - * :c:func:`PyTime_AsSecondsDouble` - :c:func:`PyTime_Monotonic`, :c:func:`PyTime_PerfCounter`, and - :c:func:`PyTime_Time` functions. + * Add functions: + + * :c:func:`PyTime_AsSecondsDouble`. + * :c:func:`PyTime_Monotonic`. + * :c:func:`PyTime_MonotonicRaw`. + * :c:func:`PyTime_PerfCounter`. + * :c:func:`PyTime_PerfCounterRaw`. + * :c:func:`PyTime_Time`. + * :c:func:`PyTime_TimeRaw`. (Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.) diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h index d8244700d614ce..5c68110aeedb86 100644 --- a/Include/cpython/pytime.h +++ b/Include/cpython/pytime.h @@ -16,6 +16,10 @@ PyAPI_FUNC(int) PyTime_Monotonic(PyTime_t *result); PyAPI_FUNC(int) PyTime_PerfCounter(PyTime_t *result); PyAPI_FUNC(int) PyTime_Time(PyTime_t *result); +PyAPI_FUNC(int) PyTime_MonotonicRaw(PyTime_t *result); +PyAPI_FUNC(int) PyTime_PerfCounterRaw(PyTime_t *result); +PyAPI_FUNC(int) PyTime_TimeRaw(PyTime_t *result); + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_capi/test_time.py b/Lib/test/test_capi/test_time.py index 10b7fbf2c372a3..17ebd7c1962d48 100644 --- a/Lib/test/test_capi/test_time.py +++ b/Lib/test/test_capi/test_time.py @@ -18,11 +18,6 @@ def test_min_max(self): self.assertEqual(PyTime_MIN, -2**63) self.assertEqual(PyTime_MAX, 2**63 - 1) - def check_clock(self, c_func, py_func): - t1 = c_func() - t2 = py_func() - self.assertAlmostEqual(t1, t2, delta=CLOCK_RES) - def test_assecondsdouble(self): # Test PyTime_AsSecondsDouble() def ns_to_sec(ns): @@ -58,14 +53,22 @@ def ns_to_sec(ns): self.assertEqual(_testcapi.PyTime_AsSecondsDouble(ns), ns_to_sec(ns)) + def check_clock(self, c_func, py_func): + t1 = c_func() + t2 = py_func() + self.assertAlmostEqual(t1, t2, delta=CLOCK_RES) + def test_monotonic(self): - # Test PyTime_Monotonic() + # Test PyTime_Monotonic() and PyTime_MonotonicRaw() self.check_clock(_testcapi.PyTime_Monotonic, time.monotonic) + self.check_clock(_testcapi.PyTime_MonotonicRaw, time.monotonic) def test_perf_counter(self): - # Test PyTime_PerfCounter() + # Test PyTime_PerfCounter() and PyTime_PerfCounterRaw() self.check_clock(_testcapi.PyTime_PerfCounter, time.perf_counter) + self.check_clock(_testcapi.PyTime_PerfCounterRaw, time.perf_counter) def test_time(self): - # Test PyTime_time() + # Test PyTime_Time() and PyTime_TimeRaw() self.check_clock(_testcapi.PyTime_Time, time.time) + self.check_clock(_testcapi.PyTime_TimeRaw, time.time) diff --git a/Misc/NEWS.d/next/C API/2024-04-29-17-19-07.gh-issue-110850.vcpLn1.rst b/Misc/NEWS.d/next/C API/2024-04-29-17-19-07.gh-issue-110850.vcpLn1.rst new file mode 100644 index 00000000000000..786da0147eae46 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-04-29-17-19-07.gh-issue-110850.vcpLn1.rst @@ -0,0 +1,7 @@ +Add "Raw" variant of PyTime functions + +* :c:func:`PyTime_MonotonicRaw` +* :c:func:`PyTime_PerfCounterRaw` +* :c:func:`PyTime_TimeRaw` + +Patch by Victor Stinner. diff --git a/Modules/_testcapi/time.c b/Modules/_testcapi/time.c index 68f082bf3f3d88..464cf5c3125012 100644 --- a/Modules/_testcapi/time.c +++ b/Modules/_testcapi/time.c @@ -51,6 +51,25 @@ test_pytime_monotonic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) PyTime_t t; int res = PyTime_Monotonic(&t); if (res < 0) { + assert(t == 0); + return NULL; + } + assert(res == 0); + return pytime_as_float(t); +} + + +static PyObject* +test_pytime_monotonic_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + PyTime_t t; + int res; + Py_BEGIN_ALLOW_THREADS + res = PyTime_MonotonicRaw(&t); + Py_END_ALLOW_THREADS + if (res < 0) { + assert(t == 0); + PyErr_SetString(PyExc_RuntimeError, "PyTime_MonotonicRaw() failed"); return NULL; } assert(res == 0); @@ -64,6 +83,25 @@ test_pytime_perf_counter(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) PyTime_t t; int res = PyTime_PerfCounter(&t); if (res < 0) { + assert(t == 0); + return NULL; + } + assert(res == 0); + return pytime_as_float(t); +} + + +static PyObject* +test_pytime_perf_counter_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + PyTime_t t; + int res; + Py_BEGIN_ALLOW_THREADS + res = PyTime_PerfCounterRaw(&t); + Py_END_ALLOW_THREADS + if (res < 0) { + assert(t == 0); + PyErr_SetString(PyExc_RuntimeError, "PyTime_PerfCounterRaw() failed"); return NULL; } assert(res == 0); @@ -77,6 +115,25 @@ test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) PyTime_t t; int res = PyTime_Time(&t); if (res < 0) { + assert(t == 0); + return NULL; + } + assert(res == 0); + return pytime_as_float(t); +} + + +static PyObject* +test_pytime_time_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + PyTime_t t; + int res; + Py_BEGIN_ALLOW_THREADS + res = PyTime_TimeRaw(&t); + Py_END_ALLOW_THREADS + if (res < 0) { + assert(t == 0); + PyErr_SetString(PyExc_RuntimeError, "PyTime_TimeRaw() failed"); return NULL; } assert(res == 0); @@ -87,8 +144,11 @@ test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) static PyMethodDef test_methods[] = { {"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS}, {"PyTime_Monotonic", test_pytime_monotonic, METH_NOARGS}, + {"PyTime_MonotonicRaw", test_pytime_monotonic_raw, METH_NOARGS}, {"PyTime_PerfCounter", test_pytime_perf_counter, METH_NOARGS}, + {"PyTime_PerfCounterRaw", test_pytime_perf_counter_raw, METH_NOARGS}, {"PyTime_Time", test_pytime_time, METH_NOARGS}, + {"PyTime_TimeRaw", test_pytime_time_raw, METH_NOARGS}, {NULL}, }; diff --git a/Python/pytime.c b/Python/pytime.c index d5b38047b6db31..12b36bbc881f9a 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -898,6 +898,10 @@ static int py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) { assert(info == NULL || raise_exc); + if (raise_exc) { + // raise_exc requires to hold the GIL + assert(PyGILState_Check()); + } #ifdef MS_WINDOWS FILETIME system_time; @@ -1004,29 +1008,44 @@ py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) } -PyTime_t -_PyTime_TimeUnchecked(void) +int +PyTime_Time(PyTime_t *result) { - PyTime_t t; - if (py_get_system_clock(&t, NULL, 0) < 0) { - // If clock_gettime(CLOCK_REALTIME) or gettimeofday() fails: - // silently ignore the failure and return 0. - t = 0; + if (py_get_system_clock(result, NULL, 1) < 0) { + *result = 0; + return -1; } - return t; + return 0; } int -PyTime_Time(PyTime_t *result) +PyTime_TimeRaw(PyTime_t *result) { - if (py_get_system_clock(result, NULL, 1) < 0) { + if (py_get_system_clock(result, NULL, 0) < 0) { *result = 0; return -1; } return 0; } + +PyTime_t +_PyTime_TimeUnchecked(void) +{ + PyTime_t t; +#ifdef Py_DEBUG + int result = PyTime_TimeRaw(&t); + if (result != 0) { + Py_FatalError("unable to read the system clock"); + } +#else + (void)PyTime_TimeRaw(&t); +#endif + return t; +} + + int _PyTime_TimeWithInfo(PyTime_t *t, _Py_clock_info_t *info) { @@ -1140,6 +1159,10 @@ static int py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) { assert(info == NULL || raise_exc); + if (raise_exc) { + // raise_exc requires to hold the GIL + assert(PyGILState_Check()); + } #if defined(MS_WINDOWS) if (py_get_win_perf_counter(tp, info, raise_exc) < 0) { @@ -1225,22 +1248,21 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) } -PyTime_t -_PyTime_MonotonicUnchecked(void) +int +PyTime_Monotonic(PyTime_t *result) { - PyTime_t t; - if (py_get_monotonic_clock(&t, NULL, 0) < 0) { - // Ignore silently the error and return 0. - t = 0; + if (py_get_monotonic_clock(result, NULL, 1) < 0) { + *result = 0; + return -1; } - return t; + return 0; } int -PyTime_Monotonic(PyTime_t *result) +PyTime_MonotonicRaw(PyTime_t *result) { - if (py_get_monotonic_clock(result, NULL, 1) < 0) { + if (py_get_monotonic_clock(result, NULL, 0) < 0) { *result = 0; return -1; } @@ -1248,6 +1270,22 @@ PyTime_Monotonic(PyTime_t *result) } +PyTime_t +_PyTime_MonotonicUnchecked(void) +{ + PyTime_t t; +#ifdef Py_DEBUG + int result = PyTime_MonotonicRaw(&t); + if (result != 0) { + Py_FatalError("unable to read the monotonic clock"); + } +#else + (void)PyTime_MonotonicRaw(&t); +#endif + return t; +} + + int _PyTime_MonotonicWithInfo(PyTime_t *tp, _Py_clock_info_t *info) { @@ -1262,17 +1300,24 @@ _PyTime_PerfCounterWithInfo(PyTime_t *t, _Py_clock_info_t *info) } -PyTime_t -_PyTime_PerfCounterUnchecked(void) +int +PyTime_PerfCounter(PyTime_t *result) { - return _PyTime_MonotonicUnchecked(); + return PyTime_Monotonic(result); } int -PyTime_PerfCounter(PyTime_t *result) +PyTime_PerfCounterRaw(PyTime_t *result) { - return PyTime_Monotonic(result); + return PyTime_MonotonicRaw(result); +} + + +PyTime_t +_PyTime_PerfCounterUnchecked(void) +{ + return _PyTime_MonotonicUnchecked(); } From 164e2c31c073b5fd8d25b3432a5e5c721f747393 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 1 May 2024 21:25:11 +0300 Subject: [PATCH 144/217] gh-117225: Document colour use in `doctest` (#118268) --- Doc/using/cmdline.rst | 12 ++++++++++-- Doc/whatsnew/3.13.rst | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 295e3fb09830ce..1f5d02d1ea54a2 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -632,11 +632,11 @@ behavior can be controlled by setting different environment variables. Setting the environment variable ``TERM`` to ``dumb`` will disable color. -If the environment variable ``FORCE_COLOR`` is set, then color will be +If the |FORCE_COLOR|_ environment variable is set, then color will be enabled regardless of the value of TERM. This is useful on CI systems which aren’t terminals but can still display ANSI escape sequences. -If the environment variable ``NO_COLOR`` is set, Python will disable all color +If the |NO_COLOR|_ environment variable is set, Python will disable all color in the output. This takes precedence over ``FORCE_COLOR``. All these environment variables are used also by other tools to control color @@ -645,6 +645,14 @@ output. To control the color output only in the Python interpreter, the precedence over ``NO_COLOR``, which in turn takes precedence over ``FORCE_COLOR``. +.. Apparently this how you hack together a formatted link: + +.. |FORCE_COLOR| replace:: ``FORCE_COLOR`` +.. _FORCE_COLOR: https://force-color.org/ + +.. |NO_COLOR| replace:: ``NO_COLOR`` +.. _NO_COLOR: https://no-color.org/ + Options you shouldn't use ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 5a169556066421..d67df8109a8ced 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -100,10 +100,18 @@ Improved Error Messages * The interpreter now colorizes error messages when displaying tracebacks by default. This feature can be controlled via the new :envvar:`PYTHON_COLORS` environment - variable as well as the canonical ``NO_COLOR`` and ``FORCE_COLOR`` environment + variable as well as the canonical |NO_COLOR|_ and |FORCE_COLOR|_ environment variables. See also :ref:`using-on-controlling-color`. (Contributed by Pablo Galindo Salgado in :gh:`112730`.) +.. Apparently this how you hack together a formatted link: + +.. |FORCE_COLOR| replace:: ``FORCE_COLOR`` +.. _FORCE_COLOR: https://force-color.org/ + +.. |NO_COLOR| replace:: ``NO_COLOR`` +.. _NO_COLOR: https://no-color.org/ + * A common mistake is to write a script with the same name as a standard library module. When this results in errors, we now display a more helpful error message: @@ -439,6 +447,12 @@ doctest :attr:`doctest.TestResults.skipped` attributes. (Contributed by Victor Stinner in :gh:`108794`.) +* Color is added to the output by default. + This can be controlled via the new :envvar:`PYTHON_COLORS` environment + variable as well as the canonical |NO_COLOR|_ and |FORCE_COLOR|_ environment + variables. See also :ref:`using-on-controlling-color`. + (Contributed by Hugo van Kemenade in :gh:`117225`.) + email ----- From 3b3f8dea575d81a9e68f04b434942d06bd03fad5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 1 May 2024 21:27:06 +0300 Subject: [PATCH 145/217] gh-117225: Move colorize functionality to own internal module (#118283) --- Lib/_colorize.py | 64 ++++++++++++++++++ Lib/doctest.py | 38 +++++------ Lib/test/support/__init__.py | 9 +-- Lib/test/test__colorize.py | 59 ++++++++++++++++ Lib/test/test_doctest/test_doctest.py | 56 ++++++++-------- Lib/test/test_traceback.py | 75 +++++---------------- Lib/traceback.py | 96 ++++++++------------------- Python/stdlib_module_names.h | 1 + 8 files changed, 218 insertions(+), 180 deletions(-) create mode 100644 Lib/_colorize.py create mode 100644 Lib/test/test__colorize.py diff --git a/Lib/_colorize.py b/Lib/_colorize.py new file mode 100644 index 00000000000000..845fb57a90abb8 --- /dev/null +++ b/Lib/_colorize.py @@ -0,0 +1,64 @@ +import io +import os +import sys + +COLORIZE = True + + +class ANSIColors: + BOLD_GREEN = "\x1b[1;32m" + BOLD_MAGENTA = "\x1b[1;35m" + BOLD_RED = "\x1b[1;31m" + GREEN = "\x1b[32m" + GREY = "\x1b[90m" + MAGENTA = "\x1b[35m" + RED = "\x1b[31m" + RESET = "\x1b[0m" + YELLOW = "\x1b[33m" + + +NoColors = ANSIColors() + +for attr in dir(NoColors): + if not attr.startswith("__"): + setattr(NoColors, attr, "") + + +def get_colors(colorize: bool = False) -> ANSIColors: + if colorize or can_colorize(): + return ANSIColors() + else: + return NoColors + + +def can_colorize() -> bool: + if sys.platform == "win32": + try: + import nt + + if not nt._supports_virtual_terminal(): + return False + except (ImportError, AttributeError): + return False + if not sys.flags.ignore_environment: + if os.environ.get("PYTHON_COLORS") == "0": + return False + if os.environ.get("PYTHON_COLORS") == "1": + return True + if "NO_COLOR" in os.environ: + return False + if not COLORIZE: + return False + if not sys.flags.ignore_environment: + if "FORCE_COLOR" in os.environ: + return True + if os.environ.get("TERM") == "dumb": + return False + + if not hasattr(sys.stderr, "fileno"): + return False + + try: + return os.isatty(sys.stderr.fileno()) + except io.UnsupportedOperation: + return sys.stderr.isatty() diff --git a/Lib/doctest.py b/Lib/doctest.py index d8c632e47e7b7a..c531e3ca6a3d5e 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -104,7 +104,8 @@ def _test(): import unittest from io import StringIO, IncrementalNewlineDecoder from collections import namedtuple -from traceback import _ANSIColors, _can_colorize +import _colorize # Used in doctests +from _colorize import ANSIColors, can_colorize class TestResults(namedtuple('TestResults', 'failed attempted')): @@ -1180,8 +1181,8 @@ class DocTestRunner: The `run` method is used to process a single DocTest case. It returns a TestResults instance. - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> tests = DocTestFinder().find(_TestClass) >>> runner = DocTestRunner(verbose=False) @@ -1234,7 +1235,7 @@ class DocTestRunner: overriding the methods `report_start`, `report_success`, `report_unexpected_exception`, and `report_failure`. - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ # This divider string is used to separate failure messages, and to # separate sections of the summary. @@ -1314,7 +1315,7 @@ def report_unexpected_exception(self, out, test, example, exc_info): def _failure_header(self, test, example): red, reset = ( - (_ANSIColors.RED, _ANSIColors.RESET) if _can_colorize() else ("", "") + (ANSIColors.RED, ANSIColors.RESET) if can_colorize() else ("", "") ) out = [f"{red}{self.DIVIDER}{reset}"] if test.filename: @@ -1556,8 +1557,8 @@ def out(s): # Make sure sys.displayhook just prints the value to stdout save_displayhook = sys.displayhook sys.displayhook = sys.__displayhook__ - saved_can_colorize = traceback._can_colorize - traceback._can_colorize = lambda: False + saved_can_colorize = _colorize.can_colorize + _colorize.can_colorize = lambda: False color_variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None} for key in color_variables: color_variables[key] = os.environ.pop(key, None) @@ -1569,7 +1570,7 @@ def out(s): sys.settrace(save_trace) linecache.getlines = self.save_linecache_getlines sys.displayhook = save_displayhook - traceback._can_colorize = saved_can_colorize + _colorize.can_colorize = saved_can_colorize for key, value in color_variables.items(): if value is not None: os.environ[key] = value @@ -1609,20 +1610,13 @@ def summarize(self, verbose=None): else: failed.append((name, (failures, tries, skips))) - if _can_colorize(): - bold_green = _ANSIColors.BOLD_GREEN - bold_red = _ANSIColors.BOLD_RED - green = _ANSIColors.GREEN - red = _ANSIColors.RED - reset = _ANSIColors.RESET - yellow = _ANSIColors.YELLOW - else: - bold_green = "" - bold_red = "" - green = "" - red = "" - reset = "" - yellow = "" + ansi = _colorize.get_colors() + bold_green = ansi.BOLD_GREEN + bold_red = ansi.BOLD_RED + green = ansi.GREEN + red = ansi.RED + reset = ansi.RESET + yellow = ansi.YELLOW if verbose: if notests: diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 52573e665a1273..999fffb03ed59a 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2579,20 +2579,21 @@ def copy_python_src_ignore(path, names): } return ignored + def force_not_colorized(func): """Force the terminal not to be colorized.""" @functools.wraps(func) def wrapper(*args, **kwargs): - import traceback - original_fn = traceback._can_colorize + import _colorize + original_fn = _colorize.can_colorize variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None} try: for key in variables: variables[key] = os.environ.pop(key, None) - traceback._can_colorize = lambda: False + _colorize.can_colorize = lambda: False return func(*args, **kwargs) finally: - traceback._can_colorize = original_fn + _colorize.can_colorize = original_fn for key, value in variables.items(): if value is not None: os.environ[key] = value diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py new file mode 100644 index 00000000000000..d55b97ade68cef --- /dev/null +++ b/Lib/test/test__colorize.py @@ -0,0 +1,59 @@ +import contextlib +import sys +import unittest +import unittest.mock +import _colorize +from test.support import force_not_colorized + +ORIGINAL_CAN_COLORIZE = _colorize.can_colorize + + +def setUpModule(): + _colorize.can_colorize = lambda: False + + +def tearDownModule(): + _colorize.can_colorize = ORIGINAL_CAN_COLORIZE + + +class TestColorizeFunction(unittest.TestCase): + @force_not_colorized + def test_colorized_detection_checks_for_environment_variables(self): + if sys.platform == "win32": + virtual_patching = unittest.mock.patch("nt._supports_virtual_terminal", + return_value=True) + else: + virtual_patching = contextlib.nullcontext() + with virtual_patching: + + flags = unittest.mock.MagicMock(ignore_environment=False) + with (unittest.mock.patch("os.isatty") as isatty_mock, + unittest.mock.patch("sys.flags", flags), + unittest.mock.patch("_colorize.can_colorize", ORIGINAL_CAN_COLORIZE)): + isatty_mock.return_value = True + with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", + {'NO_COLOR': '1', "PYTHON_COLORS": '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", + {'FORCE_COLOR': '1', 'NO_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", + {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): + self.assertEqual(_colorize.can_colorize(), False) + isatty_mock.return_value = False + with unittest.mock.patch("os.environ", {}): + self.assertEqual(_colorize.can_colorize(), False) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index 0f1e584e22a888..3a173e823dd9a3 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -16,7 +16,7 @@ import tempfile import types import contextlib -import traceback +import _colorize def doctest_skip_if(condition): @@ -893,8 +893,8 @@ def basics(): r""" DocTestRunner is used to run DocTest test cases, and to accumulate statistics. Here's a simple DocTest case we can use: - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> def f(x): ... ''' @@ -951,7 +951,7 @@ def basics(): r""" ok TestResults(failed=1, attempted=3) - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ def verbose_flag(): r""" The `verbose` flag makes the test runner generate more detailed @@ -1027,8 +1027,8 @@ def exceptions(): r""" lines between the first line and the type/value may be omitted or replaced with any other string: - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> def f(x): ... ''' @@ -1261,7 +1261,7 @@ def exceptions(): r""" ZeroDivisionError: integer division or modulo by zero TestResults(failed=1, attempted=1) - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ def displayhook(): r""" Test that changing sys.displayhook doesn't matter for doctest. @@ -1303,8 +1303,8 @@ def optionflags(): r""" The DONT_ACCEPT_TRUE_FOR_1 flag disables matches between True/False and 1/0: - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> def f(x): ... '>>> True\n1\n' @@ -1725,7 +1725,7 @@ def optionflags(): r""" Clean up. >>> del doctest.OPTIONFLAGS_BY_NAME[unlikely] - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ @@ -1736,8 +1736,8 @@ def option_directives(): r""" single example. To turn an option on for an example, follow that example with a comment of the form ``# doctest: +OPTION``: - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> def f(x): r''' ... >>> print(list(range(10))) # should fail: no ellipsis @@ -1947,7 +1947,7 @@ def option_directives(): r""" Traceback (most recent call last): ValueError: line 0 of the doctest for s has an option directive on a line with no example: '# doctest: +ELLIPSIS' - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ def test_testsource(): r""" @@ -2031,8 +2031,8 @@ def test_pdb_set_trace(): with a version that restores stdout. This is necessary for you to see debugger output. - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> doc = ''' ... >>> x = 42 @@ -2157,7 +2157,7 @@ def test_pdb_set_trace(): 9 TestResults(failed=1, attempted=3) - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ def test_pdb_set_trace_nested(): @@ -2694,8 +2694,8 @@ def test_testfile(): r""" We don't want color or `-v` in sys.argv for these tests. - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> save_argv = sys.argv >>> if '-v' in sys.argv: @@ -2863,7 +2863,7 @@ def test_testfile(): r""" TestResults(failed=0, attempted=2) >>> doctest.master = None # Reset master. >>> sys.argv = save_argv - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ class TestImporter(importlib.abc.MetaPathFinder, importlib.abc.ResourceLoader): @@ -3001,8 +3001,8 @@ def test_testmod(): r""" def test_unicode(): """ Check doctest with a non-ascii filename: - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> doc = ''' ... >>> raise Exception('clé') @@ -3030,7 +3030,7 @@ def test_unicode(): """ Exception: clé TestResults(failed=1, attempted=1) - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ @@ -3325,8 +3325,8 @@ def test_run_doctestsuite_multiple_times(): def test_exception_with_note(note): """ - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> test_exception_with_note('Note') Traceback (most recent call last): @@ -3378,7 +3378,7 @@ def test_exception_with_note(note): note TestResults(failed=1, attempted=...) - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ exc = ValueError('Text') exc.add_note(note) @@ -3459,8 +3459,8 @@ def test_syntax_error_subclass_from_stdlib(): def test_syntax_error_with_incorrect_expected_note(): """ - >>> save_colorize = traceback._COLORIZE - >>> traceback._COLORIZE = False + >>> save_colorize = _colorize.COLORIZE + >>> _colorize.COLORIZE = False >>> def f(x): ... r''' @@ -3491,7 +3491,7 @@ def test_syntax_error_with_incorrect_expected_note(): note2 TestResults(failed=1, attempted=...) - >>> traceback._COLORIZE = save_colorize + >>> _colorize.COLORIZE = save_colorize """ diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 41f82512480d4f..8969e0174c98c1 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -26,9 +26,9 @@ import json import textwrap import traceback -import contextlib from functools import partial from pathlib import Path +import _colorize MODULE_PREFIX = f'{__name__}.' if __name__ == '__main__' else '' @@ -40,25 +40,18 @@ LEVENSHTEIN_DATA_FILE = Path(__file__).parent / 'levenshtein_examples.json' -ORIGINAL_CAN_COLORIZE = traceback._can_colorize - -def setUpModule(): - traceback._can_colorize = lambda: False - -def tearDownModule(): - traceback._can_colorize = ORIGINAL_CAN_COLORIZE class TracebackCases(unittest.TestCase): # For now, a very minimal set of tests. I want to be sure that # formatting of SyntaxErrors works based on changes for 2.1. def setUp(self): super().setUp() - self.colorize = traceback._COLORIZE - traceback._COLORIZE = False + self.colorize = _colorize.COLORIZE + _colorize.COLORIZE = False def tearDown(self): super().tearDown() - traceback._COLORIZE = self.colorize + _colorize.COLORIZE = self.colorize def get_exception_format(self, func, exc): try: @@ -4478,9 +4471,9 @@ def bar(): e, capture_locals=True ) lines = "".join(exc.format(colorize=True)) - red = traceback._ANSIColors.RED - boldr = traceback._ANSIColors.BOLD_RED - reset = traceback._ANSIColors.RESET + red = _colorize.ANSIColors.RED + boldr = _colorize.ANSIColors.BOLD_RED + reset = _colorize.ANSIColors.RESET self.assertIn("y = " + red + "x['a']['b']" + reset + boldr + "['c']" + reset, lines) self.assertIn("return " + red + "(lambda *args: foo(*args))" + reset + boldr + "(1,2,3,4)" + reset, lines) self.assertIn("return (lambda *args: " + red + "foo" + reset + boldr + "(*args)" + reset + ")(1,2,3,4)", lines) @@ -4496,11 +4489,11 @@ def test_colorized_syntax_error(self): e, capture_locals=True ) actual = "".join(exc.format(colorize=True)) - red = traceback._ANSIColors.RED - magenta = traceback._ANSIColors.MAGENTA - boldm = traceback._ANSIColors.BOLD_MAGENTA - boldr = traceback._ANSIColors.BOLD_RED - reset = traceback._ANSIColors.RESET + red = _colorize.ANSIColors.RED + magenta = _colorize.ANSIColors.MAGENTA + boldm = _colorize.ANSIColors.BOLD_MAGENTA + boldr = _colorize.ANSIColors.BOLD_RED + reset = _colorize.ANSIColors.RESET expected = "".join([ f' File {magenta}""{reset}, line {magenta}1{reset}\n', f' a {boldr}${reset} b\n', @@ -4519,15 +4512,15 @@ def foo(): self.fail("No exception thrown.") except Exception as e: with captured_output("stderr") as tbstderr: - with unittest.mock.patch('traceback._can_colorize', return_value=True): + with unittest.mock.patch('_colorize.can_colorize', return_value=True): exception_print(e) actual = tbstderr.getvalue().splitlines() - red = traceback._ANSIColors.RED - boldr = traceback._ANSIColors.BOLD_RED - magenta = traceback._ANSIColors.MAGENTA - boldm = traceback._ANSIColors.BOLD_MAGENTA - reset = traceback._ANSIColors.RESET + red = _colorize.ANSIColors.RED + boldr = _colorize.ANSIColors.BOLD_RED + magenta = _colorize.ANSIColors.MAGENTA + boldm = _colorize.ANSIColors.BOLD_MAGENTA + reset = _colorize.ANSIColors.RESET lno_foo = foo.__code__.co_firstlineno expected = ['Traceback (most recent call last):', f' File {magenta}"{__file__}"{reset}, ' @@ -4541,38 +4534,6 @@ def foo(): f'{boldm}ZeroDivisionError{reset}: {magenta}division by zero{reset}'] self.assertEqual(actual, expected) - @force_not_colorized - def test_colorized_detection_checks_for_environment_variables(self): - if sys.platform == "win32": - virtual_patching = unittest.mock.patch("nt._supports_virtual_terminal", return_value=True) - else: - virtual_patching = contextlib.nullcontext() - with virtual_patching: - - flags = unittest.mock.MagicMock(ignore_environment=False) - with (unittest.mock.patch("os.isatty") as isatty_mock, - unittest.mock.patch("sys.flags", flags), - unittest.mock.patch("traceback._can_colorize", ORIGINAL_CAN_COLORIZE)): - isatty_mock.return_value = True - with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): - self.assertEqual(traceback._can_colorize(), False) - with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}): - self.assertEqual(traceback._can_colorize(), True) - with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}): - self.assertEqual(traceback._can_colorize(), False) - with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}): - self.assertEqual(traceback._can_colorize(), False) - with unittest.mock.patch("os.environ", {'NO_COLOR': '1', "PYTHON_COLORS": '1'}): - self.assertEqual(traceback._can_colorize(), True) - with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}): - self.assertEqual(traceback._can_colorize(), True) - with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', 'NO_COLOR': '1'}): - self.assertEqual(traceback._can_colorize(), False) - with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): - self.assertEqual(traceback._can_colorize(), False) - isatty_mock.return_value = False - with unittest.mock.patch("os.environ", {}): - self.assertEqual(traceback._can_colorize(), False) if __name__ == "__main__": unittest.main() diff --git a/Lib/traceback.py b/Lib/traceback.py index 6ce745f32d1324..8403173ade7b6c 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1,7 +1,5 @@ """Extract, format and print information about Python stack traces.""" -import os -import io import collections.abc import itertools import linecache @@ -9,6 +7,8 @@ import textwrap import warnings from contextlib import suppress +import _colorize +from _colorize import ANSIColors __all__ = ['extract_stack', 'extract_tb', 'format_exception', 'format_exception_only', 'format_list', 'format_stack', @@ -21,7 +21,6 @@ # Formatting and printing lists of traceback lines. # -_COLORIZE = True def print_list(extracted_list, file=None): """Print the list of tuples as returned by extract_tb() or @@ -133,41 +132,10 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ BUILTIN_EXCEPTION_LIMIT = object() -def _can_colorize(): - if sys.platform == "win32": - try: - import nt - if not nt._supports_virtual_terminal(): - return False - except (ImportError, AttributeError): - return False - if not sys.flags.ignore_environment: - if os.environ.get("PYTHON_COLORS") == "0": - return False - if os.environ.get("PYTHON_COLORS") == "1": - return True - if "NO_COLOR" in os.environ: - return False - if not _COLORIZE: - return False - if not sys.flags.ignore_environment: - if "FORCE_COLOR" in os.environ: - return True - if os.environ.get("TERM") == "dumb": - return False - - if not hasattr(sys.stderr, "fileno"): - return False - - try: - return os.isatty(sys.stderr.fileno()) - except io.UnsupportedOperation: - return sys.stderr.isatty() - def _print_exception_bltin(exc, /): file = sys.stderr if sys.stderr is not None else sys.__stderr__ - colorize = _can_colorize() + colorize = _colorize.can_colorize() return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize) @@ -214,9 +182,9 @@ def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize= end_char = "\n" if insert_final_newline else "" if colorize: if value is None or not valuestr: - line = f"{_ANSIColors.BOLD_MAGENTA}{etype}{_ANSIColors.RESET}{end_char}" + line = f"{ANSIColors.BOLD_MAGENTA}{etype}{ANSIColors.RESET}{end_char}" else: - line = f"{_ANSIColors.BOLD_MAGENTA}{etype}{_ANSIColors.RESET}: {_ANSIColors.MAGENTA}{valuestr}{_ANSIColors.RESET}{end_char}" + line = f"{ANSIColors.BOLD_MAGENTA}{etype}{ANSIColors.RESET}: {ANSIColors.MAGENTA}{valuestr}{ANSIColors.RESET}{end_char}" else: if value is None or not valuestr: line = f"{etype}{end_char}" @@ -224,6 +192,7 @@ def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize= line = f"{etype}: {valuestr}{end_char}" return line + def _safe_string(value, what, func=str): try: return func(value) @@ -449,17 +418,6 @@ def _get_code_position(code, instruction_index): _RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. -class _ANSIColors: - RED = '\x1b[31m' - BOLD_RED = '\x1b[1;31m' - MAGENTA = '\x1b[35m' - BOLD_MAGENTA = '\x1b[1;35m' - GREEN = "\x1b[32m" - BOLD_GREEN = "\x1b[1;32m" - GREY = '\x1b[90m' - RESET = '\x1b[0m' - YELLOW = "\x1b[33m" - class StackSummary(list): """A list of FrameSummary objects, representing a stack of frames.""" @@ -564,15 +522,15 @@ def format_frame_summary(self, frame_summary, **kwargs): filename = "" if colorize: row.append(' File {}"{}"{}, line {}{}{}, in {}{}{}\n'.format( - _ANSIColors.MAGENTA, + ANSIColors.MAGENTA, filename, - _ANSIColors.RESET, - _ANSIColors.MAGENTA, + ANSIColors.RESET, + ANSIColors.MAGENTA, frame_summary.lineno, - _ANSIColors.RESET, - _ANSIColors.MAGENTA, + ANSIColors.RESET, + ANSIColors.MAGENTA, frame_summary.name, - _ANSIColors.RESET, + ANSIColors.RESET, ) ) else: @@ -696,11 +654,11 @@ def output_line(lineno): for color, group in itertools.groupby(itertools.zip_longest(line, carets, fillvalue=""), key=lambda x: x[1]): caret_group = list(group) if color == "^": - colorized_line_parts.append(_ANSIColors.BOLD_RED + "".join(char for char, _ in caret_group) + _ANSIColors.RESET) - colorized_carets_parts.append(_ANSIColors.BOLD_RED + "".join(caret for _, caret in caret_group) + _ANSIColors.RESET) + colorized_line_parts.append(ANSIColors.BOLD_RED + "".join(char for char, _ in caret_group) + ANSIColors.RESET) + colorized_carets_parts.append(ANSIColors.BOLD_RED + "".join(caret for _, caret in caret_group) + ANSIColors.RESET) elif color == "~": - colorized_line_parts.append(_ANSIColors.RED + "".join(char for char, _ in caret_group) + _ANSIColors.RESET) - colorized_carets_parts.append(_ANSIColors.RED + "".join(caret for _, caret in caret_group) + _ANSIColors.RESET) + colorized_line_parts.append(ANSIColors.RED + "".join(char for char, _ in caret_group) + ANSIColors.RESET) + colorized_carets_parts.append(ANSIColors.RED + "".join(caret for _, caret in caret_group) + ANSIColors.RESET) else: colorized_line_parts.append("".join(char for char, _ in caret_group)) colorized_carets_parts.append("".join(caret for _, caret in caret_group)) @@ -1307,12 +1265,12 @@ def _format_syntax_error(self, stype, **kwargs): if self.lineno is not None: if colorize: yield ' File {}"{}"{}, line {}{}{}\n'.format( - _ANSIColors.MAGENTA, + ANSIColors.MAGENTA, self.filename or "", - _ANSIColors.RESET, - _ANSIColors.MAGENTA, + ANSIColors.RESET, + ANSIColors.MAGENTA, self.lineno, - _ANSIColors.RESET, + ANSIColors.RESET, ) else: yield ' File "{}", line {}\n'.format( @@ -1352,11 +1310,11 @@ def _format_syntax_error(self, stype, **kwargs): # colorize from colno to end_colno ltext = ( ltext[:colno] + - _ANSIColors.BOLD_RED + ltext[colno:end_colno] + _ANSIColors.RESET + + ANSIColors.BOLD_RED + ltext[colno:end_colno] + ANSIColors.RESET + ltext[end_colno:] ) - start_color = _ANSIColors.BOLD_RED - end_color = _ANSIColors.RESET + start_color = ANSIColors.BOLD_RED + end_color = ANSIColors.RESET yield ' {}\n'.format(ltext) yield ' {}{}{}{}\n'.format( "".join(caretspace), @@ -1369,12 +1327,12 @@ def _format_syntax_error(self, stype, **kwargs): msg = self.msg or "" if colorize: yield "{}{}{}: {}{}{}{}\n".format( - _ANSIColors.BOLD_MAGENTA, + ANSIColors.BOLD_MAGENTA, stype, - _ANSIColors.RESET, - _ANSIColors.MAGENTA, + ANSIColors.RESET, + ANSIColors.MAGENTA, msg, - _ANSIColors.RESET, + ANSIColors.RESET, filename_suffix) else: yield "{}: {}{}\n".format(stype, msg, filename_suffix) diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index f44abf1f9c3121..ba320842173b48 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -20,6 +20,7 @@ static const char* _Py_stdlib_module_names[] = { "_codecs_tw", "_collections", "_collections_abc", +"_colorize", "_compat_pickle", "_compression", "_contextvars", From 1161ab9085ee6e2d6efdf497c81129ba0837031b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 19:15:30 +0000 Subject: [PATCH 146/217] build(deps-dev): bump types-psutil from 5.9.5.20240316 to 5.9.5.20240423 in /Tools (#118464) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Tools/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/requirements-dev.txt b/Tools/requirements-dev.txt index ddeff674e92457..1767727373918f 100644 --- a/Tools/requirements-dev.txt +++ b/Tools/requirements-dev.txt @@ -3,5 +3,5 @@ mypy==1.10.0 # needed for peg_generator: -types-psutil==5.9.5.20240316 +types-psutil==5.9.5.20240423 types-setuptools==69.5.0.20240423 From 6763bfcc0fbab7895514c1f954d79a2555036323 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 1 May 2024 21:51:40 +0100 Subject: [PATCH 147/217] gh-118272: set stacktop to 0 before freeing contents, to avoid access to invalid objects during GC (#118478) --- Python/frame.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Python/frame.c b/Python/frame.c index ec390e7426ad47..2bb12823572028 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -98,10 +98,11 @@ void _PyFrame_ClearLocals(_PyInterpreterFrame *frame) { assert(frame->stacktop >= 0); - for (int i = 0; i < frame->stacktop; i++) { + int stacktop = frame->stacktop; + frame->stacktop = 0; + for (int i = 0; i < stacktop; i++) { Py_XDECREF(frame->localsplus[i]); } - frame->stacktop = 0; Py_CLEAR(frame->f_locals); } From 8a50544a998764b8a315b316ceadaebdc748d8fe Mon Sep 17 00:00:00 2001 From: mpage Date: Wed, 1 May 2024 13:59:12 -0700 Subject: [PATCH 148/217] gh-118433: Temporarily skip `test_interrupt_main_subthread` in free-threaded builds (#118485) Free-threaded builds can intermittently tickle a longstanding bug (24 years!) in the implementation of `threading.Condition`, leading to flakiness in the test suite. Fixing the underlying issue will require more discussion, and will likely apply to most of the concurrency primitives in the `threading` module that are written in Python. See gh-118433 for more details. --- Lib/test/test_threading.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 362a3f9c4a01d1..0047e8a8798d4e 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -3,7 +3,7 @@ """ import test.support -from test.support import threading_helper, requires_subprocess +from test.support import threading_helper, requires_subprocess, requires_gil_enabled from test.support import verbose, cpython_only, os_helper from test.support.import_helper import import_module from test.support.script_helper import assert_python_ok, assert_python_failure @@ -2025,6 +2025,7 @@ def check_interrupt_main_noerror(self, signum): # Restore original handler signal.signal(signum, handler) + @requires_gil_enabled("gh-118433: Flaky due to a longstanding bug") def test_interrupt_main_subthread(self): # Calling start_new_thread with a function that executes interrupt_main # should raise KeyboardInterrupt upon completion. From 39981fd07a479ef3cefb38ee96ac319fe00f2dde Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 1 May 2024 22:18:31 +0100 Subject: [PATCH 149/217] GH-118095: Make sure that progress is made if there are pending calls being handled. (GH-118484) --- Include/internal/pycore_ceval_state.h | 2 +- Python/ceval_gil.c | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_ceval_state.h b/Include/internal/pycore_ceval_state.h index 11f2a100bf531e..376d96ad5d334c 100644 --- a/Include/internal/pycore_ceval_state.h +++ b/Include/internal/pycore_ceval_state.h @@ -41,7 +41,7 @@ struct _pending_call { #define MAXPENDINGCALLSLOOP_MAIN 0 struct _pending_calls { - int busy; + PyThreadState *handling_thread; PyMutex mutex; /* Request for running pending calls. */ int32_t npending; diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index fdbb4882c3d711..e4f7217255efd8 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -877,21 +877,20 @@ make_pending_calls(PyThreadState *tstate) /* Only one thread (per interpreter) may run the pending calls at once. In the same way, we don't do recursive pending calls. */ PyMutex_Lock(&pending->mutex); - if (pending->busy) { + if (pending->handling_thread != NULL) { /* A pending call was added after another thread was already handling the pending calls (and had already "unsignaled"). Once that thread is done, it may have taken care of all the pending calls, or there might be some still waiting. - Regardless, this interpreter's pending calls will stay - "signaled" until that first thread has finished. At that - point the next thread to trip the eval breaker will take - care of any remaining pending calls. Until then, though, - all the interpreter's threads will be tripping the eval - breaker every time it's checked. */ + To avoid all threads constantly stopping on the eval breaker, + we clear the bit for this thread and make sure it is set + for the thread currently handling the pending call. */ + _Py_set_eval_breaker_bit(pending->handling_thread, _PY_CALLS_TO_DO_BIT); + _Py_unset_eval_breaker_bit(tstate, _PY_CALLS_TO_DO_BIT); PyMutex_Unlock(&pending->mutex); return 0; } - pending->busy = 1; + pending->handling_thread = tstate; PyMutex_Unlock(&pending->mutex); /* unsignal before starting to call callbacks, so that any callback @@ -900,7 +899,7 @@ make_pending_calls(PyThreadState *tstate) int32_t npending; if (_make_pending_calls(pending, &npending) != 0) { - pending->busy = 0; + pending->handling_thread = NULL; /* There might not be more calls to make, but we play it safe. */ signal_pending_calls(tstate, interp); return -1; @@ -912,7 +911,7 @@ make_pending_calls(PyThreadState *tstate) if (_Py_IsMainThread() && _Py_IsMainInterpreter(interp)) { if (_make_pending_calls(pending_main, &npending) != 0) { - pending->busy = 0; + pending->handling_thread = NULL; /* There might not be more calls to make, but we play it safe. */ signal_pending_calls(tstate, interp); return -1; @@ -923,7 +922,7 @@ make_pending_calls(PyThreadState *tstate) } } - pending->busy = 0; + pending->handling_thread = NULL; return 0; } From 424438b11ec90110054f720bfa6ea67d644cc2ec Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 1 May 2024 14:35:49 -0700 Subject: [PATCH 150/217] GH-113464: Display a warning when building the JIT (GH-118481) --- Tools/jit/_llvm.py | 1 + Tools/jit/_schema.py | 1 + Tools/jit/_stencils.py | 8 +++---- Tools/jit/_targets.py | 49 +++++++++++++++++++++++++----------------- Tools/jit/_writer.py | 1 + Tools/jit/build.py | 4 ++-- 6 files changed, 38 insertions(+), 26 deletions(-) diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py index 74a048ccbfcc58..45bd69ff861b56 100644 --- a/Tools/jit/_llvm.py +++ b/Tools/jit/_llvm.py @@ -1,4 +1,5 @@ """Utilities for invoking LLVM tools.""" + import asyncio import functools import os diff --git a/Tools/jit/_schema.py b/Tools/jit/_schema.py index 6aef887e12475b..228fc389584dd7 100644 --- a/Tools/jit/_schema.py +++ b/Tools/jit/_schema.py @@ -1,4 +1,5 @@ """Schema for the JSON produced by llvm-readobj --elf-output-style=JSON.""" + import typing HoleKind: typing.TypeAlias = typing.Literal[ diff --git a/Tools/jit/_stencils.py b/Tools/jit/_stencils.py index f8ecffcf3ddda2..9feceb45388d05 100644 --- a/Tools/jit/_stencils.py +++ b/Tools/jit/_stencils.py @@ -1,4 +1,5 @@ """Core data structures for compiled code templates.""" + import dataclasses import enum import sys @@ -29,7 +30,7 @@ class HoleValue(enum.Enum): OPARG = enum.auto() # The current uop's operand on 64-bit platforms (exposed as _JIT_OPERAND): OPERAND = enum.auto() - # The current uop's operand on 32-bit platforms (exposed as _JIT_OPERAND_HI and _JIT_OPERAND_LO): + # The current uop's operand on 32-bit platforms (exposed as _JIT_OPERAND_HI/LO): OPERAND_HI = enum.auto() OPERAND_LO = enum.auto() # The current uop's target (exposed as _JIT_TARGET): @@ -203,9 +204,8 @@ def process_relocations(self, *, alignment: int = 1) -> None: """Fix up all GOT and internal relocations for this stencil group.""" for hole in self.code.holes.copy(): if ( - hole.kind in { - "R_AARCH64_CALL26", "R_AARCH64_JUMP26", "ARM64_RELOC_BRANCH26" - } + hole.kind + in {"R_AARCH64_CALL26", "R_AARCH64_JUMP26", "ARM64_RELOC_BRANCH26"} and hole.value is HoleValue.ZERO ): self.code.pad(alignment) diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 274d17bcf38deb..23bb18947f80ea 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -1,4 +1,5 @@ """Target-specific code generation, parsing, and processing.""" + import asyncio import dataclasses import hashlib @@ -40,8 +41,8 @@ class _Target(typing.Generic[_S, _R]): args: typing.Sequence[str] = () ghccc: bool = False prefix: str = "" + stable: bool = False debug: bool = False - force: bool = False verbose: bool = False def _compute_digest(self, out: pathlib.Path) -> str: @@ -186,12 +187,19 @@ async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: tasks.append(group.create_task(coro, name=opname)) return {task.get_name(): task.result() for task in tasks} - def build(self, out: pathlib.Path, *, comment: str = "") -> None: + def build( + self, out: pathlib.Path, *, comment: str = "", force: bool = False + ) -> None: """Build jit_stencils.h in the given directory.""" + if not self.stable: + warning = f"JIT support for {self.triple} is still experimental!" + request = "Please report any issues you encounter.".center(len(warning)) + outline = "=" * len(warning) + print("\n".join(["", outline, warning, request, outline, ""])) digest = f"// {self._compute_digest(out)}\n" jit_stencils = out / "jit_stencils.h" if ( - not self.force + not force and jit_stencils.exists() and jit_stencils.read_text().startswith(digest) ): @@ -450,9 +458,7 @@ def _handle_relocation( } | { "Offset": offset, "Symbol": {"Name": s}, - "Type": { - "Name": "X86_64_RELOC_BRANCH" | "X86_64_RELOC_SIGNED" as kind - }, + "Type": {"Name": "X86_64_RELOC_BRANCH" | "X86_64_RELOC_SIGNED" as kind}, }: offset += base s = s.removeprefix(self.prefix) @@ -481,23 +487,26 @@ def _handle_relocation( def get_target(host: str) -> _COFF | _ELF | _MachO: """Build a _Target for the given host "triple" and options.""" # ghccc currently crashes Clang when combined with musttail on aarch64. :( + target: _COFF | _ELF | _MachO if re.fullmatch(r"aarch64-apple-darwin.*", host): - return _MachO(host, alignment=8, prefix="_") - if re.fullmatch(r"aarch64-pc-windows-msvc", host): + target = _MachO(host, alignment=8, prefix="_") + elif re.fullmatch(r"aarch64-pc-windows-msvc", host): args = ["-fms-runtime-lib=dll"] - return _COFF(host, alignment=8, args=args) - if re.fullmatch(r"aarch64-.*-linux-gnu", host): + target = _COFF(host, alignment=8, args=args) + elif re.fullmatch(r"aarch64-.*-linux-gnu", host): args = ["-fpic"] - return _ELF(host, alignment=8, args=args) - if re.fullmatch(r"i686-pc-windows-msvc", host): + target = _ELF(host, alignment=8, args=args) + elif re.fullmatch(r"i686-pc-windows-msvc", host): args = ["-DPy_NO_ENABLE_SHARED"] - return _COFF(host, args=args, ghccc=True, prefix="_") - if re.fullmatch(r"x86_64-apple-darwin.*", host): - return _MachO(host, ghccc=True, prefix="_") - if re.fullmatch(r"x86_64-pc-windows-msvc", host): + target = _COFF(host, args=args, ghccc=True, prefix="_") + elif re.fullmatch(r"x86_64-apple-darwin.*", host): + target = _MachO(host, ghccc=True, prefix="_") + elif re.fullmatch(r"x86_64-pc-windows-msvc", host): args = ["-fms-runtime-lib=dll"] - return _COFF(host, args=args, ghccc=True) - if re.fullmatch(r"x86_64-.*-linux-gnu", host): + target = _COFF(host, args=args, ghccc=True) + elif re.fullmatch(r"x86_64-.*-linux-gnu", host): args = ["-fpic"] - return _ELF(host, args=args, ghccc=True) - raise ValueError(host) + target = _ELF(host, args=args, ghccc=True) + else: + raise ValueError(host) + return target diff --git a/Tools/jit/_writer.py b/Tools/jit/_writer.py index 6b36d8a9c66a3f..ccd67850c37787 100644 --- a/Tools/jit/_writer.py +++ b/Tools/jit/_writer.py @@ -1,4 +1,5 @@ """Utilities for writing StencilGroups out to a C header file.""" + import typing import _schema diff --git a/Tools/jit/build.py b/Tools/jit/build.py index 4d4ace14ebf26c..4a23c6f0afa74a 100644 --- a/Tools/jit/build.py +++ b/Tools/jit/build.py @@ -1,4 +1,5 @@ """Build an experimental just-in-time compiler for CPython.""" + import argparse import pathlib import shlex @@ -23,6 +24,5 @@ ) args = parser.parse_args() args.target.debug = args.debug - args.target.force = args.force args.target.verbose = args.verbose - args.target.build(pathlib.Path.cwd(), comment=comment) + args.target.build(pathlib.Path.cwd(), comment=comment, force=args.force) From a7711a2a4e5cf16b34fc284085da724a8c2c06dd Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Wed, 1 May 2024 23:44:55 +0200 Subject: [PATCH 151/217] gh-117607: Speedup os.path.relpath() (GH-117608) --- Lib/ntpath.py | 19 ++++++++++--------- Lib/posixpath.py | 8 +++++--- ...-04-07-18-42-09.gh-issue-117607.C978BD.rst | 1 + 3 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-07-18-42-09.gh-issue-117607.C978BD.rst diff --git a/Lib/ntpath.py b/Lib/ntpath.py index e810b655e5ac85..b833e0bad2645f 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -805,6 +805,9 @@ def realpath(path, *, strict=False): def relpath(path, start=None): """Return a relative version of a path""" path = os.fspath(path) + if not path: + raise ValueError("no path specified") + if isinstance(path, bytes): sep = b'\\' curdir = b'.' @@ -816,22 +819,20 @@ def relpath(path, start=None): if start is None: start = curdir + else: + start = os.fspath(start) - if not path: - raise ValueError("no path specified") - - start = os.fspath(start) try: - start_abs = abspath(normpath(start)) - path_abs = abspath(normpath(path)) + start_abs = abspath(start) + path_abs = abspath(path) start_drive, _, start_rest = splitroot(start_abs) path_drive, _, path_rest = splitroot(path_abs) if normcase(start_drive) != normcase(path_drive): raise ValueError("path is on mount %r, start on mount %r" % ( path_drive, start_drive)) - start_list = [x for x in start_rest.split(sep) if x] - path_list = [x for x in path_rest.split(sep) if x] + start_list = start_rest.split(sep) if start_rest else [] + path_list = path_rest.split(sep) if path_rest else [] # Work out how much of the filepath is shared by start and path. i = 0 for e1, e2 in zip(start_list, path_list): @@ -842,7 +843,7 @@ def relpath(path, start=None): rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir - return join(*rel_list) + return sep.join(rel_list) except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning): genericpath._check_arg_types('relpath', path, start) raise diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 56b7915826daf4..f189c3359fbea6 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -532,15 +532,17 @@ def relpath(path, start=None): start = os.fspath(start) try: - start_list = [x for x in abspath(start).split(sep) if x] - path_list = [x for x in abspath(path).split(sep) if x] + start_tail = abspath(start).lstrip(sep) + path_tail = abspath(path).lstrip(sep) + start_list = start_tail.split(sep) if start_tail else [] + path_list = path_tail.split(sep) if path_tail else [] # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir - return join(*rel_list) + return sep.join(rel_list) except (TypeError, AttributeError, BytesWarning, DeprecationWarning): genericpath._check_arg_types('relpath', path, start) raise diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-07-18-42-09.gh-issue-117607.C978BD.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-07-18-42-09.gh-issue-117607.C978BD.rst new file mode 100644 index 00000000000000..0c17dfd95ec0ec --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-07-18-42-09.gh-issue-117607.C978BD.rst @@ -0,0 +1 @@ +Speedup :func:`os.path.relpath`. From c408c36e9b346f9f15a34e98a5596f311df65efa Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Wed, 1 May 2024 17:58:22 -0400 Subject: [PATCH 152/217] gh-118413: Temporarily skip `test_release_task_refs` in free-threaded builds (#118491) --- Lib/test/_test_multiprocessing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index a74b61013c4848..91f85990116ee7 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2804,6 +2804,7 @@ def test_map_no_failfast(self): # check that we indeed waited for all jobs self.assertGreater(time.monotonic() - t_start, 0.9) + @support.requires_gil_enabled("gh-118413: test is flaky with GIL disabled") def test_release_task_refs(self): # Issue #29861: task arguments and results should not be kept # alive after we are done with them. From 97feb4a78bf1ebdec26b685fd509cf57b3333e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Thu, 2 May 2024 00:13:32 +0100 Subject: [PATCH 153/217] GH-118174: specify the type for the path argument of shutil.which --- Doc/library/shutil.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 8e5828c789e4e2..542d2886477792 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -449,8 +449,9 @@ Directory and files operations *mode* is a permission mask passed to :func:`os.access`, by default determining if the file exists and executable. - When no *path* is specified, the results of :func:`os.environ` are used, - returning either the "PATH" value or a fallback of :data:`os.defpath`. + *path* is a "``PATH`` string" specifying the lookup directory list. When no + *path* is specified, the results of :func:`os.environ` are used, returning + either the "PATH" value or a fallback of :data:`os.defpath`. On Windows, the current directory is prepended to the *path* if *mode* does not include ``os.X_OK``. When the *mode* does include ``os.X_OK``, the From a524152b8c56f82d807ecdd7ae9d39cfd643c469 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 May 2024 16:36:29 -0700 Subject: [PATCH 154/217] gh-118335: Make REGEN_JIT_COMMAND empty if tier2 interpreter enabled (#118493) Also patch up news blurb for gh-118339 (add warning that PYTHON_UOPS is now PYTHON_JIT). --- ...-04-26-14-06-18.gh-issue-118335.SRFsxO.rst | 3 +++ configure | 24 ++++++++++++------- configure.ac | 22 ++++++++++------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-26-14-06-18.gh-issue-118335.SRFsxO.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-26-14-06-18.gh-issue-118335.SRFsxO.rst index e13edbbbe528eb..54295a75c02e85 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-04-26-14-06-18.gh-issue-118335.SRFsxO.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-26-14-06-18.gh-issue-118335.SRFsxO.rst @@ -2,3 +2,6 @@ Change how to use the tier 2 interpreter. Instead of running Python with ``-X uops`` or setting the environment variable ``PYTHON_UOPS=1``, this choice is now made at build time by configuring with ``--enable-experimental-jit=interpreter``. + +**Beware!** This changes the environment variable to enable or disable +micro-ops to ``PYTHON_JIT``. The old ``PYTHON_UOPS`` is no longer used. diff --git a/configure b/configure index 24e7396e389fd1..cc85aed2aa51c2 100755 --- a/configure +++ b/configure @@ -8235,18 +8235,24 @@ else $as_nop fi case $enable_experimental_jit in - no) enable_experimental_jit=no ;; - yes) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=1" ;; - yes-off) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=3" ;; - interpreter) enable_experimental_jit="-D_Py_TIER2=4" ;; - interpreter-off) enable_experimental_jit="-D_Py_TIER2=6" ;; # Secret option + no) jit_flags=""; tier2_flags="" ;; + yes) jit_flags="-D_Py_JIT"; tier2_flags="-D_Py_TIER2=1" ;; + yes-off) jit_flags="-D_Py_JIT"; tier2_flags="-D_Py_TIER2=3" ;; + interpreter) jit_flags=""; tier2_flags="-D_Py_TIER2=4" ;; + interpreter-off) jit_flags=""; tier2_flags="-D_Py_TIER2=6" ;; # Secret option *) as_fn_error $? "invalid argument: --enable-experimental-jit=$enable_experimental_jit; expected no|yes|yes-off|interpreter" "$LINENO" 5 ;; esac -if test "x$enable_experimental_jit" = xno +if ${tier2_flags:+false} : then : else $as_nop - as_fn_append CFLAGS_NODIST " $enable_experimental_jit" + as_fn_append CFLAGS_NODIST " $tier2_flags" +fi +if ${jit_flags:+false} : +then : + +else $as_nop + as_fn_append CFLAGS_NODIST " $jit_flags" REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host" JIT_STENCILS_H="jit_stencils.h" if test "x$Py_DEBUG" = xtrue @@ -8256,8 +8262,8 @@ fi fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_experimental_jit" >&5 -printf "%s\n" "$enable_experimental_jit" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $tier2_flags $jit_flags" >&5 +printf "%s\n" "$tier2_flags $jit_flags" >&6; } # Enable optimization flags diff --git a/configure.ac b/configure.ac index 0f1177872d9137..c55e33add20fde 100644 --- a/configure.ac +++ b/configure.ac @@ -1776,18 +1776,22 @@ AC_ARG_ENABLE([experimental-jit], [], [enable_experimental_jit=no]) case $enable_experimental_jit in - no) enable_experimental_jit=no ;; - yes) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=1" ;; - yes-off) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=3" ;; - interpreter) enable_experimental_jit="-D_Py_TIER2=4" ;; - interpreter-off) enable_experimental_jit="-D_Py_TIER2=6" ;; # Secret option + no) jit_flags=""; tier2_flags="" ;; + yes) jit_flags="-D_Py_JIT"; tier2_flags="-D_Py_TIER2=1" ;; + yes-off) jit_flags="-D_Py_JIT"; tier2_flags="-D_Py_TIER2=3" ;; + interpreter) jit_flags=""; tier2_flags="-D_Py_TIER2=4" ;; + interpreter-off) jit_flags=""; tier2_flags="-D_Py_TIER2=6" ;; # Secret option *) AC_MSG_ERROR( [invalid argument: --enable-experimental-jit=$enable_experimental_jit; expected no|yes|yes-off|interpreter]) ;; esac -AS_VAR_IF([enable_experimental_jit], - [no], +AS_VAR_IF([tier2_flags], [], - [AS_VAR_APPEND([CFLAGS_NODIST], [" $enable_experimental_jit"]) + [], + [AS_VAR_APPEND([CFLAGS_NODIST], [" $tier2_flags"])]) +AS_VAR_IF([jit_flags], + [], + [], + [AS_VAR_APPEND([CFLAGS_NODIST], [" $jit_flags"]) AS_VAR_SET([REGEN_JIT_COMMAND], ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host"]) AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"]) @@ -1797,7 +1801,7 @@ AS_VAR_IF([enable_experimental_jit], [])]) AC_SUBST([REGEN_JIT_COMMAND]) AC_SUBST([JIT_STENCILS_H]) -AC_MSG_RESULT([$enable_experimental_jit]) +AC_MSG_RESULT([$tier2_flags $jit_flags]) # Enable optimization flags AC_SUBST([DEF_MAKE_ALL_RULE]) From 526ca4c09eed63e19ea5ffc46a76c5d8077a8bc9 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 1 May 2024 17:40:28 -0600 Subject: [PATCH 155/217] gh-117953: Work Relative to Specific Extension Kinds in the Import Machinery (gh-118205) This change will make some later changes simpler. --- Include/internal/pycore_importdl.h | 32 ++++- Python/import.c | 151 +++++++++++++++------ Python/importdl.c | 207 +++++++++++++++++++++++++---- 3 files changed, 324 insertions(+), 66 deletions(-) diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index adb89167d124eb..b0af28da34e087 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -15,8 +15,15 @@ extern "C" { extern const char *_PyImport_DynLoadFiletab[]; -typedef PyObject *(*PyModInitFunction)(void); +typedef enum ext_module_kind { + _Py_ext_module_kind_UNKNOWN = 0, + _Py_ext_module_kind_SINGLEPHASE = 1, + _Py_ext_module_kind_MULTIPHASE = 2, + _Py_ext_module_kind_INVALID = 3, +} _Py_ext_module_kind; + +/* Input for loading an extension module. */ struct _Py_ext_module_loader_info { PyObject *filename; #ifndef MS_WINDOWS @@ -43,10 +50,33 @@ extern int _Py_ext_module_loader_info_init_from_spec( struct _Py_ext_module_loader_info *info, PyObject *spec); +/* The result from running an extension module's init function. */ struct _Py_ext_module_loader_result { PyModuleDef *def; PyObject *module; + _Py_ext_module_kind kind; + struct _Py_ext_module_loader_result_error *err; + struct _Py_ext_module_loader_result_error { + enum _Py_ext_module_loader_result_error_kind { + _Py_ext_module_loader_result_EXCEPTION = 0, + _Py_ext_module_loader_result_ERR_MISSING = 1, + _Py_ext_module_loader_result_ERR_UNREPORTED_EXC = 2, + _Py_ext_module_loader_result_ERR_UNINITIALIZED = 3, + _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE = 4, + _Py_ext_module_loader_result_ERR_NOT_MODULE = 5, + _Py_ext_module_loader_result_ERR_MISSING_DEF = 6, + } kind; + PyObject *exc; + } _err; }; +extern void _Py_ext_module_loader_result_clear( + struct _Py_ext_module_loader_result *res); +extern void _Py_ext_module_loader_result_apply_error( + struct _Py_ext_module_loader_result *res, + const char *name); + +/* The module init function. */ +typedef PyObject *(*PyModInitFunction)(void); extern PyModInitFunction _PyImport_GetModInitFunc( struct _Py_ext_module_loader_info *info, FILE *fp); diff --git a/Python/import.c b/Python/import.c index 447114ab115bc7..0c51ffc6285a9c 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1193,26 +1193,63 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) } #ifndef NDEBUG -static bool -is_singlephase(PyModuleDef *def) +static _Py_ext_module_kind +_get_extension_kind(PyModuleDef *def, bool check_size) { + _Py_ext_module_kind kind; if (def == NULL) { /* It must be a module created by reload_singlephase_extension() * from m_copy. Ideally we'd do away with this case. */ - return true; + kind = _Py_ext_module_kind_SINGLEPHASE; } - else if (def->m_slots == NULL) { - return true; + else if (def->m_slots != NULL) { + kind = _Py_ext_module_kind_MULTIPHASE; } - else { - return false; + else if (check_size && def->m_size == -1) { + kind = _Py_ext_module_kind_SINGLEPHASE; } -} + else if (def->m_base.m_init != NULL) { + kind = _Py_ext_module_kind_SINGLEPHASE; + } + else { + // This is probably single-phase init, but a multi-phase + // module *can* have NULL m_slots. + kind = _Py_ext_module_kind_UNKNOWN; + } + return kind; +} + +/* The module might not be fully initialized yet + * and PyModule_FromDefAndSpec() checks m_size + * so we skip m_size. */ +#define assert_multiphase_def(def) \ + do { \ + _Py_ext_module_kind kind = _get_extension_kind(def, false); \ + assert(kind == _Py_ext_module_kind_MULTIPHASE \ + /* m_slots can be NULL. */ \ + || kind == _Py_ext_module_kind_UNKNOWN); \ + } while (0) + +#define assert_singlephase_def(def) \ + do { \ + _Py_ext_module_kind kind = _get_extension_kind(def, true); \ + assert(kind == _Py_ext_module_kind_SINGLEPHASE \ + || kind == _Py_ext_module_kind_UNKNOWN); \ + } while (0) + +#define assert_singlephase(def) \ + assert_singlephase_def(def) + +#else /* defined(NDEBUG) */ +#define assert_multiphase_def(def) +#define assert_singlephase_def(def) +#define assert_singlephase(def) #endif struct singlephase_global_update { PyObject *m_dict; + PyModInitFunction m_init; }; static int @@ -1226,10 +1263,24 @@ update_global_state_for_extension(PyThreadState *tstate, assert(def->m_base.m_copy == NULL); } else { - assert(def->m_base.m_init != NULL - || is_core_module(tstate->interp, name, path)); - if (singlephase->m_dict == NULL) { + if (singlephase->m_init != NULL) { + assert(singlephase->m_dict == NULL); + assert(def->m_base.m_copy == NULL); + assert(def->m_size >= 0); + /* Remember pointer to module init function. */ + // XXX If two modules share a def then def->m_base will + // reflect the last one added (here) to the global cache. + // We should prevent this somehow. The simplest solution + // is probably to store m_copy/m_init in the cache along + // with the def, rather than within the def. + def->m_base.m_init = singlephase->m_init; + } + else if (singlephase->m_dict == NULL) { + /* It must be a core builtin module. */ + assert(is_core_module(tstate->interp, name, path)); + assert(def->m_size == -1); assert(def->m_base.m_copy == NULL); + assert(def->m_base.m_init == NULL); } else { assert(PyDict_Check(singlephase->m_dict)); @@ -1316,7 +1367,7 @@ import_find_extension(PyThreadState *tstate, if (def == NULL) { return NULL; } - assert(is_singlephase(def)); + assert_singlephase(def); /* It may have been successfully imported previously in an interpreter that allows legacy modules @@ -1369,11 +1420,26 @@ import_find_extension(PyThreadState *tstate, } struct _Py_ext_module_loader_result res; if (_PyImport_RunModInitFunc(def->m_base.m_init, info, &res) < 0) { + _Py_ext_module_loader_result_apply_error(&res, name_buf); return NULL; } assert(!PyErr_Occurred()); + assert(res.err == NULL); + assert(res.kind == _Py_ext_module_kind_SINGLEPHASE); mod = res.module; - // XXX __file__ doesn't get set! + /* Tchnically, the init function could return a different module def. + * Then we would probably need to update the global cache. + * However, we don't expect anyone to change the def. */ + assert(res.def == def); + _Py_ext_module_loader_result_clear(&res); + + /* Remember the filename as the __file__ attribute */ + if (info->filename != NULL) { + if (PyModule_AddObjectRef(mod, "__file__", info->filename) < 0) { + PyErr_Clear(); /* Not important enough to report */ + } + } + if (PyObject_SetItem(modules, info->name, mod) == -1) { Py_DECREF(mod); return NULL; @@ -1398,78 +1464,89 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, struct _Py_ext_module_loader_info *info, PyObject *spec, PyObject *modules) { + /* Core modules go through _PyImport_FixupBuiltin(). */ + assert(!is_core_module(tstate->interp, info->name, info->path)); + PyObject *mod = NULL; PyModuleDef *def = NULL; + const char *name_buf = PyBytes_AS_STRING(info->name_encoded); struct _Py_ext_module_loader_result res; if (_PyImport_RunModInitFunc(p0, info, &res) < 0) { /* We discard res.def. */ assert(res.module == NULL); - assert(PyErr_Occurred()); - goto finally; + _Py_ext_module_loader_result_apply_error(&res, name_buf); + return NULL; } assert(!PyErr_Occurred()); + assert(res.err == NULL); mod = res.module; res.module = NULL; def = res.def; assert(def != NULL); - if (mod == NULL) { - //assert(!is_singlephase(def)); + if (res.kind == _Py_ext_module_kind_MULTIPHASE) { + assert_multiphase_def(def); assert(mod == NULL); mod = PyModule_FromDefAndSpec(def, spec); if (mod == NULL) { - goto finally; + goto error; } } else { - assert(is_singlephase(def)); + assert(res.kind == _Py_ext_module_kind_SINGLEPHASE); + assert_singlephase_def(def); assert(PyModule_Check(mod)); - const char *name_buf = PyBytes_AS_STRING(info->name_encoded); if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { - Py_CLEAR(mod); - goto finally; + goto error; } - /* Remember pointer to module init function. */ - def->m_base.m_init = p0; - + /* Remember the filename as the __file__ attribute */ if (info->filename != NULL) { - /* Remember the filename as the __file__ attribute */ if (PyModule_AddObjectRef(mod, "__file__", info->filename) < 0) { PyErr_Clear(); /* Not important enough to report */ } } + /* Update global import state. */ struct singlephase_global_update singlephase = {0}; // gh-88216: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. - if (def->m_size == -1 - && !is_core_module(tstate->interp, info->name, info->path)) - { + if (def->m_size == -1) { + /* We will reload from m_copy. */ + assert(def->m_base.m_init == NULL); singlephase.m_dict = PyModule_GetDict(mod); assert(singlephase.m_dict != NULL); } + else { + /* We will reload via the init function. */ + assert(def->m_size >= 0); + singlephase.m_init = p0; + } if (update_global_state_for_extension( tstate, info->path, info->name, def, &singlephase) < 0) { - Py_CLEAR(mod); - goto finally; + goto error; } + /* Update per-interpreter import state. */ PyObject *modules = get_modules_dict(tstate, true); if (finish_singlephase_extension( tstate, mod, def, info->name, modules) < 0) { - Py_CLEAR(mod); - goto finally; + goto error; } } -finally: + _Py_ext_module_loader_result_clear(&res); return mod; + +error: + Py_XDECREF(mod); + _Py_ext_module_loader_result_clear(&res); + return NULL; } @@ -1532,7 +1609,7 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, * module state, but we also don't populate def->m_base.m_copy * for them. */ assert(is_core_module(tstate->interp, nameobj, nameobj)); - assert(is_singlephase(def)); + assert_singlephase_def(def); assert(def->m_size == -1); assert(def->m_base.m_copy == NULL); @@ -1586,7 +1663,7 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) PyObject *mod = import_find_extension(tstate, &info); if (mod != NULL) { assert(!_PyErr_Occurred(tstate)); - assert(is_singlephase(_PyModule_GetDef(mod))); + assert_singlephase(_PyModule_GetDef(mod)); goto finally; } else if (_PyErr_Occurred(tstate)) { @@ -3940,7 +4017,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) mod = import_find_extension(tstate, &info); if (mod != NULL) { assert(!_PyErr_Occurred(tstate)); - assert(is_singlephase(_PyModule_GetDef(mod))); + assert_singlephase(_PyModule_GetDef(mod)); goto finally; } else if (_PyErr_Occurred(tstate)) { diff --git a/Python/importdl.c b/Python/importdl.c index 3e4ea175c0cfbc..a3091946bc94bf 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -28,6 +28,11 @@ extern dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix, const char *pathname, FILE *fp); #endif + +/***********************************/ +/* module info to use when loading */ +/***********************************/ + static const char * const ascii_only_prefix = "PyInit"; static const char * const nonascii_prefix = "PyInitU"; @@ -205,6 +210,150 @@ _Py_ext_module_loader_info_init_from_spec( } +/********************************/ +/* module init function results */ +/********************************/ + +void +_Py_ext_module_loader_result_clear(struct _Py_ext_module_loader_result *res) +{ + /* Instead, the caller should have called + * _Py_ext_module_loader_result_apply_error(). */ + assert(res->err == NULL); + *res = (struct _Py_ext_module_loader_result){0}; +} + +static void +_Py_ext_module_loader_result_set_error( + struct _Py_ext_module_loader_result *res, + enum _Py_ext_module_loader_result_error_kind kind) +{ +#ifndef NDEBUG + switch (kind) { + case _Py_ext_module_loader_result_EXCEPTION: /* fall through */ + case _Py_ext_module_loader_result_ERR_UNREPORTED_EXC: + assert(PyErr_Occurred()); + break; + case _Py_ext_module_loader_result_ERR_MISSING: /* fall through */ + case _Py_ext_module_loader_result_ERR_UNINITIALIZED: /* fall through */ + case _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE: /* fall through */ + case _Py_ext_module_loader_result_ERR_NOT_MODULE: /* fall through */ + case _Py_ext_module_loader_result_ERR_MISSING_DEF: + assert(!PyErr_Occurred()); + break; + default: + /* We added a new error kind but forgot to add it to this switch. */ + assert(0); + } +#endif + + assert(res->err == NULL && res->_err.exc == NULL); + res->err = &res->_err; + *res->err = (struct _Py_ext_module_loader_result_error){ + .kind=kind, + .exc=PyErr_GetRaisedException(), + }; + + /* For some kinds, we also set/check res->kind. */ + switch (kind) { + case _Py_ext_module_loader_result_ERR_UNINITIALIZED: + assert(res->kind == _Py_ext_module_kind_UNKNOWN); + res->kind = _Py_ext_module_kind_INVALID; + break; + /* None of the rest affect the result kind. */ + case _Py_ext_module_loader_result_EXCEPTION: /* fall through */ + case _Py_ext_module_loader_result_ERR_MISSING: /* fall through */ + case _Py_ext_module_loader_result_ERR_UNREPORTED_EXC: /* fall through */ + case _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE: /* fall through */ + case _Py_ext_module_loader_result_ERR_NOT_MODULE: /* fall through */ + case _Py_ext_module_loader_result_ERR_MISSING_DEF: + break; + default: + /* We added a new error kind but forgot to add it to this switch. */ + assert(0); + } +} + +void +_Py_ext_module_loader_result_apply_error( + struct _Py_ext_module_loader_result *res, + const char *name) +{ + assert(!PyErr_Occurred()); + assert(res->err != NULL && res->err == &res->_err); + struct _Py_ext_module_loader_result_error err = *res->err; + res->err = NULL; + + /* We're otherwise done with the result at this point. */ + _Py_ext_module_loader_result_clear(res); + +#ifndef NDEBUG + switch (err.kind) { + case _Py_ext_module_loader_result_EXCEPTION: /* fall through */ + case _Py_ext_module_loader_result_ERR_UNREPORTED_EXC: + assert(err.exc != NULL); + break; + case _Py_ext_module_loader_result_ERR_MISSING: /* fall through */ + case _Py_ext_module_loader_result_ERR_UNINITIALIZED: /* fall through */ + case _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE: /* fall through */ + case _Py_ext_module_loader_result_ERR_NOT_MODULE: /* fall through */ + case _Py_ext_module_loader_result_ERR_MISSING_DEF: + assert(err.exc == NULL); + break; + default: + /* We added a new error kind but forgot to add it to this switch. */ + assert(0); + } +#endif + + const char *msg = NULL; + switch (err.kind) { + case _Py_ext_module_loader_result_EXCEPTION: + break; + case _Py_ext_module_loader_result_ERR_MISSING: + msg = "initialization of %s failed without raising an exception"; + break; + case _Py_ext_module_loader_result_ERR_UNREPORTED_EXC: + msg = "initialization of %s raised unreported exception"; + break; + case _Py_ext_module_loader_result_ERR_UNINITIALIZED: + msg = "init function of %s returned uninitialized object"; + break; + case _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE: + msg = "initialization of %s did not return PyModuleDef"; + break; + case _Py_ext_module_loader_result_ERR_NOT_MODULE: + msg = "initialization of %s did not return an extension module"; + break; + case _Py_ext_module_loader_result_ERR_MISSING_DEF: + msg = "initialization of %s did not return a valid extension module"; + break; + default: + /* We added a new error kind but forgot to add it to this switch. */ + assert(0); + PyErr_Format(PyExc_SystemError, + "loading %s failed due to init function", name); + return; + } + + if (err.exc != NULL) { + PyErr_SetRaisedException(err.exc); + err.exc = NULL; /* PyErr_SetRaisedException() stole our reference. */ + if (msg != NULL) { + _PyErr_FormatFromCause(PyExc_SystemError, msg, name); + } + } + else { + assert(msg != NULL); + PyErr_Format(PyExc_SystemError, msg, name); + } +} + + +/********************************************/ +/* getting/running the module init function */ +/********************************************/ + PyModInitFunction _PyImport_GetModInitFunc(struct _Py_ext_module_loader_info *info, FILE *fp) @@ -245,8 +394,9 @@ _PyImport_RunModInitFunc(PyModInitFunction p0, struct _Py_ext_module_loader_info *info, struct _Py_ext_module_loader_result *p_res) { - struct _Py_ext_module_loader_result res = {0}; - const char *name_buf = PyBytes_AS_STRING(info->name_encoded); + struct _Py_ext_module_loader_result res = { + .kind=_Py_ext_module_kind_UNKNOWN, + }; /* Call the module init function. */ @@ -258,18 +408,18 @@ _PyImport_RunModInitFunc(PyModInitFunction p0, /* Validate the result (and populate "res". */ if (m == NULL) { - if (!PyErr_Occurred()) { - PyErr_Format( - PyExc_SystemError, - "initialization of %s failed without raising an exception", - name_buf); + if (PyErr_Occurred()) { + _Py_ext_module_loader_result_set_error( + &res, _Py_ext_module_loader_result_EXCEPTION); + } + else { + _Py_ext_module_loader_result_set_error( + &res, _Py_ext_module_loader_result_ERR_MISSING); } goto error; } else if (PyErr_Occurred()) { - _PyErr_FormatFromCause( - PyExc_SystemError, - "initialization of %s raised unreported exception", - name_buf); + _Py_ext_module_loader_result_set_error( + &res, _Py_ext_module_loader_result_ERR_UNREPORTED_EXC); /* We would probably be correct to decref m here, * but we weren't doing so before, * so we stick with doing nothing. */ @@ -281,9 +431,8 @@ _PyImport_RunModInitFunc(PyModInitFunction p0, /* This can happen when a PyModuleDef is returned without calling * PyModuleDef_Init on it */ - PyErr_Format(PyExc_SystemError, - "init function of %s returned uninitialized object", - name_buf); + _Py_ext_module_loader_result_set_error( + &res, _Py_ext_module_loader_result_ERR_UNINITIALIZED); /* Likewise, decref'ing here makes sense. However, the original * code has a note about "prevent segfault in DECREF", * so we play it safe and leave it alone. */ @@ -293,48 +442,50 @@ _PyImport_RunModInitFunc(PyModInitFunction p0, if (PyObject_TypeCheck(m, &PyModuleDef_Type)) { /* multi-phase init */ + res.kind = _Py_ext_module_kind_MULTIPHASE; res.def = (PyModuleDef *)m; /* Run PyModule_FromDefAndSpec() to finish loading the module. */ } else if (info->hook_prefix == nonascii_prefix) { - /* It should have been multi-phase init? */ + /* Non-ASCII is only supported for multi-phase init. */ + res.kind = _Py_ext_module_kind_MULTIPHASE; /* Don't allow legacy init for non-ASCII module names. */ - PyErr_Format( - PyExc_SystemError, - "initialization of %s did not return PyModuleDef", - name_buf); - Py_DECREF(m); - return -1; + _Py_ext_module_loader_result_set_error( + &res, _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE); + goto error; } else { /* single-phase init (legacy) */ + res.kind = _Py_ext_module_kind_SINGLEPHASE; res.module = m; if (!PyModule_Check(m)) { - PyErr_Format(PyExc_SystemError, - "initialization of %s did not return an extension " - "module", name_buf); + _Py_ext_module_loader_result_set_error( + &res, _Py_ext_module_loader_result_ERR_NOT_MODULE); goto error; } res.def = _PyModule_GetDef(m); if (res.def == NULL) { - PyErr_Format(PyExc_SystemError, - "initialization of %s did not return a valid extension " - "module", name_buf); + PyErr_Clear(); + _Py_ext_module_loader_result_set_error( + &res, _Py_ext_module_loader_result_ERR_MISSING_DEF); goto error; } } assert(!PyErr_Occurred()); + assert(res.err == NULL); *p_res = res; return 0; error: - assert(PyErr_Occurred()); + assert(!PyErr_Occurred()); + assert(res.err != NULL); Py_CLEAR(res.module); res.def = NULL; *p_res = res; + p_res->err = &p_res->_err; return -1; } From a37b0932285b5e883b13a46ff2a32f15d7339894 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 May 2024 17:48:34 -0700 Subject: [PATCH 156/217] gh-118335: Rename --experimental-interpreter on Windows to --experimental-jit-interpreter (#118497) Also fix docs for this in whatsnew. --- Doc/whatsnew/3.13.rst | 3 ++- PCbuild/build.bat | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index d67df8109a8ced..52fb9752cf4132 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -946,7 +946,8 @@ The ``--enable-experimental-jit`` flag has the following optional values: The interpreter can be disabled by running with ``PYTHON_JIT=0``. -(On Windows, use ``PCbuild/build.bat --enable-jit`` to enable the JIT.) +(On Windows, use ``PCbuild/build.bat --experimental-jit`` to enable the JIT +or ``--experimental-jit-interpreter`` to enable the Tier 2 interpreter.) See :pep:`744` for more details. diff --git a/PCbuild/build.bat b/PCbuild/build.bat index 43a99aa684483f..13bd895694f176 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -38,7 +38,7 @@ echo. --test-marker Enable the test marker within the build. echo. --regen Regenerate all opcodes, grammar and tokens. echo. --experimental-jit Enable the experimental just-in-time compiler. echo. --experimental-jit-off Ditto but off by default (PYTHON_JIT=1 enables). -echo. --experimental-interpreter Enable the experimental Tier 2 interpreter. +echo. --experimental-jit-interpreter Enable the experimental Tier 2 interpreter. echo. echo.Available flags to avoid building certain modules. echo.These flags have no effect if '-e' is not given: @@ -91,8 +91,8 @@ if "%~1"=="-V" shift & goto Version if "%~1"=="--regen" (set Regen=true) & shift & goto CheckOpts if "%~1"=="--experimental-jit" (set UseJIT=true) & (set UseTIER2=1) & shift & goto CheckOpts if "%~1"=="--experimental-jit-off" (set UseJIT=true) & (set UseTIER2=3) & shift & goto CheckOpts -if "%~1"=="--experimental-interpreter" (set UseTIER2=4) & shift & goto CheckOpts -if "%~1"=="--experimental-interpreter-off" (set UseTIER2=6) & shift & goto CheckOpts +if "%~1"=="--experimental-jit-interpreter" (set UseTIER2=4) & shift & goto CheckOpts +if "%~1"=="--experimental-jit-interpreter-off" (set UseTIER2=6) & shift & goto CheckOpts rem These use the actual property names used by MSBuild. We could just let rem them in through the environment, but we specify them on the command line rem anyway for visibility so set defaults after this From a6b610a94bee0e4436aee2825c14f05ec2f22f75 Mon Sep 17 00:00:00 2001 From: Andrew Zipperer <47086307+zipperer@users.noreply.github.com> Date: Thu, 2 May 2024 00:37:12 -0500 Subject: [PATCH 157/217] docs: typo: tiny grammar change: "pointed by" -> "pointed to by" (#118411) * docs: tiny grammar change: "pointed by" -> "pointed to by" This commit uses "file pointed to by" to replace "file pointed by" in - doc for shutil.copytree - docstring for shutil.copytree - docstring _abc.PathBase.open - docstring for pathlib.Path.open - doc for os.copy_file_range - doc for os.splice The docs use "file pointed to by" more frequently than "file pointed by". So, this commit replaces the uses of "file pointed by" in order to make the uses consistent through the docs. ```bash $ grep -ri 'pointed to by' cpython/ ``` yields more results than ```bash $ grep -ri 'pointed by' cpython/ ``` Separately: There are two occurrences of "tree pointed by": - cpython/Doc/library/xml.etree.elementtree.rst for `xml.etree.ElementInclude.include` - cpython/Lib/xml/etree/ElementInclude.py for `include` For those uses of "tree pointed by", I expect "tree pointed to by" instead. However, I found enough uses online of (a) "tree pointed by" rather than (b) "tree pointed to by" to convince me that (a) is in common use. So, this commit does not replace those occurrences of "tree pointed by" to "tree pointed to by". But I will replace them if a reviewer believes it is correct to replace them. * docs: typo: "exists and executable" -> "exists and is executable" --------- Co-authored-by: Andrew-Zipperer --- Doc/library/os.rst | 4 ++-- Doc/library/shutil.rst | 4 ++-- Lib/pathlib/__init__.py | 2 +- Lib/pathlib/_abc.py | 2 +- Lib/shutil.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 06ec7da14ebfae..844b5f26d8d4d1 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -926,7 +926,7 @@ as internal buffering of data. If *offset_src* is None, then *src* is read from the current position; respectively for *offset_dst*. - In Linux kernel older than 5.3, the files pointed by *src* and *dst* + In Linux kernel older than 5.3, the files pointed to by *src* and *dst* must reside in the same filesystem, otherwise an :exc:`OSError` is raised with :attr:`~OSError.errno` set to :const:`errno.EXDEV`. @@ -1720,7 +1720,7 @@ or `the MSDN `_ on Windo At least one of the file descriptors must refer to a pipe. If *offset_src* is None, then *src* is read from the current position; respectively for *offset_dst*. The offset associated to the file descriptor that refers to a - pipe must be ``None``. The files pointed by *src* and *dst* must reside in + pipe must be ``None``. The files pointed to by *src* and *dst* must reside in the same filesystem, otherwise an :exc:`OSError` is raised with :attr:`~OSError.errno` set to :const:`errno.EXDEV`. diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 542d2886477792..331acfce3afee3 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -242,7 +242,7 @@ Directory and files operations be copied as far as the platform allows; if false or omitted, the contents and metadata of the linked files are copied to the new tree. - When *symlinks* is false, if the file pointed by the symlink doesn't + When *symlinks* is false, if the file pointed to by the symlink doesn't exist, an exception will be added in the list of errors raised in an :exc:`Error` exception at the end of the copy process. You can set the optional *ignore_dangling_symlinks* flag to true if you @@ -447,7 +447,7 @@ Directory and files operations called. If no *cmd* would be called, return ``None``. *mode* is a permission mask passed to :func:`os.access`, by default - determining if the file exists and executable. + determining if the file exists and is executable. *path* is a "``PATH`` string" specifying the lookup directory list. When no *path* is specified, the results of :func:`os.environ` are used, returning diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index f03f317ef6c16a..8eecf2cefb790f 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -540,7 +540,7 @@ def is_junction(self): def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): """ - Open the file pointed by this path and return a file object, as + Open the file pointed to by this path and return a file object, as the built-in open() function does. """ if "b" not in mode: diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 05698d5de24afb..c7e8e2f68ed058 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -637,7 +637,7 @@ def samefile(self, other_path): def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): """ - Open the file pointed by this path and return a file object, as + Open the file pointed to by this path and return a file object, as the built-in open() function does. """ raise UnsupportedOperation(self._unsupported_msg('open()')) diff --git a/Lib/shutil.py b/Lib/shutil.py index 910d6b6c63ac08..c9b4da34b1e19b 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -556,7 +556,7 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, If the optional symlinks flag is true, symbolic links in the source tree result in symbolic links in the destination tree; if it is false, the contents of the files pointed to by symbolic - links are copied. If the file pointed by the symlink doesn't + links are copied. If the file pointed to by the symlink doesn't exist, an exception will be added in the list of errors raised in an Error exception at the end of the copy process. From f8e088df2a87f613ee23ea4f6787f87d9196b9de Mon Sep 17 00:00:00 2001 From: Andrej Date: Thu, 2 May 2024 13:57:45 +0500 Subject: [PATCH 158/217] gdb/libpython.py: Update PyLongObjectPtr docstring (GH-118438) --- Tools/gdb/libpython.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 74165acd831131..5fdc812a00f059 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -255,7 +255,7 @@ def proxyval(self, visited): Derived classes will override this. - For example, a PyIntObject* with ob_ival 42 in the inferior process + For example, a PyLongObjectPtr* with long_value 42 in the inferior process should result in an int(42) in this process. visited: a set of all gdb.Value pyobject pointers already visited @@ -867,7 +867,7 @@ class PyLongObjectPtr(PyObjectPtr): def proxyval(self, visited): ''' - Python's Include/longobjrep.h has this declaration: + Python's Include/longinterpr.h has this declaration: typedef struct _PyLongValue { uintptr_t lv_tag; /* Number of digits, sign and flags */ @@ -876,14 +876,18 @@ def proxyval(self, visited): struct _longobject { PyObject_HEAD - _PyLongValue long_value; + _PyLongValue long_value; }; with this description: The absolute value of a number is equal to - SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i) - Negative numbers are represented with ob_size < 0; - zero is represented by ob_size == 0. + SUM(for i=0 through ndigits-1) ob_digit[i] * 2**(PyLong_SHIFT*i) + The sign of the value is stored in the lower 2 bits of lv_tag. + - 0: Positive + - 1: Zero + - 2: Negative + The third lowest bit of lv_tag is reserved for an immortality flag, but is + not currently used. where SHIFT can be either: #define PyLong_SHIFT 30 From 67bba9dd0f5b9c2d24c2bc6d239c4502040484af Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 2 May 2024 13:10:31 +0100 Subject: [PATCH 159/217] GH-117442: Check eval-breaker at start (rather than end) of tier 2 loops (GH-118482) --- Include/cpython/optimizer.h | 2 +- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_ids.h | 207 +++++++++++----------- Include/internal/pycore_uop_metadata.h | 8 +- Python/bytecodes.c | 38 ++-- Python/executor_cases.c.h | 11 -- Python/generated_cases.c.h | 16 +- Python/optimizer.c | 22 +-- Python/optimizer_cases.c.h | 4 - Tools/jit/template.c | 1 - 10 files changed, 137 insertions(+), 174 deletions(-) diff --git a/Include/cpython/optimizer.h b/Include/cpython/optimizer.h index 60b35747deb4f3..744a272251e75c 100644 --- a/Include/cpython/optimizer.h +++ b/Include/cpython/optimizer.h @@ -142,7 +142,7 @@ void _Py_BloomFilter_Init(_PyBloomFilter *); void _Py_BloomFilter_Add(_PyBloomFilter *bloom, void *obj); PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj); PyAPI_FUNC(void) _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation); -extern void _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation); +PyAPI_FUNC(void) _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation); /* For testing */ PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewCounter(void); diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 808badea90d8ed..d10224c70f82f5 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1029,7 +1029,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [END_ASYNC_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [END_FOR] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [END_SEND] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, - [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG }, + [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [EXIT_INIT_CHECK] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [EXTENDED_ARG] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [FORMAT_SIMPLE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 0a2231e88488c9..aa3940456b62f2 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -94,47 +94,46 @@ extern "C" { #define _DYNAMIC_EXIT 343 #define _END_SEND END_SEND #define _ERROR_POP_N 344 -#define _EVAL_BREAKER_EXIT 345 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK -#define _FATAL_ERROR 346 +#define _FATAL_ERROR 345 #define _FORMAT_SIMPLE FORMAT_SIMPLE #define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC -#define _FOR_ITER 347 -#define _FOR_ITER_GEN_FRAME 348 -#define _FOR_ITER_TIER_TWO 349 +#define _FOR_ITER 346 +#define _FOR_ITER_GEN_FRAME 347 +#define _FOR_ITER_TIER_TWO 348 #define _GET_AITER GET_AITER #define _GET_ANEXT GET_ANEXT #define _GET_AWAITABLE GET_AWAITABLE #define _GET_ITER GET_ITER #define _GET_LEN GET_LEN #define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER -#define _GUARD_BOTH_FLOAT 350 -#define _GUARD_BOTH_INT 351 -#define _GUARD_BOTH_UNICODE 352 -#define _GUARD_BUILTINS_VERSION 353 -#define _GUARD_DORV_NO_DICT 354 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 355 -#define _GUARD_GLOBALS_VERSION 356 -#define _GUARD_IS_FALSE_POP 357 -#define _GUARD_IS_NONE_POP 358 -#define _GUARD_IS_NOT_NONE_POP 359 -#define _GUARD_IS_TRUE_POP 360 -#define _GUARD_KEYS_VERSION 361 -#define _GUARD_NOS_FLOAT 362 -#define _GUARD_NOS_INT 363 -#define _GUARD_NOT_EXHAUSTED_LIST 364 -#define _GUARD_NOT_EXHAUSTED_RANGE 365 -#define _GUARD_NOT_EXHAUSTED_TUPLE 366 -#define _GUARD_TOS_FLOAT 367 -#define _GUARD_TOS_INT 368 -#define _GUARD_TYPE_VERSION 369 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 370 -#define _INIT_CALL_PY_EXACT_ARGS 371 -#define _INIT_CALL_PY_EXACT_ARGS_0 372 -#define _INIT_CALL_PY_EXACT_ARGS_1 373 -#define _INIT_CALL_PY_EXACT_ARGS_2 374 -#define _INIT_CALL_PY_EXACT_ARGS_3 375 -#define _INIT_CALL_PY_EXACT_ARGS_4 376 +#define _GUARD_BOTH_FLOAT 349 +#define _GUARD_BOTH_INT 350 +#define _GUARD_BOTH_UNICODE 351 +#define _GUARD_BUILTINS_VERSION 352 +#define _GUARD_DORV_NO_DICT 353 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 354 +#define _GUARD_GLOBALS_VERSION 355 +#define _GUARD_IS_FALSE_POP 356 +#define _GUARD_IS_NONE_POP 357 +#define _GUARD_IS_NOT_NONE_POP 358 +#define _GUARD_IS_TRUE_POP 359 +#define _GUARD_KEYS_VERSION 360 +#define _GUARD_NOS_FLOAT 361 +#define _GUARD_NOS_INT 362 +#define _GUARD_NOT_EXHAUSTED_LIST 363 +#define _GUARD_NOT_EXHAUSTED_RANGE 364 +#define _GUARD_NOT_EXHAUSTED_TUPLE 365 +#define _GUARD_TOS_FLOAT 366 +#define _GUARD_TOS_INT 367 +#define _GUARD_TYPE_VERSION 368 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 369 +#define _INIT_CALL_PY_EXACT_ARGS 370 +#define _INIT_CALL_PY_EXACT_ARGS_0 371 +#define _INIT_CALL_PY_EXACT_ARGS_1 372 +#define _INIT_CALL_PY_EXACT_ARGS_2 373 +#define _INIT_CALL_PY_EXACT_ARGS_3 374 +#define _INIT_CALL_PY_EXACT_ARGS_4 375 #define _INSTRUMENTED_CALL INSTRUMENTED_CALL #define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX #define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW @@ -151,65 +150,65 @@ extern "C" { #define _INSTRUMENTED_RETURN_CONST INSTRUMENTED_RETURN_CONST #define _INSTRUMENTED_RETURN_VALUE INSTRUMENTED_RETURN_VALUE #define _INSTRUMENTED_YIELD_VALUE INSTRUMENTED_YIELD_VALUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 377 -#define _IS_NONE 378 +#define _INTERNAL_INCREMENT_OPT_COUNTER 376 +#define _IS_NONE 377 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 379 -#define _ITER_CHECK_RANGE 380 -#define _ITER_CHECK_TUPLE 381 -#define _ITER_JUMP_LIST 382 -#define _ITER_JUMP_RANGE 383 -#define _ITER_JUMP_TUPLE 384 -#define _ITER_NEXT_LIST 385 -#define _ITER_NEXT_RANGE 386 -#define _ITER_NEXT_TUPLE 387 -#define _JUMP_TO_TOP 388 +#define _ITER_CHECK_LIST 378 +#define _ITER_CHECK_RANGE 379 +#define _ITER_CHECK_TUPLE 380 +#define _ITER_JUMP_LIST 381 +#define _ITER_JUMP_RANGE 382 +#define _ITER_JUMP_TUPLE 383 +#define _ITER_NEXT_LIST 384 +#define _ITER_NEXT_RANGE 385 +#define _ITER_NEXT_TUPLE 386 +#define _JUMP_TO_TOP 387 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND #define _LOAD_ASSERTION_ERROR LOAD_ASSERTION_ERROR -#define _LOAD_ATTR 389 -#define _LOAD_ATTR_CLASS 390 -#define _LOAD_ATTR_CLASS_0 391 -#define _LOAD_ATTR_CLASS_1 392 +#define _LOAD_ATTR 388 +#define _LOAD_ATTR_CLASS 389 +#define _LOAD_ATTR_CLASS_0 390 +#define _LOAD_ATTR_CLASS_1 391 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 393 -#define _LOAD_ATTR_INSTANCE_VALUE_0 394 -#define _LOAD_ATTR_INSTANCE_VALUE_1 395 -#define _LOAD_ATTR_METHOD_LAZY_DICT 396 -#define _LOAD_ATTR_METHOD_NO_DICT 397 -#define _LOAD_ATTR_METHOD_WITH_VALUES 398 -#define _LOAD_ATTR_MODULE 399 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 400 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 401 +#define _LOAD_ATTR_INSTANCE_VALUE 392 +#define _LOAD_ATTR_INSTANCE_VALUE_0 393 +#define _LOAD_ATTR_INSTANCE_VALUE_1 394 +#define _LOAD_ATTR_METHOD_LAZY_DICT 395 +#define _LOAD_ATTR_METHOD_NO_DICT 396 +#define _LOAD_ATTR_METHOD_WITH_VALUES 397 +#define _LOAD_ATTR_MODULE 398 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 399 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 400 #define _LOAD_ATTR_PROPERTY LOAD_ATTR_PROPERTY -#define _LOAD_ATTR_SLOT 402 -#define _LOAD_ATTR_SLOT_0 403 -#define _LOAD_ATTR_SLOT_1 404 -#define _LOAD_ATTR_WITH_HINT 405 +#define _LOAD_ATTR_SLOT 401 +#define _LOAD_ATTR_SLOT_0 402 +#define _LOAD_ATTR_SLOT_1 403 +#define _LOAD_ATTR_WITH_HINT 404 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS #define _LOAD_CONST LOAD_CONST -#define _LOAD_CONST_INLINE 406 -#define _LOAD_CONST_INLINE_BORROW 407 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 408 -#define _LOAD_CONST_INLINE_WITH_NULL 409 +#define _LOAD_CONST_INLINE 405 +#define _LOAD_CONST_INLINE_BORROW 406 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 407 +#define _LOAD_CONST_INLINE_WITH_NULL 408 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 410 -#define _LOAD_FAST_0 411 -#define _LOAD_FAST_1 412 -#define _LOAD_FAST_2 413 -#define _LOAD_FAST_3 414 -#define _LOAD_FAST_4 415 -#define _LOAD_FAST_5 416 -#define _LOAD_FAST_6 417 -#define _LOAD_FAST_7 418 +#define _LOAD_FAST 409 +#define _LOAD_FAST_0 410 +#define _LOAD_FAST_1 411 +#define _LOAD_FAST_2 412 +#define _LOAD_FAST_3 413 +#define _LOAD_FAST_4 414 +#define _LOAD_FAST_5 415 +#define _LOAD_FAST_6 416 +#define _LOAD_FAST_7 417 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 419 -#define _LOAD_GLOBAL_BUILTINS 420 -#define _LOAD_GLOBAL_MODULE 421 +#define _LOAD_GLOBAL 418 +#define _LOAD_GLOBAL_BUILTINS 419 +#define _LOAD_GLOBAL_MODULE 420 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR @@ -223,51 +222,51 @@ extern "C" { #define _MATCH_SEQUENCE MATCH_SEQUENCE #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_FRAME 422 -#define _POP_JUMP_IF_FALSE 423 -#define _POP_JUMP_IF_TRUE 424 +#define _POP_FRAME 421 +#define _POP_JUMP_IF_FALSE 422 +#define _POP_JUMP_IF_TRUE 423 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 425 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 424 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 426 +#define _PUSH_FRAME 425 #define _PUSH_NULL PUSH_NULL -#define _REPLACE_WITH_TRUE 427 +#define _REPLACE_WITH_TRUE 426 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR -#define _SAVE_RETURN_OFFSET 428 -#define _SEND 429 +#define _SAVE_RETURN_OFFSET 427 +#define _SEND 428 #define _SEND_GEN SEND_GEN #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _SIDE_EXIT 430 -#define _START_EXECUTOR 431 -#define _STORE_ATTR 432 -#define _STORE_ATTR_INSTANCE_VALUE 433 -#define _STORE_ATTR_SLOT 434 +#define _SIDE_EXIT 429 +#define _START_EXECUTOR 430 +#define _STORE_ATTR 431 +#define _STORE_ATTR_INSTANCE_VALUE 432 +#define _STORE_ATTR_SLOT 433 #define _STORE_ATTR_WITH_HINT STORE_ATTR_WITH_HINT #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 435 -#define _STORE_FAST_0 436 -#define _STORE_FAST_1 437 -#define _STORE_FAST_2 438 -#define _STORE_FAST_3 439 -#define _STORE_FAST_4 440 -#define _STORE_FAST_5 441 -#define _STORE_FAST_6 442 -#define _STORE_FAST_7 443 +#define _STORE_FAST 434 +#define _STORE_FAST_0 435 +#define _STORE_FAST_1 436 +#define _STORE_FAST_2 437 +#define _STORE_FAST_3 438 +#define _STORE_FAST_4 439 +#define _STORE_FAST_5 440 +#define _STORE_FAST_6 441 +#define _STORE_FAST_7 442 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME #define _STORE_SLICE STORE_SLICE -#define _STORE_SUBSCR 444 +#define _STORE_SUBSCR 443 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 445 -#define _TO_BOOL 446 +#define _TIER2_RESUME_CHECK 444 +#define _TO_BOOL 445 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -277,13 +276,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 447 +#define _UNPACK_SEQUENCE 446 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 447 +#define MAX_UOP_ID 446 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 5697b13d1fc51d..d0860024e0db4d 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -233,7 +233,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GUARD_IS_FALSE_POP] = HAS_EXIT_FLAG, [_GUARD_IS_NONE_POP] = HAS_EXIT_FLAG, [_GUARD_IS_NOT_NONE_POP] = HAS_EXIT_FLAG, - [_JUMP_TO_TOP] = HAS_EVAL_BREAK_FLAG, + [_JUMP_TO_TOP] = 0, [_SET_IP] = 0, [_CHECK_STACK_SPACE_OPERAND] = HAS_DEOPT_FLAG, [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG, @@ -254,8 +254,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_DEOPT] = 0, [_SIDE_EXIT] = 0, [_ERROR_POP_N] = HAS_ARG_FLAG, - [_TIER2_RESUME_CHECK] = HAS_EXIT_FLAG, - [_EVAL_BREAKER_EXIT] = HAS_ESCAPES_FLAG, + [_TIER2_RESUME_CHECK] = HAS_DEOPT_FLAG, }; const uint8_t _PyUop_Replication[MAX_UOP_ID+1] = { @@ -339,7 +338,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_DYNAMIC_EXIT] = "_DYNAMIC_EXIT", [_END_SEND] = "_END_SEND", [_ERROR_POP_N] = "_ERROR_POP_N", - [_EVAL_BREAKER_EXIT] = "_EVAL_BREAKER_EXIT", [_EXIT_INIT_CHECK] = "_EXIT_INIT_CHECK", [_EXIT_TRACE] = "_EXIT_TRACE", [_FATAL_ERROR] = "_FATAL_ERROR", @@ -978,8 +976,6 @@ int _PyUop_num_popped(int opcode, int oparg) return oparg; case _TIER2_RESUME_CHECK: return 0; - case _EVAL_BREAKER_EXIT: - return 0; default: return -1; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 002b5a3529c127..b1be40dd38ce21 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2410,20 +2410,21 @@ dummy_func( tier1 inst(ENTER_EXECUTOR, (--)) { #ifdef _Py_TIER2 - int prevoparg = oparg; - CHECK_EVAL_BREAKER(); - if (this_instr->op.code != ENTER_EXECUTOR || - this_instr->op.arg != prevoparg) { - next_instr = this_instr; - DISPATCH(); - } - PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; assert(executor->vm_data.index == INSTR_OFFSET() - 1); assert(executor->vm_data.code == code); assert(executor->vm_data.valid); assert(tstate->previous_executor == NULL); + /* If the eval breaker is set then stay in tier 1. + * This avoids any potentially infinite loops + * involving _RESUME_CHECK */ + if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { + opcode = executor->vm_data.opcode; + oparg = (oparg & ~255) | executor->vm_data.oparg; + next_instr = this_instr; + DISPATCH_GOTO(); + } tstate->previous_executor = Py_None; Py_INCREF(executor); GOTO_TIER_TWO(executor); @@ -4111,7 +4112,6 @@ dummy_func( #ifndef _Py_JIT next_uop = ¤t_executor->trace[1]; #endif - CHECK_EVAL_BREAKER(); } tier2 op(_SET_IP, (instr_ptr/4 --)) { @@ -4277,30 +4277,18 @@ dummy_func( GOTO_UNWIND(); } - - /* Special version of RESUME_CHECK that (when paired with _EVAL_BREAKER_EXIT) - * is safe for tier 2. Progress is guaranteed because _EVAL_BREAKER_EXIT calls - * _Py_HandlePending which clears the eval_breaker so that _TIER2_RESUME_CHECK - * will not exit if it is immediately executed again. */ + /* Progress is guaranteed if we DEOPT on the eval breaker, because + * ENTER_EXECUTOR will not re-enter tier 2 with the eval breaker set. */ tier2 op(_TIER2_RESUME_CHECK, (--)) { #if defined(__EMSCRIPTEN__) - EXIT_IF(_Py_emscripten_signal_clock == 0); + DEOPT_IF(_Py_emscripten_signal_clock == 0); _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; #endif uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); - EXIT_IF(eval_breaker & _PY_EVAL_EVENTS_MASK); + DEOPT_IF(eval_breaker & _PY_EVAL_EVENTS_MASK); assert(eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); } - tier2 op(_EVAL_BREAKER_EXIT, (--)) { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_HandlePending(tstate) != 0) { - GOTO_UNWIND(); - } - EXIT_TO_TRACE(); - } - // END BYTECODES // } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index b17f3762714c72..f97f279836eb66 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4094,7 +4094,6 @@ #ifndef _Py_JIT next_uop = ¤t_executor->trace[1]; #endif - CHECK_EVAL_BREAKER(); break; } @@ -4353,14 +4352,4 @@ break; } - case _EVAL_BREAKER_EXIT: { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_HandlePending(tstate) != 0) { - GOTO_UNWIND(); - } - EXIT_TO_TRACE(); - break; - } - #undef TIER_TWO diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 32b485ecb9d8cc..2a0f268ce6ed54 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2493,19 +2493,21 @@ next_instr += 1; INSTRUCTION_STATS(ENTER_EXECUTOR); #ifdef _Py_TIER2 - int prevoparg = oparg; - CHECK_EVAL_BREAKER(); - if (this_instr->op.code != ENTER_EXECUTOR || - this_instr->op.arg != prevoparg) { - next_instr = this_instr; - DISPATCH(); - } PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; assert(executor->vm_data.index == INSTR_OFFSET() - 1); assert(executor->vm_data.code == code); assert(executor->vm_data.valid); assert(tstate->previous_executor == NULL); + /* If the eval breaker is set then stay in tier 1. + * This avoids any potentially infinite loops + * involving _RESUME_CHECK */ + if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { + opcode = executor->vm_data.opcode; + oparg = (oparg & ~255) | executor->vm_data.oparg; + next_instr = this_instr; + DISPATCH_GOTO(); + } tstate->previous_executor = Py_None; Py_INCREF(executor); GOTO_TIER_TWO(executor); diff --git a/Python/optimizer.c b/Python/optimizer.c index 2389338531b0f3..050947396d1724 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -627,6 +627,9 @@ translate_bytecode_to_trace( if (opcode == JUMP_BACKWARD || opcode == JUMP_BACKWARD_NO_INTERRUPT) { instr += 1 + _PyOpcode_Caches[opcode] - (int32_t)oparg; initial_instr = instr; + if (opcode == JUMP_BACKWARD) { + ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target); + } continue; } else { @@ -976,6 +979,7 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) int32_t current_error = -1; int32_t current_error_target = -1; int32_t current_popped = -1; + int32_t current_exit_op = -1; /* Leaving in NOPs slows down the interpreter and messes up the stats */ _PyUOpInstruction *copy_to = &buffer[0]; for (int i = 0; i < length; i++) { @@ -994,20 +998,11 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) int opcode = inst->opcode; int32_t target = (int32_t)uop_get_target(inst); if (_PyUop_Flags[opcode] & (HAS_EXIT_FLAG | HAS_DEOPT_FLAG)) { - if (target != current_jump_target) { - uint16_t exit_op; - if (_PyUop_Flags[opcode] & HAS_EXIT_FLAG) { - if (opcode == _TIER2_RESUME_CHECK) { - exit_op = _EVAL_BREAKER_EXIT; - } - else { - exit_op = _SIDE_EXIT; - } - } - else { - exit_op = _DEOPT; - } + uint16_t exit_op = (_PyUop_Flags[opcode] & HAS_EXIT_FLAG) ? + _SIDE_EXIT : _DEOPT; + if (target != current_jump_target || current_exit_op != exit_op) { make_exit(&buffer[next_spare], exit_op, target); + current_exit_op = exit_op; current_jump_target = target; current_jump = next_spare; next_spare++; @@ -1114,7 +1109,6 @@ sanity_check(_PyExecutorObject *executor) CHECK( opcode == _DEOPT || opcode == _SIDE_EXIT || - opcode == _EVAL_BREAKER_EXIT || opcode == _ERROR_POP_N); if (opcode == _SIDE_EXIT) { CHECK(inst->format == UOP_FORMAT_EXIT); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 0b696921ebfc9f..e680d76141776f 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2153,7 +2153,3 @@ break; } - case _EVAL_BREAKER_EXIT: { - break; - } - diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 0dd0744f7aec9c..a81e866e9da4b3 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -106,7 +106,6 @@ _JIT_ENTRY(_PyInterpreterFrame *frame, PyObject **stack_pointer, PyThreadState * // The actual instruction definitions (only one will be used): if (uopcode == _JUMP_TO_TOP) { - CHECK_EVAL_BREAKER(); PATCH_JUMP(_JIT_TOP); } switch (uopcode) { From 7c97dc8c9594c71bd3d1f69758a27de45f57e4c3 Mon Sep 17 00:00:00 2001 From: Crowthebird <78076854+thatbirdguythatuknownot@users.noreply.github.com> Date: Thu, 2 May 2024 21:32:20 +0800 Subject: [PATCH 160/217] gh-118216: Don't consider dotted `__future__` imports (#118267) --- Doc/whatsnew/3.13.rst | 4 ++++ Lib/test/test_future_stmt/test_future.py | 19 +++++++++++++++++++ ...-04-25-11-48-28.gh-issue-118216.SVg700.rst | 1 + Python/compile.c | 2 +- Python/future.c | 2 +- 5 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-25-11-48-28.gh-issue-118216.SVg700.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 52fb9752cf4132..3ccf17be9796d5 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -288,6 +288,10 @@ Other Language Changes class scopes are not inlined into their parent scope. (Contributed by Jelle Zijlstra in :gh:`109118` and :gh:`118160`.) +* ``from __future__ import ...`` statements are now just normal + relative imports if dots are present before the module name. + (Contributed by Jeremiah Gabriel Pascual in :gh:`118216`.) + New Modules =========== diff --git a/Lib/test/test_future_stmt/test_future.py b/Lib/test/test_future_stmt/test_future.py index 2c8ceb664cb362..69ae58b0fbcae3 100644 --- a/Lib/test/test_future_stmt/test_future.py +++ b/Lib/test/test_future_stmt/test_future.py @@ -203,6 +203,25 @@ def test_syntactical_future_repl(self): out = kill_python(p) self.assertNotIn(b'SyntaxError: invalid syntax', out) + def test_future_dotted_import(self): + with self.assertRaises(ImportError): + exec("from .__future__ import spam") + + code = dedent( + """ + from __future__ import print_function + from ...__future__ import ham + """ + ) + with self.assertRaises(ImportError): + exec(code) + + code = """ + from .__future__ import nested_scopes + from __future__ import barry_as_FLUFL + """ + self.assertSyntaxError(code, lineno=2) + class AnnotationsFutureTestCase(unittest.TestCase): template = dedent( """ diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-25-11-48-28.gh-issue-118216.SVg700.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-25-11-48-28.gh-issue-118216.SVg700.rst new file mode 100644 index 00000000000000..937cdffefda6f1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-25-11-48-28.gh-issue-118216.SVg700.rst @@ -0,0 +1 @@ +Don't consider :mod:`__future__` imports with dots before the module name. diff --git a/Python/compile.c b/Python/compile.c index 4e94f9297a32d1..feedd988834397 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3849,7 +3849,7 @@ compiler_from_import(struct compiler *c, stmt_ty s) } if (location_is_after(LOC(s), c->c_future.ff_location) && - s->v.ImportFrom.module && + s->v.ImportFrom.module && s->v.ImportFrom.level == 0 && _PyUnicode_EqualToASCIIString(s->v.ImportFrom.module, "__future__")) { Py_DECREF(names); diff --git a/Python/future.c b/Python/future.c index 399345bd8fcbd9..8d94d515605dcd 100644 --- a/Python/future.c +++ b/Python/future.c @@ -77,7 +77,7 @@ future_parse(_PyFutureFeatures *ff, mod_ty mod, PyObject *filename) * are another future statement and a doc string. */ - if (s->kind == ImportFrom_kind) { + if (s->kind == ImportFrom_kind && s->v.ImportFrom.level == 0) { identifier modname = s->v.ImportFrom.module; if (modname && _PyUnicode_EqualToASCIIString(modname, "__future__")) { From 7d2ffada0a6490e6839697f729bcd80380e9f561 Mon Sep 17 00:00:00 2001 From: NGRsoftlab <78017794+NGRsoftlab@users.noreply.github.com> Date: Thu, 2 May 2024 16:43:03 +0300 Subject: [PATCH 161/217] gh-116180: Check the globals argument in PyRun_* C API (GH-116637) It used to crash when passing NULL or non-dict as globals. Now it sets a SystemError. --- Lib/test/test_capi/test_run.py | 36 ++++++++++++++++++++++------------ Python/pythonrun.c | 21 +++++++++++--------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/Lib/test/test_capi/test_run.py b/Lib/test/test_capi/test_run.py index 3a390273ec21ad..894f66b437a39c 100644 --- a/Lib/test/test_capi/test_run.py +++ b/Lib/test/test_capi/test_run.py @@ -11,6 +11,10 @@ Py_eval_input = _testcapi.Py_eval_input +class DictSubclass(dict): + pass + + class CAPITest(unittest.TestCase): # TODO: Test the following functions: # @@ -50,15 +54,19 @@ def run(s, *args): self.assertRaises(TypeError, run, b'a\n', dict(a=1), []) self.assertRaises(TypeError, run, b'a\n', dict(a=1), 1) + self.assertIsNone(run(b'a\n', DictSubclass(a=1))) + self.assertIsNone(run(b'a\n', DictSubclass(), dict(a=1))) + self.assertRaises(NameError, run, b'a\n', DictSubclass()) + self.assertIsNone(run(b'\xc3\xa4\n', {'\xe4': 1})) self.assertRaises(SyntaxError, run, b'\xe4\n', {}) - # CRASHES run(b'a\n', NULL) - # CRASHES run(b'a\n', NULL, {}) - # CRASHES run(b'a\n', NULL, dict(a=1)) - # CRASHES run(b'a\n', UserDict()) - # CRASHES run(b'a\n', UserDict(), {}) - # CRASHES run(b'a\n', UserDict(), dict(a=1)) + self.assertRaises(SystemError, run, b'a\n', NULL) + self.assertRaises(SystemError, run, b'a\n', NULL, {}) + self.assertRaises(SystemError, run, b'a\n', NULL, dict(a=1)) + self.assertRaises(SystemError, run, b'a\n', UserDict()) + self.assertRaises(SystemError, run, b'a\n', UserDict(), {}) + self.assertRaises(SystemError, run, b'a\n', UserDict(), dict(a=1)) # CRASHES run(NULL, {}) @@ -82,12 +90,16 @@ def run(*args): self.assertRaises(TypeError, run, dict(a=1), []) self.assertRaises(TypeError, run, dict(a=1), 1) - # CRASHES run(NULL) - # CRASHES run(NULL, {}) - # CRASHES run(NULL, dict(a=1)) - # CRASHES run(UserDict()) - # CRASHES run(UserDict(), {}) - # CRASHES run(UserDict(), dict(a=1)) + self.assertIsNone(run(DictSubclass(a=1))) + self.assertIsNone(run(DictSubclass(), dict(a=1))) + self.assertRaises(NameError, run, DictSubclass()) + + self.assertRaises(SystemError, run, NULL) + self.assertRaises(SystemError, run, NULL, {}) + self.assertRaises(SystemError, run, NULL, dict(a=1)) + self.assertRaises(SystemError, run, UserDict()) + self.assertRaises(SystemError, run, UserDict(), {}) + self.assertRaises(SystemError, run, UserDict(), dict(a=1)) @unittest.skipUnless(TESTFN_UNDECODABLE, 'only works if there are undecodable paths') @unittest.skipIf(os.name == 'nt', 'does not work on Windows') diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 2970248da13705..31213aec3cd9c3 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1275,17 +1275,20 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py _PyRuntime.signals.unhandled_keyboard_interrupt = 0; /* Set globals['__builtins__'] if it doesn't exist */ - if (globals != NULL) { - int has_builtins = PyDict_ContainsString(globals, "__builtins__"); - if (has_builtins < 0) { + if (!globals || !PyDict_Check(globals)) { + PyErr_SetString(PyExc_SystemError, "globals must be a real dict"); + return NULL; + } + int has_builtins = PyDict_ContainsString(globals, "__builtins__"); + if (has_builtins < 0) { + return NULL; + } + if (!has_builtins) { + if (PyDict_SetItemString(globals, "__builtins__", + tstate->interp->builtins) < 0) + { return NULL; } - if (!has_builtins) { - if (PyDict_SetItemString(globals, "__builtins__", - tstate->interp->builtins) < 0) { - return NULL; - } - } } v = PyEval_EvalCode((PyObject*)co, globals, locals); From b3372481b6cae5766330b041c4622c28cee2119f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 2 May 2024 16:56:33 +0300 Subject: [PATCH 162/217] gh-117903: Clarify that the staticmethod descriptor is callable (GH-117925) --- Doc/library/functions.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index e598ef423de497..be3a64cf41b425 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1733,8 +1733,9 @@ are always available. They are listed here in alphabetical order. :ref:`function` for details. A static method can be called either on the class (such as ``C.f()``) or on - an instance (such as ``C().f()``). Moreover, they can be called as regular - functions (such as ``f()``). + an instance (such as ``C().f()``). + Moreover, the static method :term:`descriptor` is also callable, so it can + be used in the class definition (such as ``f()``). Static methods in Python are similar to those found in Java or C++. Also, see :func:`classmethod` for a variant that is useful for creating alternate class From 81939dad77001556c527485d31a2d0f4a759033e Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 2 May 2024 15:20:43 +0100 Subject: [PATCH 163/217] gh-118486: Support mkdir(mode=0o700) on Windows (GH-118488) --- Doc/library/os.rst | 7 + Lib/test/test_os.py | 19 +++ Lib/test/test_tempfile.py | 28 ++++ ...-05-01-20-57-09.gh-issue-118486.K44KJG.rst | 2 + Modules/posixmodule.c | 158 +++++++++++++++++- 5 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-05-01-20-57-09.gh-issue-118486.K44KJG.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 844b5f26d8d4d1..6c92eed9c063f1 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2430,6 +2430,10 @@ features: platform-dependent. On some platforms, they are ignored and you should call :func:`chmod` explicitly to set them. + On Windows, a *mode* of ``0o700`` is specifically handled to apply access + control to the new directory such that only the current user and + administrators have access. Other values of *mode* are ignored. + This function can also support :ref:`paths relative to directory descriptors `. @@ -2444,6 +2448,9 @@ features: .. versionchanged:: 3.6 Accepts a :term:`path-like object`. + .. versionchanged:: 3.13 + Windows now handles a *mode* of ``0o700``. + .. function:: makedirs(name, mode=0o777, exist_ok=False) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index eaa676673f8af0..9c9c8536dc7542 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1811,6 +1811,25 @@ def test_exist_ok_existing_regular_file(self): self.assertRaises(OSError, os.makedirs, path, exist_ok=True) os.remove(path) + @unittest.skipUnless(os.name == 'nt', "requires Windows") + def test_win32_mkdir_700(self): + base = os_helper.TESTFN + path1 = os.path.join(os_helper.TESTFN, 'dir1') + path2 = os.path.join(os_helper.TESTFN, 'dir2') + # mode=0o700 is special-cased to override ACLs on Windows + # There's no way to know exactly how the ACLs will look, so we'll + # check that they are different from a regularly created directory. + os.mkdir(path1, mode=0o700) + os.mkdir(path2, mode=0o777) + + out1 = subprocess.check_output(["icacls.exe", path1], encoding="oem") + out2 = subprocess.check_output(["icacls.exe", path2], encoding="oem") + os.rmdir(path1) + os.rmdir(path2) + out1 = out1.replace(path1, "") + out2 = out2.replace(path2, "") + self.assertNotEqual(out1, out2) + def tearDown(self): path = os.path.join(os_helper.TESTFN, 'dir1', 'dir2', 'dir3', 'dir4', 'dir5', 'dir6') diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index b64b6a4f2baeb5..19ddeaa169bf93 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -13,6 +13,7 @@ import weakref import gc import shutil +import subprocess from unittest import mock import unittest @@ -803,6 +804,33 @@ def test_mode(self): finally: os.rmdir(dir) + @unittest.skipUnless(os.name == "nt", "Only on Windows.") + def test_mode_win32(self): + # Use icacls.exe to extract the users with some level of access + # Main thing we are testing is that the BUILTIN\Users group has + # no access. The exact ACL is going to vary based on which user + # is running the test. + dir = self.do_create() + try: + out = subprocess.check_output(["icacls.exe", dir], encoding="oem").casefold() + finally: + os.rmdir(dir) + + dir = dir.casefold() + users = set() + found_user = False + for line in out.strip().splitlines(): + acl = None + # First line of result includes our directory + if line.startswith(dir): + acl = line.removeprefix(dir).strip() + elif line and line[:1].isspace(): + acl = line.strip() + if acl: + users.add(acl.partition(":")[0]) + + self.assertNotIn(r"BUILTIN\Users".casefold(), users) + def test_collision_with_existing_file(self): # mkdtemp tries another name when a file with # the chosen name already exists diff --git a/Misc/NEWS.d/next/Windows/2024-05-01-20-57-09.gh-issue-118486.K44KJG.rst b/Misc/NEWS.d/next/Windows/2024-05-01-20-57-09.gh-issue-118486.K44KJG.rst new file mode 100644 index 00000000000000..cdbce9a0bebf6b --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-05-01-20-57-09.gh-issue-118486.K44KJG.rst @@ -0,0 +1,2 @@ +:func:`os.mkdir` now accepts *mode* of ``0o700`` to restrict the new +directory to the current user. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 722159a39d098f..f9533577a8fa34 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -37,6 +37,8 @@ # include # include // UNLEN # include "osdefs.h" // SEP +# include // SetEntriesInAcl +# include // SDDL_REVISION_1 # if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) # define HAVE_SYMLINK # endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ @@ -5539,6 +5541,133 @@ os__path_normpath_impl(PyObject *module, PyObject *path) return result; } +#ifdef MS_WINDOWS + +/* We centralise SECURITY_ATTRIBUTE initialization based around +templates that will probably mostly match common POSIX mode settings. +The _Py_SECURITY_ATTRIBUTE_DATA structure contains temporary data, as +a constructed SECURITY_ATTRIBUTE structure typically refers to memory +that has to be alive while it's being used. + +Typical use will look like: + SECURITY_ATTRIBUTES *pSecAttr = NULL; + struct _Py_SECURITY_ATTRIBUTE_DATA secAttrData; + int error, error2; + + Py_BEGIN_ALLOW_THREADS + switch (mode) { + case 0x1C0: // 0o700 + error = initializeMkdir700SecurityAttributes(&pSecAttr, &secAttrData); + break; + ... + default: + error = initializeDefaultSecurityAttributes(&pSecAttr, &secAttrData); + break; + } + + if (!error) { + // do operation, passing pSecAttr + } + + // Unconditionally clear secAttrData. + error2 = clearSecurityAttributes(&pSecAttr, &secAttrData); + if (!error) { + error = error2; + } + Py_END_ALLOW_THREADS + + if (error) { + PyErr_SetFromWindowsErr(error); + return NULL; + } +*/ + +struct _Py_SECURITY_ATTRIBUTE_DATA { + SECURITY_ATTRIBUTES securityAttributes; + PACL acl; + SECURITY_DESCRIPTOR sd; + EXPLICIT_ACCESS_W ea[4]; +}; + +static int +initializeDefaultSecurityAttributes( + PSECURITY_ATTRIBUTES *securityAttributes, + struct _Py_SECURITY_ATTRIBUTE_DATA *data +) { + assert(securityAttributes); + assert(data); + *securityAttributes = NULL; + memset(data, 0, sizeof(*data)); + return 0; +} + +static int +initializeMkdir700SecurityAttributes( + PSECURITY_ATTRIBUTES *securityAttributes, + struct _Py_SECURITY_ATTRIBUTE_DATA *data +) { + assert(securityAttributes); + assert(data); + *securityAttributes = NULL; + memset(data, 0, sizeof(*data)); + + if (!InitializeSecurityDescriptor(&data->sd, SECURITY_DESCRIPTOR_REVISION) + || !SetSecurityDescriptorGroup(&data->sd, NULL, TRUE)) { + return GetLastError(); + } + + data->securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + data->ea[0].grfAccessPermissions = GENERIC_ALL; + data->ea[0].grfAccessMode = SET_ACCESS; + data->ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + data->ea[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME; + data->ea[0].Trustee.TrusteeType = TRUSTEE_IS_ALIAS; + data->ea[0].Trustee.ptstrName = L"CURRENT_USER"; + + data->ea[1].grfAccessPermissions = GENERIC_ALL; + data->ea[1].grfAccessMode = SET_ACCESS; + data->ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + data->ea[1].Trustee.TrusteeForm = TRUSTEE_IS_NAME; + data->ea[1].Trustee.TrusteeType = TRUSTEE_IS_ALIAS; + data->ea[1].Trustee.ptstrName = L"SYSTEM"; + + data->ea[2].grfAccessPermissions = GENERIC_ALL; + data->ea[2].grfAccessMode = SET_ACCESS; + data->ea[2].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + data->ea[2].Trustee.TrusteeForm = TRUSTEE_IS_NAME; + data->ea[2].Trustee.TrusteeType = TRUSTEE_IS_ALIAS; + data->ea[2].Trustee.ptstrName = L"ADMINISTRATORS"; + + int r = SetEntriesInAclW(3, data->ea, NULL, &data->acl); + if (r) { + return r; + } + if (!SetSecurityDescriptorDacl(&data->sd, TRUE, data->acl, FALSE)) { + return GetLastError(); + } + data->securityAttributes.lpSecurityDescriptor = &data->sd; + *securityAttributes = &data->securityAttributes; + return 0; +} + +static int +clearSecurityAttributes( + PSECURITY_ATTRIBUTES *securityAttributes, + struct _Py_SECURITY_ATTRIBUTE_DATA *data +) { + assert(securityAttributes); + assert(data); + *securityAttributes = NULL; + if (data->acl) { + if (LocalFree((void *)data->acl)) { + return GetLastError(); + } + } + return 0; +} + +#endif + /*[clinic input] os.mkdir @@ -5568,6 +5697,12 @@ os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) /*[clinic end generated code: output=a70446903abe821f input=a61722e1576fab03]*/ { int result; +#ifdef MS_WINDOWS + int error = 0; + int pathError = 0; + SECURITY_ATTRIBUTES *pSecAttr = NULL; + struct _Py_SECURITY_ATTRIBUTE_DATA secAttrData; +#endif #ifdef HAVE_MKDIRAT int mkdirat_unavailable = 0; #endif @@ -5579,11 +5714,30 @@ os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) #ifdef MS_WINDOWS Py_BEGIN_ALLOW_THREADS - result = CreateDirectoryW(path->wide, NULL); + switch (mode) { + case 0x1C0: // 0o700 + error = initializeMkdir700SecurityAttributes(&pSecAttr, &secAttrData); + break; + default: + error = initializeDefaultSecurityAttributes(&pSecAttr, &secAttrData); + break; + } + if (!error) { + result = CreateDirectoryW(path->wide, pSecAttr); + error = clearSecurityAttributes(&pSecAttr, &secAttrData); + } else { + // Ignore error from "clear" - we have a more interesting one already + clearSecurityAttributes(&pSecAttr, &secAttrData); + } Py_END_ALLOW_THREADS - if (!result) + if (error) { + PyErr_SetFromWindowsErr(error); + return NULL; + } + if (!result) { return path_error(path); + } #else Py_BEGIN_ALLOW_THREADS #if HAVE_MKDIRAT From 9789440de387219bb7677fe0d66860aa8c9deb02 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 2 May 2024 17:44:33 +0300 Subject: [PATCH 164/217] gh-82062: Fix support of parameter defaults on methods in extension modules (GH-115270) Now inspect.signature() supports references to the module globals in parameter defaults on methods in extension modules. Previously it was only supported in functions. The workaround was to specify the fully qualified name, including the module name. --- Lib/inspect.py | 5 +++++ Lib/test/test_inspect/test_inspect.py | 7 +++++++ .../2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst | 3 +++ Modules/_testcapi/docstring.c | 10 ++++++++++ 4 files changed, 25 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index d46514f4b10467..a0c80bd5c8b601 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2285,7 +2285,12 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True): module = None module_dict = {} + module_name = getattr(obj, '__module__', None) + if not module_name: + objclass = getattr(obj, '__objclass__', None) + module_name = getattr(objclass, '__module__', None) + if module_name: module = sys.modules.get(module_name, None) if module: diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index fbef34eddacb3a..d12240353ff832 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -3069,6 +3069,13 @@ def test_signature_on_builtins_no_signature(self): self.assertEqual(inspect.signature(builtin), inspect.signature(template)) + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_signature_parsing_with_defaults(self): + _testcapi = import_helper.import_module("_testcapi") + meth = _testcapi.DocStringUnrepresentableSignatureTest.with_default + self.assertEqual(str(inspect.signature(meth)), '(self, /, x=1)') + def test_signature_on_non_function(self): with self.assertRaisesRegex(TypeError, 'is not a callable object'): inspect.signature(42) diff --git a/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst new file mode 100644 index 00000000000000..a57a5918b135bb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst @@ -0,0 +1,3 @@ +Fix :func:`inspect.signature()` to correctly handle parameter defaults +on methods in extension modules that use names defined in the module +namespace. diff --git a/Modules/_testcapi/docstring.c b/Modules/_testcapi/docstring.c index d99fbdd904b594..3f7acbae1b181b 100644 --- a/Modules/_testcapi/docstring.c +++ b/Modules/_testcapi/docstring.c @@ -169,6 +169,13 @@ static PyMethodDef DocStringUnrepresentableSignatureTest_methods[] = { "--\n\n" "This docstring has a signature with unrepresentable default." )}, + {"with_default", + (PyCFunction)test_with_docstring, METH_VARARGS, + PyDoc_STR( + "with_default($self, /, x=ONE)\n" + "--\n\n" + "This instance method has a default parameter value from the module scope." + )}, {NULL}, }; @@ -193,5 +200,8 @@ _PyTestCapi_Init_Docstring(PyObject *mod) if (PyModule_AddType(mod, &DocStringUnrepresentableSignatureTest) < 0) { return -1; } + if (PyModule_AddObject(mod, "ONE", PyLong_FromLong(1)) < 0) { + return -1; + } return 0; } From 72867c962cc59c6d56805f86530696bea6beb039 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 2 May 2024 16:17:59 +0100 Subject: [PATCH 165/217] GH-118095: Unify the behavior of tier 2 FOR_ITER branch micro-ops (GH-118420) * Target _FOR_ITER_TIER_TWO at POP_TOP following the matching END_FOR * Modify _GUARD_NOT_EXHAUSTED_RANGE, _GUARD_NOT_EXHAUSTED_LIST and _GUARD_NOT_EXHAUSTED_TUPLE so that they also target the POP_TOP following the matching END_FOR --- Python/bytecodes.c | 4 +-- Python/executor_cases.c.h | 4 +-- Python/optimizer.c | 50 ++++++++++++++++++++++++++++++-------- Python/optimizer_symbols.c | 13 +++++++--- Python/specialize.c | 6 +++-- 5 files changed, 56 insertions(+), 21 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b1be40dd38ce21..9769cfe68aaeba 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2610,9 +2610,7 @@ dummy_func( _PyErr_Clear(tstate); } /* iterator ended normally */ - Py_DECREF(iter); - STACK_SHRINK(1); - /* The translator sets the deopt target just past END_FOR */ + /* The translator sets the deopt target just past the matching END_FOR */ DEOPT_IF(true); } // Common case: no jump, leave it to the code generator diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index f97f279836eb66..03db9b623cbd86 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2664,9 +2664,7 @@ _PyErr_Clear(tstate); } /* iterator ended normally */ - Py_DECREF(iter); - STACK_SHRINK(1); - /* The translator sets the deopt target just past END_FOR */ + /* The translator sets the deopt target just past the matching END_FOR */ if (true) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); diff --git a/Python/optimizer.c b/Python/optimizer.c index 050947396d1724..56768ae8f542f6 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -23,6 +23,19 @@ #define MAX_EXECUTORS_SIZE 256 +#ifdef Py_DEBUG +static int +base_opcode(PyCodeObject *code, int offset) +{ + int opcode = _Py_GetBaseOpcode(code, offset); + if (opcode == ENTER_EXECUTOR) { + int oparg = _PyCode_CODE(code)[offset].op.arg; + _PyExecutorObject *ex = code->co_executors->executors[oparg]; + return ex->vm_data.opcode; + } + return opcode; +} +#endif static bool has_space_for_executor(PyCodeObject *code, _Py_CODEUNIT *instr) @@ -445,6 +458,14 @@ _PyUOp_Replacements[MAX_UOP_ID + 1] = { [_FOR_ITER] = _FOR_ITER_TIER_TWO, }; +static const uint8_t +is_for_iter_test[MAX_UOP_ID + 1] = { + [_GUARD_NOT_EXHAUSTED_RANGE] = 1, + [_GUARD_NOT_EXHAUSTED_LIST] = 1, + [_GUARD_NOT_EXHAUSTED_TUPLE] = 1, + [_FOR_ITER_TIER_TWO] = 1, +}; + static const uint16_t BRANCH_TO_GUARD[4][2] = { [POP_JUMP_IF_FALSE - POP_JUMP_IF_FALSE][0] = _GUARD_IS_TRUE_POP, @@ -594,7 +615,6 @@ translate_bytecode_to_trace( uint32_t opcode = instr->op.code; uint32_t oparg = instr->op.arg; - uint32_t extended = 0; DPRINTF(2, "%d: %s(%d)\n", target, _PyOpcode_OpName[opcode], oparg); @@ -608,7 +628,6 @@ translate_bytecode_to_trace( if (opcode == EXTENDED_ARG) { instr++; - extended = 1; opcode = instr->op.code; oparg = (oparg << 8) | instr->op.arg; if (opcode == EXTENDED_ARG) { @@ -772,12 +791,15 @@ translate_bytecode_to_trace( case OPARG_REPLACED: uop = _PyUOp_Replacements[uop]; assert(uop != 0); - if (uop == _FOR_ITER_TIER_TWO) { - target += 1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 2 + extended; - assert(_PyCode_CODE(code)[target-2].op.code == END_FOR || - _PyCode_CODE(code)[target-2].op.code == INSTRUMENTED_END_FOR); - assert(_PyCode_CODE(code)[target-1].op.code == POP_TOP); +#ifdef Py_DEBUG + { + uint32_t next_inst = target + 1 + INLINE_CACHE_ENTRIES_FOR_ITER + (oparg > 255); + uint32_t jump_target = next_inst + oparg; + assert(base_opcode(code, jump_target) == END_FOR || + base_opcode(code, jump_target) == INSTRUMENTED_END_FOR); + assert(base_opcode(code, jump_target+1) == POP_TOP); } +#endif break; default: fprintf(stderr, @@ -1000,10 +1022,18 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) if (_PyUop_Flags[opcode] & (HAS_EXIT_FLAG | HAS_DEOPT_FLAG)) { uint16_t exit_op = (_PyUop_Flags[opcode] & HAS_EXIT_FLAG) ? _SIDE_EXIT : _DEOPT; - if (target != current_jump_target || current_exit_op != exit_op) { - make_exit(&buffer[next_spare], exit_op, target); + int32_t jump_target = target; + if (is_for_iter_test[opcode]) { + /* Target the POP_TOP immediately after the END_FOR, + * leaving only the iterator on the stack. */ + int extended_arg = inst->oparg > 255; + int32_t next_inst = target + 1 + INLINE_CACHE_ENTRIES_FOR_ITER + extended_arg; + jump_target = next_inst + inst->oparg + 1; + } + if (jump_target != current_jump_target || current_exit_op != exit_op) { + make_exit(&buffer[next_spare], exit_op, jump_target); current_exit_op = exit_op; - current_jump_target = target; + current_jump_target = jump_target; current_jump = next_spare; next_spare++; } diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c index d52f490853c006..4aeb04fe0405d2 100644 --- a/Python/optimizer_symbols.c +++ b/Python/optimizer_symbols.c @@ -164,19 +164,26 @@ _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val) return true; } - bool _Py_uop_sym_set_null(_Py_UopsSymbol *sym) { + if (_Py_uop_sym_is_not_null(sym)) { + sym_set_bottom(sym); + return false; + } sym_set_flag(sym, IS_NULL); - return !_Py_uop_sym_is_bottom(sym); + return true; } bool _Py_uop_sym_set_non_null(_Py_UopsSymbol *sym) { + if (_Py_uop_sym_is_null(sym)) { + sym_set_bottom(sym); + return false; + } sym_set_flag(sym, NOT_NULL); - return !_Py_uop_sym_is_bottom(sym); + return true; } diff --git a/Python/specialize.c b/Python/specialize.c index ee51781372166a..72114f27f69c52 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -215,6 +215,7 @@ print_gc_stats(FILE *out, GCStats *stats) } } +#ifdef _Py_TIER2 static void print_histogram(FILE *out, const char *name, uint64_t hist[_Py_UOP_HIST_SIZE]) { @@ -249,7 +250,6 @@ print_optimization_stats(FILE *out, OptimizationStats *stats) stats->optimizer_failure_reason_no_memory); fprintf(out, "Optimizer remove globals builtins changed: %" PRIu64 "\n", stats->remove_globals_builtins_changed); fprintf(out, "Optimizer remove globals incorrect keys: %" PRIu64 "\n", stats->remove_globals_incorrect_keys); - for (int i = 0; i <= MAX_UOP_ID; i++) { if (stats->opcode[i].execution_count) { fprintf(out, "uops[%s].execution_count : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].execution_count); @@ -258,7 +258,6 @@ print_optimization_stats(FILE *out, OptimizationStats *stats) fprintf(out, "uops[%s].specialization.miss : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].miss); } } - for (int i = 0; i < 256; i++) { if (stats->unsupported_opcode[i]) { fprintf( @@ -289,6 +288,7 @@ print_optimization_stats(FILE *out, OptimizationStats *stats) } } } +#endif static void print_rare_event_stats(FILE *out, RareEventStats *stats) @@ -309,7 +309,9 @@ print_stats(FILE *out, PyStats *stats) print_call_stats(out, &stats->call_stats); print_object_stats(out, &stats->object_stats); print_gc_stats(out, stats->gc_stats); +#ifdef _Py_TIER2 print_optimization_stats(out, &stats->optimization_stats); +#endif print_rare_event_stats(out, &stats->rare_event_stats); } From 2770d5caca42d48102f8e18210132a964c34af7c Mon Sep 17 00:00:00 2001 From: Raphael Gaschignard Date: Fri, 3 May 2024 01:55:29 +1000 Subject: [PATCH 166/217] gh-105879: Add support for keyword arguments to eval and exec (#105885) Co-authored-by: Jelle Zijlstra --- Doc/library/functions.rst | 18 ++-- Lib/test/test_builtin.py | 20 +++++ ...-06-18-00-27-57.gh-issue-105879.dPw78k.rst | 2 + Python/bltinmodule.c | 8 +- Python/clinic/bltinmodule.c.h | 83 +++++++++++++------ 5 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-18-00-27-57.gh-issue-105879.dPw78k.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index be3a64cf41b425..4fdf30e0d252ae 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -524,11 +524,11 @@ are always available. They are listed here in alphabetical order. .. _func-eval: -.. function:: eval(expression, globals=None, locals=None) +.. function:: eval(source, /, globals=None, locals=None) - :param expression: + :param source: A Python expression. - :type expression: :class:`str` | :ref:`code object ` + :type source: :class:`str` | :ref:`code object ` :param globals: The global namespace (default: ``None``). @@ -583,11 +583,15 @@ are always available. They are listed here in alphabetical order. Raises an :ref:`auditing event ` ``exec`` with the code object as the argument. Code compilation events may also be raised. + .. versionchanged:: 3.13 + + The *globals* and *locals* arguments can now be passed as keywords. + .. index:: pair: built-in function; exec -.. function:: exec(object, globals=None, locals=None, /, *, closure=None) +.. function:: exec(source, /, globals=None, locals=None, *, closure=None) - This function supports dynamic execution of Python code. *object* must be + This function supports dynamic execution of Python code. *source* must be either a string or a code object. If it is a string, the string is parsed as a suite of Python statements which is then executed (unless a syntax error occurs). [#]_ If it is a code object, it is simply executed. In all cases, @@ -640,6 +644,10 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.11 Added the *closure* parameter. + .. versionchanged:: 3.13 + + The *globals* and *locals* arguments can now be passed as keywords. + .. function:: filter(function, iterable) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 9a0bf524e3943f..230789f29ff788 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -46,6 +46,8 @@ x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer HAVE_DOUBLE_ROUNDING = (x + y == 1e16 + 4) +# used as proof of globals being used +A_GLOBAL_VALUE = 123 class Squares: @@ -684,6 +686,11 @@ def __getitem__(self, key): raise ValueError self.assertRaises(ValueError, eval, "foo", {}, X()) + def test_eval_kwargs(self): + data = {"A_GLOBAL_VALUE": 456} + self.assertEqual(eval("globals()['A_GLOBAL_VALUE']", globals=data), 456) + self.assertEqual(eval("globals()['A_GLOBAL_VALUE']", locals=data), 123) + def test_general_eval(self): # Tests that general mappings can be used for the locals argument @@ -777,6 +784,19 @@ def test_exec(self): del l['__builtins__'] self.assertEqual((g, l), ({'a': 1}, {'b': 2})) + def test_exec_kwargs(self): + g = {} + exec('global z\nz = 1', globals=g) + if '__builtins__' in g: + del g['__builtins__'] + self.assertEqual(g, {'z': 1}) + + # if we only set locals, the global assignment will not + # reach this locals dictionary + g = {} + exec('global z\nz = 1', locals=g) + self.assertEqual(g, {}) + def test_exec_globals(self): code = compile("print('Hello World!')", "", "exec") # no builtin function diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-18-00-27-57.gh-issue-105879.dPw78k.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-18-00-27-57.gh-issue-105879.dPw78k.rst new file mode 100644 index 00000000000000..e666688d09cb5c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-06-18-00-27-57.gh-issue-105879.dPw78k.rst @@ -0,0 +1,2 @@ +Allow the *globals* and *locals* arguments to :func:`exec` +and :func:`eval` to be passed as keywords. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 7af3ac9c5158d6..722353ebcbfc3d 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -925,9 +925,9 @@ builtin_divmod_impl(PyObject *module, PyObject *x, PyObject *y) eval as builtin_eval source: object + / globals: object = None locals: object = None - / Evaluate the given source in the context of globals and locals. @@ -941,7 +941,7 @@ If only globals is given, locals defaults to it. static PyObject * builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, PyObject *locals) -/*[clinic end generated code: output=0a0824aa70093116 input=11ee718a8640e527]*/ +/*[clinic end generated code: output=0a0824aa70093116 input=7c7bce5299a89062]*/ { PyObject *result = NULL, *source_copy; const char *str; @@ -1024,9 +1024,9 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, exec as builtin_exec source: object + / globals: object = None locals: object = None - / * closure: object(c_default="NULL") = None @@ -1044,7 +1044,7 @@ when source is a code object requiring exactly that many cellvars. static PyObject * builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, PyObject *locals, PyObject *closure) -/*[clinic end generated code: output=7579eb4e7646743d input=f13a7e2b503d1d9a]*/ +/*[clinic end generated code: output=7579eb4e7646743d input=25e989b6d87a3a21]*/ { PyObject *v; diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 3f005bcbfb6a1a..f75a8d4ac0ccd4 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -395,7 +395,7 @@ builtin_divmod(PyObject *module, PyObject *const *args, Py_ssize_t nargs) } PyDoc_STRVAR(builtin_eval__doc__, -"eval($module, source, globals=None, locals=None, /)\n" +"eval($module, source, /, globals=None, locals=None)\n" "--\n" "\n" "Evaluate the given source in the context of globals and locals.\n" @@ -407,33 +407,63 @@ PyDoc_STRVAR(builtin_eval__doc__, "If only globals is given, locals defaults to it."); #define BUILTIN_EVAL_METHODDEF \ - {"eval", _PyCFunction_CAST(builtin_eval), METH_FASTCALL, builtin_eval__doc__}, + {"eval", _PyCFunction_CAST(builtin_eval), METH_FASTCALL|METH_KEYWORDS, builtin_eval__doc__}, static PyObject * builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, PyObject *locals); static PyObject * -builtin_eval(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +builtin_eval(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(globals), &_Py_ID(locals), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "globals", "locals", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "eval", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *source; PyObject *globals = Py_None; PyObject *locals = Py_None; - if (!_PyArg_CheckPositional("eval", nargs, 1, 3)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + if (!args) { goto exit; } source = args[0]; - if (nargs < 2) { - goto skip_optional; + if (!noptargs) { + goto skip_optional_pos; } - globals = args[1]; - if (nargs < 3) { - goto skip_optional; + if (args[1]) { + globals = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } } locals = args[2]; -skip_optional: +skip_optional_pos: return_value = builtin_eval_impl(module, source, globals, locals); exit: @@ -441,7 +471,7 @@ builtin_eval(PyObject *module, PyObject *const *args, Py_ssize_t nargs) } PyDoc_STRVAR(builtin_exec__doc__, -"exec($module, source, globals=None, locals=None, /, *, closure=None)\n" +"exec($module, source, /, globals=None, locals=None, *, closure=None)\n" "--\n" "\n" "Execute the given source in the context of globals and locals.\n" @@ -467,14 +497,14 @@ builtin_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 1 + #define NUM_KEYWORDS 3 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD PyObject *ob_item[NUM_KEYWORDS]; } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(closure), }, + .ob_item = { &_Py_ID(globals), &_Py_ID(locals), &_Py_ID(closure), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -483,7 +513,7 @@ builtin_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"", "", "", "closure", NULL}; + static const char * const _keywords[] = {"", "globals", "locals", "closure", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "exec", @@ -502,17 +532,22 @@ builtin_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject goto exit; } source = args[0]; - if (nargs < 2) { - goto skip_optional_posonly; + if (!noptargs) { + goto skip_optional_pos; } - noptargs--; - globals = args[1]; - if (nargs < 3) { - goto skip_optional_posonly; + if (args[1]) { + globals = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } } - noptargs--; - locals = args[2]; -skip_optional_posonly: + if (args[2]) { + locals = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: if (!noptargs) { goto skip_optional_kwonly; } @@ -1193,4 +1228,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=6d15edfc194b2c08 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=435d3f286a863c49 input=a9049054013a1b77]*/ From 6bcbee09df9f6fa6496fb7f68b6d655f190903a7 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Thu, 2 May 2024 19:30:00 +0200 Subject: [PATCH 167/217] gh-93502: Add new C-API functions to trace object creation and destruction (#115945) --- Doc/c-api/init.rst | 52 ++++++++++++ Doc/whatsnew/3.13.rst | 5 ++ Include/cpython/object.h | 10 +++ Include/internal/pycore_object.h | 2 +- Include/internal/pycore_runtime.h | 7 ++ Include/internal/pycore_runtime_init.h | 4 + ...4-02-26-13-14-52.gh-issue-93502.JMWRvA.rst | 4 + Modules/_testcapimodule.c | 84 +++++++++++++++++++ Objects/object.c | 37 ++++++-- Python/tracemalloc.c | 10 ++- 10 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-02-26-13-14-52.gh-issue-93502.JMWRvA.rst diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 8725ce085145aa..9e118d4f36145f 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1904,6 +1904,58 @@ Python-level trace functions in previous versions. .. versionadded:: 3.12 +Reference tracing +================= + +.. versionadded:: 3.13 + +.. c:type:: int (*PyRefTracer)(PyObject *, int event, void* data) + + The type of the trace function registered using :c:func:`PyRefTracer_SetTracer`. + The first parameter is a Python object that has been just created (when **event** + is set to :c:data:`PyRefTracer_CREATE`) or about to be destroyed (when **event** + is set to :c:data:`PyRefTracer_DESTROY`). The **data** argument is the opaque pointer + that was provided when :c:func:`PyRefTracer_SetTracer` was called. + +.. versionadded:: 3.13 + +.. c:var:: int PyRefTracer_CREATE + + The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python + object has been created. + +.. c:var:: int PyRefTracer_DESTROY + + The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python + object has been destroyed. + +.. c:function:: int PyRefTracer_SetTracer(PyRefTracer tracer, void *data) + + Register a reference tracer function. The function will be called when a new + Python has been created or when an object is going to be destroyed. If + **data** is provided it must be an opaque pointer that will be provided when + the tracer function is called. Return ``0`` on success. Set an exception and + return ``-1`` on error. + + Not that tracer functions **must not** create Python objects inside or + otherwise the call will be re-entrant. The tracer also **must not** clear + any existing exception or set an exception. The GIL will be held every time + the tracer function is called. + + The GIL must be held when calling this function. + +.. versionadded:: 3.13 + +.. c:function:: PyRefTracer PyRefTracer_GetTracer(void** data) + + Get the registered reference tracer function and the value of the opaque data + pointer that was registered when :c:func:`PyRefTracer_SetTracer` was called. + If no tracer was registered this function will return NULL and will set the + **data** pointer to NULL. + + The GIL must be held when calling this function. + +.. versionadded:: 3.13 .. _advanced-debugging: diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 3ccf17be9796d5..fbf2f4c447468d 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1961,6 +1961,11 @@ New Features * Add :c:func:`PyType_GetModuleByDef` to the limited C API (Contributed by Victor Stinner in :gh:`116936`.) +* Add two new functions to the C-API, :c:func:`PyRefTracer_SetTracer` and + :c:func:`PyRefTracer_GetTracer`, that allows to track object creation and + destruction the same way the :mod:`tracemalloc` module does. (Contributed + by Pablo Galindo in :gh:`93502`.) + Porting to Python 3.13 ---------------------- diff --git a/Include/cpython/object.h b/Include/cpython/object.h index a6b93b93ab0f7a..c2830b75e66fbe 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -510,3 +510,13 @@ PyAPI_FUNC(int) PyType_Unwatch(int watcher_id, PyObject *type); * assigned, or 0 if a new tag could not be assigned. */ PyAPI_FUNC(int) PyUnstable_Type_AssignVersionTag(PyTypeObject *type); + + +typedef enum { + PyRefTracer_CREATE = 0, + PyRefTracer_DESTROY = 1, +} PyRefTracerEvent; + +typedef int (*PyRefTracer)(PyObject *, PyRefTracerEvent event, void *); +PyAPI_FUNC(int) PyRefTracer_SetTracer(PyRefTracer tracer, void *data); +PyAPI_FUNC(PyRefTracer) PyRefTracer_GetTracer(void**); diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 7df8003196d8cc..3b0222b05cbd70 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -257,7 +257,7 @@ extern int _PyDict_CheckConsistency(PyObject *mp, int check_content); when a memory block is reused from a free list. Internal function called by _Py_NewReference(). */ -extern int _PyTraceMalloc_NewReference(PyObject *op); +extern int _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, void*); // Fast inlined version of PyType_HasFeature() static inline int diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index dc6f6f100f7a92..f58eccf729cb2a 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -132,6 +132,12 @@ typedef struct _Py_DebugOffsets { } unicode_object; } _Py_DebugOffsets; +/* Reference tracer state */ +struct _reftracer_runtime_state { + PyRefTracer tracer_func; + void* tracer_data; +}; + /* Full Python runtime state */ /* _PyRuntimeState holds the global state for the CPython runtime. @@ -236,6 +242,7 @@ typedef struct pyruntimestate { struct _fileutils_state fileutils; struct _faulthandler_runtime_state faulthandler; struct _tracemalloc_runtime_state tracemalloc; + struct _reftracer_runtime_state ref_tracer; // The rwmutex is used to prevent overlapping global and per-interpreter // stop-the-world events. Global stop-the-world events lock the mutex diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 41331df8320a9c..98920dbb7c7a92 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -128,6 +128,10 @@ extern PyTypeObject _PyExc_MemoryError; }, \ .faulthandler = _faulthandler_runtime_state_INIT, \ .tracemalloc = _tracemalloc_runtime_state_INIT, \ + .ref_tracer = { \ + .tracer_func = NULL, \ + .tracer_data = NULL, \ + }, \ .stoptheworld = { \ .is_global = 1, \ }, \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-02-26-13-14-52.gh-issue-93502.JMWRvA.rst b/Misc/NEWS.d/next/Core and Builtins/2024-02-26-13-14-52.gh-issue-93502.JMWRvA.rst new file mode 100644 index 00000000000000..524626950c02e6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-02-26-13-14-52.gh-issue-93502.JMWRvA.rst @@ -0,0 +1,4 @@ +Add two new functions to the C-API, :c:func:`PyRefTracer_SetTracer` and +:c:func:`PyRefTracer_GetTracer`, that allows to track object creation and +destruction the same way the :mod:`tracemalloc` module does. Patch by Pablo +Galindo diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3448291e401e35..f5892fc5ed2a2c 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3219,6 +3219,89 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) _Py_COMP_DIAG_POP } +struct simpletracer_data { + int create_count; + int destroy_count; + void* addresses[10]; +}; + +static int _simpletracer(PyObject *obj, PyRefTracerEvent event, void* data) { + struct simpletracer_data* the_data = (struct simpletracer_data*)data; + assert(the_data->create_count + the_data->destroy_count < (int)Py_ARRAY_LENGTH(the_data->addresses)); + the_data->addresses[the_data->create_count + the_data->destroy_count] = obj; + if (event == PyRefTracer_CREATE) { + the_data->create_count++; + } else { + the_data->destroy_count++; + } + return 0; +} + +static PyObject * +test_reftracer(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + // Save the current tracer and data to restore it later + void* current_data; + PyRefTracer current_tracer = PyRefTracer_GetTracer(¤t_data); + + struct simpletracer_data tracer_data = {0}; + void* the_data = &tracer_data; + // Install a simple tracer function + if (PyRefTracer_SetTracer(_simpletracer, the_data) != 0) { + goto failed; + } + + // Check that the tracer was correctly installed + void* data; + if (PyRefTracer_GetTracer(&data) != _simpletracer || data != the_data) { + PyErr_SetString(PyExc_AssertionError, "The reftracer not correctly installed"); + (void)PyRefTracer_SetTracer(NULL, NULL); + goto failed; + } + + // Create a bunch of objects + PyObject* obj = PyList_New(0); + if (obj == NULL) { + goto failed; + } + PyObject* obj2 = PyDict_New(); + if (obj2 == NULL) { + Py_DECREF(obj); + goto failed; + } + + // Kill all objects + Py_DECREF(obj); + Py_DECREF(obj2); + + // Remove the tracer + (void)PyRefTracer_SetTracer(NULL, NULL); + + // Check that the tracer was removed + if (PyRefTracer_GetTracer(&data) != NULL || data != NULL) { + PyErr_SetString(PyExc_ValueError, "The reftracer was not correctly removed"); + goto failed; + } + + if (tracer_data.create_count != 2 || + tracer_data.addresses[0] != obj || + tracer_data.addresses[1] != obj2) { + PyErr_SetString(PyExc_ValueError, "The object creation was not correctly traced"); + goto failed; + } + + if (tracer_data.destroy_count != 2 || + tracer_data.addresses[2] != obj || + tracer_data.addresses[3] != obj2) { + PyErr_SetString(PyExc_ValueError, "The object destruction was not correctly traced"); + goto failed; + } + PyRefTracer_SetTracer(current_tracer, current_data); + Py_RETURN_NONE; +failed: + PyRefTracer_SetTracer(current_tracer, current_data); + return NULL; +} static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, @@ -3257,6 +3340,7 @@ static PyMethodDef TestMethods[] = { {"get_type_fullyqualname", get_type_fullyqualname, METH_O}, {"get_type_module_name", get_type_module_name, METH_O}, {"test_get_type_dict", test_get_type_dict, METH_NOARGS}, + {"test_reftracer", test_reftracer, METH_NOARGS}, {"_test_thread_state", test_thread_state, METH_VARARGS}, #ifndef MS_WINDOWS {"_spawn_pthread_waiter", spawn_pthread_waiter, METH_NOARGS}, diff --git a/Objects/object.c b/Objects/object.c index 45310a6c22d677..79e4fb4dbbf7c6 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2372,9 +2372,6 @@ _PyTypes_FiniTypes(PyInterpreterState *interp) static inline void new_reference(PyObject *op) { - if (_PyRuntime.tracemalloc.config.tracing) { - _PyTraceMalloc_NewReference(op); - } // Skip the immortal object check in Py_SET_REFCNT; always set refcnt to 1 #if !defined(Py_GIL_DISABLED) op->ob_refcnt = 1; @@ -2389,6 +2386,11 @@ new_reference(PyObject *op) #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op); #endif + struct _reftracer_runtime_state *tracer = &_PyRuntime.ref_tracer; + if (tracer->tracer_func != NULL) { + void* data = tracer->tracer_data; + tracer->tracer_func(op, PyRefTracer_CREATE, data); + } } void @@ -2450,12 +2452,13 @@ _PyObject_SetDeferredRefcount(PyObject *op) void _Py_ResurrectReference(PyObject *op) { - if (_PyRuntime.tracemalloc.config.tracing) { - _PyTraceMalloc_NewReference(op); - } #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op); #endif + if (_PyRuntime.ref_tracer.tracer_func != NULL) { + void* data = _PyRuntime.ref_tracer.tracer_data; + _PyRuntime.ref_tracer.tracer_func(op, PyRefTracer_CREATE, data); + } } @@ -2845,6 +2848,12 @@ _Py_Dealloc(PyObject *op) Py_INCREF(type); #endif + struct _reftracer_runtime_state *tracer = &_PyRuntime.ref_tracer; + if (tracer->tracer_func != NULL) { + void* data = tracer->tracer_data; + tracer->tracer_func(op, PyRefTracer_DESTROY, data); + } + #ifdef Py_TRACE_REFS _Py_ForgetReference(op); #endif @@ -2933,6 +2942,22 @@ _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt) Py_SET_REFCNT(ob, refcnt); } +int PyRefTracer_SetTracer(PyRefTracer tracer, void *data) { + assert(PyGILState_Check()); + _PyRuntime.ref_tracer.tracer_func = tracer; + _PyRuntime.ref_tracer.tracer_data = data; + return 0; +} + +PyRefTracer PyRefTracer_GetTracer(void** data) { + assert(PyGILState_Check()); + if (data != NULL) { + *data = _PyRuntime.ref_tracer.tracer_data; + } + return _PyRuntime.ref_tracer.tracer_func; +} + + static PyObject* constants[] = { &_Py_NoneStruct, // Py_CONSTANT_NONE diff --git a/Python/tracemalloc.c b/Python/tracemalloc.c index 19b64c619feb6a..e3ec72062f6931 100644 --- a/Python/tracemalloc.c +++ b/Python/tracemalloc.c @@ -906,6 +906,10 @@ _PyTraceMalloc_Start(int max_nframe) return -1; } + if (PyRefTracer_SetTracer(_PyTraceMalloc_TraceRef, NULL) < 0) { + return -1; + } + if (tracemalloc_config.tracing) { /* hook already installed: do nothing */ return 0; @@ -1352,8 +1356,12 @@ _PyTraceMalloc_Fini(void) Do nothing if tracemalloc is not tracing memory allocations or if the object memory block is not already traced. */ int -_PyTraceMalloc_NewReference(PyObject *op) +_PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, void* Py_UNUSED(ignore)) { + if (event != PyRefTracer_CREATE) { + return 0; + } + assert(PyGILState_Check()); if (!tracemalloc_config.tracing) { From b28a3339e4c63ea3a801dba9bbbc6af5af42c3a0 Mon Sep 17 00:00:00 2001 From: infohash <46137868+infohash@users.noreply.github.com> Date: Thu, 2 May 2024 23:06:35 +0530 Subject: [PATCH 168/217] gh-90848: Fixed create_autospec ignoring configure_mock style kwargs (#118163) --- Lib/test/test_unittest/testmock/testmock.py | 13 ++++++++++++ Lib/unittest/mock.py | 20 +++++++++++-------- ...4-04-22-21-54-12.gh-issue-90848.5jHEEc.rst | 1 + 3 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index b81b3049d56bf8..77f6f1eb4b76b9 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -115,6 +115,19 @@ def f(): pass with self.assertRaises(TypeError): mock() + def test_create_autospec_should_be_configurable_by_kwargs(self): + """If kwargs are given to configure mock, the function must configure + the parent mock during initialization.""" + mocked_result = 'mocked value' + class_mock = create_autospec(spec=Something, **{ + 'return_value.meth.side_effect': [ValueError, DEFAULT], + 'return_value.meth.return_value': mocked_result}) + with self.assertRaises(ValueError): + class_mock().meth(a=None, b=None, c=None) + self.assertEqual(class_mock().meth(a=None, b=None, c=None), mocked_result) + # Only the parent mock should be configurable because the user will + # pass kwargs with respect to the parent mock. + self.assertEqual(class_mock().return_value.meth.side_effect, None) def test_repr(self): mock = Mock(name='foo') diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 1799e9bbf58592..a2634b6164062a 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2788,8 +2788,8 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, if _parent is not None and not instance: _parent._mock_children[_name] = mock - wrapped = kwargs.get('wraps') - + # Pop wraps from kwargs because it must not be passed to configure_mock. + wrapped = kwargs.pop('wraps', None) if is_type and not instance and 'return_value' not in kwargs: mock.return_value = create_autospec(spec, spec_set, instance=True, _name='()', _parent=mock, @@ -2814,12 +2814,12 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, except AttributeError: continue - kwargs = {'spec': original} + child_kwargs = {'spec': original} # Wrap child attributes also. if wrapped and hasattr(wrapped, entry): - kwargs.update(wraps=original) + child_kwargs.update(wraps=original) if spec_set: - kwargs = {'spec_set': original} + child_kwargs = {'spec_set': original} if not isinstance(original, FunctionTypes): new = _SpecState(original, spec_set, mock, entry, instance) @@ -2830,14 +2830,13 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, parent = mock.mock skipfirst = _must_skip(spec, entry, is_type) - kwargs['_eat_self'] = skipfirst + child_kwargs['_eat_self'] = skipfirst if iscoroutinefunction(original): child_klass = AsyncMock else: child_klass = MagicMock new = child_klass(parent=parent, name=entry, _new_name=entry, - _new_parent=parent, - **kwargs) + _new_parent=parent, **child_kwargs) mock._mock_children[entry] = new new.return_value = child_klass() _check_signature(original, new, skipfirst=skipfirst) @@ -2848,6 +2847,11 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, # setting as an instance attribute? if isinstance(new, FunctionTypes): setattr(mock, entry, new) + # kwargs are passed with respect to the parent mock so, they are not used + # for creating return_value of the parent mock. So, this condition + # should be true only for the parent mock if kwargs are given. + if _is_instance_mock(mock) and kwargs: + mock.configure_mock(**kwargs) return mock diff --git a/Misc/NEWS.d/next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst b/Misc/NEWS.d/next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst new file mode 100644 index 00000000000000..adbca0127ed0ce --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst @@ -0,0 +1 @@ +Fixed :func:`unittest.mock.create_autospec` to configure parent mock with keyword arguments. From 83c51da6cebdced80ebc59de70e8844244de7298 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 2 May 2024 13:41:15 -0400 Subject: [PATCH 169/217] gh-118413: Fix test_release_task_refs on free-threaded build (#118494) The `time.sleep()` call should happen before the GC to give the worker threads time to clean-up their remaining references to objs. Additionally, use `support.gc_collect()` instead of `gc.collect()` just in case the extra GC calls matter. --- Lib/test/_test_multiprocessing.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 91f85990116ee7..5fc4181a1eeadb 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2804,7 +2804,6 @@ def test_map_no_failfast(self): # check that we indeed waited for all jobs self.assertGreater(time.monotonic() - t_start, 0.9) - @support.requires_gil_enabled("gh-118413: test is flaky with GIL disabled") def test_release_task_refs(self): # Issue #29861: task arguments and results should not be kept # alive after we are done with them. @@ -2813,8 +2812,8 @@ def test_release_task_refs(self): self.pool.map(identity, objs) del objs - gc.collect() # For PyPy or other GCs. time.sleep(DELTA) # let threaded cleanup code run + support.gc_collect() # For PyPy or other GCs. self.assertEqual(set(wr() for wr in refs), {None}) # With a process pool, copies of the objects are returned, check # they were released too. From 16acecd77924b849f51b36f41cd21ca759c9ff2a Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 2 May 2024 13:51:28 -0400 Subject: [PATCH 170/217] Remove stray `__cplusplus` guard in sysmodule.c (#118511) --- Python/sysmodule.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 726051521cf574..d3fbfcd3e79636 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2496,10 +2496,6 @@ PyAPI_FUNC(int) PyUnstable_CopyPerfMapFile(const char* parent_filename) { return 0; } -#ifdef __cplusplus -} -#endif - static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ From 8ed546679524140d8282175411fd141fe7df070d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 2 May 2024 19:43:54 +0100 Subject: [PATCH 171/217] gh-118486: Switch mkdir(mode=0o700) on Windows to use OWNER RIGHTS instead of CURRENT_USER (GH-118515) --- Modules/posixmodule.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index f9533577a8fa34..e1a14e772c4bd0 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5587,6 +5587,7 @@ struct _Py_SECURITY_ATTRIBUTE_DATA { PACL acl; SECURITY_DESCRIPTOR sd; EXPLICIT_ACCESS_W ea[4]; + char sid[64]; }; static int @@ -5616,13 +5617,25 @@ initializeMkdir700SecurityAttributes( return GetLastError(); } + int use_alias = 0; + DWORD cbSid = sizeof(data->sid); + if (!CreateWellKnownSid(WinCreatorOwnerRightsSid, NULL, (PSID)data->sid, &cbSid)) { + use_alias = 1; + } + data->securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); data->ea[0].grfAccessPermissions = GENERIC_ALL; data->ea[0].grfAccessMode = SET_ACCESS; data->ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; - data->ea[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME; - data->ea[0].Trustee.TrusteeType = TRUSTEE_IS_ALIAS; - data->ea[0].Trustee.ptstrName = L"CURRENT_USER"; + if (use_alias) { + data->ea[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME; + data->ea[0].Trustee.TrusteeType = TRUSTEE_IS_ALIAS; + data->ea[0].Trustee.ptstrName = L"CURRENT_USER"; + } else { + data->ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + data->ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + data->ea[0].Trustee.ptstrName = (LPWCH)(SID*)data->sid; + } data->ea[1].grfAccessPermissions = GENERIC_ALL; data->ea[1].grfAccessMode = SET_ACCESS; From 1e67b9207c31a92d76bfac09fc706c4dc703669e Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Thu, 2 May 2024 13:03:05 -0700 Subject: [PATCH 172/217] gh-117657: Fix TSAN list set failure (#118260) * Fix TSAN list set failure * Relaxed atomic is sufficient, add targetted test * More list * Remove atomic assign in list * Fixup white space --- Include/internal/pycore_list.h | 4 ++ Lib/test/test_free_threading/test_list.py | 80 +++++++++++++++++++++++ Objects/listobject.c | 9 ++- 3 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 Lib/test/test_free_threading/test_list.py diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h index 2a82912e41d557..73695d10e0c372 100644 --- a/Include/internal/pycore_list.h +++ b/Include/internal/pycore_list.h @@ -28,7 +28,11 @@ _PyList_AppendTakeRef(PyListObject *self, PyObject *newitem) Py_ssize_t allocated = self->allocated; assert((size_t)len + 1 < PY_SSIZE_T_MAX); if (allocated > len) { +#ifdef Py_GIL_DISABLED + _Py_atomic_store_ptr_release(&self->ob_item[len], newitem); +#else PyList_SET_ITEM(self, len, newitem); +#endif Py_SET_SIZE(self, len + 1); return 0; } diff --git a/Lib/test/test_free_threading/test_list.py b/Lib/test/test_free_threading/test_list.py new file mode 100644 index 00000000000000..79cb0a93092365 --- /dev/null +++ b/Lib/test/test_free_threading/test_list.py @@ -0,0 +1,80 @@ +import unittest + +from threading import Thread +from unittest import TestCase + +from test.support import is_wasi + + +class C: + def __init__(self, v): + self.v = v + + +@unittest.skipIf(is_wasi, "WASI has no threads.") +class TestList(TestCase): + def test_racing_iter_append(self): + + l = [] + OBJECT_COUNT = 10000 + + def writer_func(): + for i in range(OBJECT_COUNT): + l.append(C(i + OBJECT_COUNT)) + + def reader_func(): + while True: + count = len(l) + for i, x in enumerate(l): + self.assertEqual(x.v, i + OBJECT_COUNT) + if count == OBJECT_COUNT: + break + + writer = Thread(target=writer_func) + readers = [] + for x in range(30): + reader = Thread(target=reader_func) + readers.append(reader) + reader.start() + + writer.start() + writer.join() + for reader in readers: + reader.join() + + def test_racing_iter_extend(self): + iters = [ + lambda x: [x], + ] + for iter_case in iters: + with self.subTest(iter=iter_case): + l = [] + OBJECT_COUNT = 10000 + + def writer_func(): + for i in range(OBJECT_COUNT): + l.extend(iter_case(C(i + OBJECT_COUNT))) + + def reader_func(): + while True: + count = len(l) + for i, x in enumerate(l): + self.assertEqual(x.v, i + OBJECT_COUNT) + if count == OBJECT_COUNT: + break + + writer = Thread(target=writer_func) + readers = [] + for x in range(30): + reader = Thread(target=reader_func) + readers.append(reader) + reader.start() + + writer.start() + writer.join() + for reader in readers: + reader.join() + + +if __name__ == "__main__": + unittest.main() diff --git a/Objects/listobject.c b/Objects/listobject.c index 4eaf20033fa262..3c4e2d2e6ed7de 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -141,6 +141,9 @@ list_resize(PyListObject *self, Py_ssize_t newsize) target_bytes = allocated * sizeof(PyObject*); } memcpy(array->ob_item, self->ob_item, target_bytes); + } + if (new_allocated > (size_t)allocated) { + memset(array->ob_item + allocated, 0, sizeof(PyObject *) * (new_allocated - allocated)); } _Py_atomic_store_ptr_release(&self->ob_item, &array->ob_item); self->allocated = new_allocated; @@ -502,7 +505,7 @@ _PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem) Py_DECREF(newitem); return -1; } - PyList_SET_ITEM(self, len, newitem); + FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[len], newitem); return 0; } @@ -1181,7 +1184,7 @@ list_extend_fast(PyListObject *self, PyObject *iterable) PyObject **dest = self->ob_item + m; for (Py_ssize_t i = 0; i < n; i++) { PyObject *o = src[i]; - dest[i] = Py_NewRef(o); + FT_ATOMIC_STORE_PTR_RELEASE(dest[i], Py_NewRef(o)); } return 0; } @@ -1238,7 +1241,7 @@ list_extend_iter_lock_held(PyListObject *self, PyObject *iterable) if (Py_SIZE(self) < self->allocated) { Py_ssize_t len = Py_SIZE(self); - PyList_SET_ITEM(self, len, item); // steals item ref + FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[len], item); // steals item ref Py_SET_SIZE(self, len + 1); } else { From e54b0c8a4ad6c6e958245eb3ea4ecc47e0f97ff0 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Thu, 2 May 2024 13:03:29 -0700 Subject: [PATCH 173/217] gh-118519: Fix empty weakref list check (#118520) Fix empty list check --- Objects/weakrefobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 93c5fe3aacecfd..88afaec86827ed 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -988,7 +988,7 @@ PyObject_ClearWeakRefs(PyObject *object) } list = GET_WEAKREFS_LISTPTR(object); - if (FT_ATOMIC_LOAD_PTR(list) == NULL) { + if (FT_ATOMIC_LOAD_PTR(*list) == NULL) { // Fast path for the common case return; } From 4e2caf2aa046bf80e87e9b858837bb527459a034 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Thu, 2 May 2024 13:53:27 -0700 Subject: [PATCH 174/217] gh-118500: Add pdb support for zipapp (#118501) --- Doc/whatsnew/3.13.rst | 3 ++ Lib/pdb.py | 50 +++++++++++++++++-- Lib/test/test_pdb.py | 25 ++++++++++ Lib/test/test_pyclbr.py | 2 +- ...-05-02-04-27-12.gh-issue-118500.pBGGtQ.rst | 1 + 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-05-02-04-27-12.gh-issue-118500.pBGGtQ.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index fbf2f4c447468d..d59c4ee22e67d3 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -705,6 +705,9 @@ pdb command line option or :envvar:`PYTHONSAFEPATH` environment variable). (Contributed by Tian Gao and Christian Walther in :gh:`111762`.) +* :mod:`zipapp` is supported as a debugging target. + (Contributed by Tian Gao in :gh:`118501`.) + queue ----- diff --git a/Lib/pdb.py b/Lib/pdb.py index fa2e1ec94be235..bb669a0d2c1ce5 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -120,7 +120,10 @@ def find_function(funcname, filename): try: fp = tokenize.open(filename) except OSError: - return None + lines = linecache.getlines(filename) + if not lines: + return None + fp = io.StringIO(''.join(lines)) funcdef = "" funcstart = None # consumer of this info expects the first line to be 1 @@ -237,6 +240,44 @@ def namespace(self): ) +class _ZipTarget(_ExecutableTarget): + def __init__(self, target): + import runpy + + self._target = os.path.realpath(target) + sys.path.insert(0, self._target) + try: + _, self._spec, self._code = runpy._get_main_module_details() + except ImportError as e: + print(f"ImportError: {e}") + sys.exit(1) + except Exception: + traceback.print_exc() + sys.exit(1) + + def __repr__(self): + return self._target + + @property + def filename(self): + return self._code.co_filename + + @property + def code(self): + return self._code + + @property + def namespace(self): + return dict( + __name__='__main__', + __file__=os.path.normcase(os.path.abspath(self.filename)), + __package__=self._spec.parent, + __loader__=self._spec.loader, + __spec__=self._spec, + __builtins__=__builtins__, + ) + + class _PdbInteractiveConsole(code.InteractiveConsole): def __init__(self, ns, message): self._message = message @@ -1076,7 +1117,7 @@ def lineinfo(self, identifier): if f: fname = f item = parts[1] - answer = find_function(item, fname) + answer = find_function(item, self.canonic(fname)) return answer or failed def checkline(self, filename, lineno): @@ -2282,7 +2323,10 @@ def main(): if not opts.args: parser.error("no module or script to run") file = opts.args.pop(0) - target = _ScriptTarget(file) + if file.endswith('.pyz'): + target = _ZipTarget(file) + else: + target = _ScriptTarget(file) sys.argv[:] = [file] + opts.args # Hide "pdb.py" and pdb options from argument list diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 5635d17c99d2a3..a3d2dda43b086d 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -10,6 +10,7 @@ import subprocess import textwrap import linecache +import zipapp from contextlib import ExitStack, redirect_stdout from io import StringIO @@ -3532,6 +3533,30 @@ def test_non_utf8_encoding(self): if filename.endswith(".py"): self._run_pdb([os.path.join(script_dir, filename)], 'q') + def test_zipapp(self): + with os_helper.temp_dir() as temp_dir: + os.mkdir(os.path.join(temp_dir, 'source')) + script = textwrap.dedent( + """ + def f(x): + return x + 1 + f(21 + 21) + """ + ) + with open(os.path.join(temp_dir, 'source', '__main__.py'), 'w') as f: + f.write(script) + zipapp.create_archive(os.path.join(temp_dir, 'source'), + os.path.join(temp_dir, 'zipapp.pyz')) + stdout, _ = self._run_pdb([os.path.join(temp_dir, 'zipapp.pyz')], '\n'.join([ + 'b f', + 'c', + 'p x', + 'q' + ])) + self.assertIn('42', stdout) + self.assertIn('return x + 1', stdout) + + class ChecklineTests(unittest.TestCase): def setUp(self): linecache.clearcache() # Pdb.checkline() uses linecache.getline() diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index c7c5419ffe3e37..46206accbafc36 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -226,7 +226,7 @@ def test_others(self): cm( 'pdb', # pyclbr does not handle elegantly `typing` or properties - ignore=('Union', '_ModuleTarget', '_ScriptTarget'), + ignore=('Union', '_ModuleTarget', '_ScriptTarget', '_ZipTarget'), ) cm('pydoc', ignore=('input', 'output',)) # properties diff --git a/Misc/NEWS.d/next/Library/2024-05-02-04-27-12.gh-issue-118500.pBGGtQ.rst b/Misc/NEWS.d/next/Library/2024-05-02-04-27-12.gh-issue-118500.pBGGtQ.rst new file mode 100644 index 00000000000000..62c7b5f8f24f26 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-02-04-27-12.gh-issue-118500.pBGGtQ.rst @@ -0,0 +1 @@ +Add :mod:`pdb` support for zipapps From f8290df63f1fd970dfd6bbfdc9a86341a9f97d05 Mon Sep 17 00:00:00 2001 From: Brett Simmers Date: Thu, 2 May 2024 15:25:36 -0700 Subject: [PATCH 175/217] gh-116738: Make `_codecs` module thread-safe (#117530) The module itself is a thin wrapper around calls to functions in `Python/codecs.c`, so that's where the meaningful changes happened: - Move codecs-related state that lives on `PyInterpreterState` to a struct declared in `pycore_codecs.h`. - In free-threaded builds, add a mutex to `codecs_state` to synchronize operations on `search_path`. Because `search_path_mutex` is used as a normal mutex and not a critical section, we must be extremely careful with operations called while holding it. - The codec registry is explicitly initialized as part of `_PyUnicode_InitEncodings` to simplify thread-safety. --- Include/internal/pycore_codecs.h | 31 ++++++ Include/internal/pycore_interp.h | 6 +- Objects/unicodeobject.c | 6 +- Python/codecs.c | 150 ++++++++++++++------------- Python/pystate.c | 4 +- Tools/c-analyzer/cpython/ignored.tsv | 2 +- 6 files changed, 120 insertions(+), 79 deletions(-) diff --git a/Include/internal/pycore_codecs.h b/Include/internal/pycore_codecs.h index a2a7151d50ade7..5e2d5c5ce9d868 100644 --- a/Include/internal/pycore_codecs.h +++ b/Include/internal/pycore_codecs.h @@ -8,6 +8,17 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex + +/* Initialize codecs-related state for the given interpreter, including + registering the first codec search function. Must be called before any other + PyCodec-related functions, and while only one thread is active. */ +extern PyStatus _PyCodec_InitRegistry(PyInterpreterState *interp); + +/* Finalize codecs-related state for the given interpreter. No PyCodec-related + functions other than PyCodec_Unregister() may be called after this. */ +extern void _PyCodec_Fini(PyInterpreterState *interp); + extern PyObject* _PyCodec_Lookup(const char *encoding); /* Text codec specific encoding and decoding API. @@ -48,6 +59,26 @@ extern PyObject* _PyCodecInfo_GetIncrementalEncoder( PyObject *codec_info, const char *errors); +// Per-interpreter state used by codecs.c. +struct codecs_state { + // A list of callable objects used to search for codecs. + PyObject *search_path; + + // A dict mapping codec names to codecs returned from a callable in + // search_path. + PyObject *search_cache; + + // A dict mapping error handling strategies to functions to implement them. + PyObject *error_registry; + +#ifdef Py_GIL_DISABLED + // Used to safely delete a specific item from search_path. + PyMutex search_path_mutex; +#endif + + // Whether or not the rest of the state is initialized. + int initialized; +}; #ifdef __cplusplus } diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index d38959e3ce4ec5..a2aec6dd4b7241 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -14,6 +14,7 @@ extern "C" { #include "pycore_atexit.h" // struct atexit_state #include "pycore_ceval_state.h" // struct _ceval_state #include "pycore_code.h" // struct callable_cache +#include "pycore_codecs.h" // struct codecs_state #include "pycore_context.h" // struct _Py_context_state #include "pycore_crossinterp.h" // struct _xidregistry #include "pycore_dict_state.h" // struct _Py_dict_state @@ -182,10 +183,7 @@ struct _is { possible to facilitate out-of-process observability tools. */ - PyObject *codec_search_path; - PyObject *codec_search_cache; - PyObject *codec_error_registry; - int codecs_initialized; + struct codecs_state codecs; PyConfig config; unsigned long feature_flags; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 2c259b7e869efe..67b1282de790e0 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15441,7 +15441,11 @@ init_fs_encoding(PyThreadState *tstate) PyStatus _PyUnicode_InitEncodings(PyThreadState *tstate) { - PyStatus status = init_fs_encoding(tstate); + PyStatus status = _PyCodec_InitRegistry(tstate->interp); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + status = init_fs_encoding(tstate); if (_PyStatus_EXCEPTION(status)) { return status; } diff --git a/Python/codecs.c b/Python/codecs.c index d8fe7b22063a80..bed245366f9234 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -11,6 +11,7 @@ Copyright (c) Corporation for National Research Initiatives. #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_interp.h" // PyInterpreterState.codec_search_path +#include "pycore_lock.h" // PyMutex #include "pycore_pyerrors.h" // _PyErr_FormatNote() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI @@ -19,24 +20,10 @@ const char *Py_hexdigits = "0123456789abcdef"; /* --- Codec Registry ----------------------------------------------------- */ -/* Import the standard encodings package which will register the first - codec search function. - - This is done in a lazy way so that the Unicode implementation does - not downgrade startup time of scripts not needing it. - - ImportErrors are silently ignored by this function. Only one try is - made. - -*/ - -static int _PyCodecRegistry_Init(void); /* Forward */ - int PyCodec_Register(PyObject *search_function) { PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) - goto onError; + assert(interp->codecs.initialized); if (search_function == NULL) { PyErr_BadArgument(); goto onError; @@ -45,7 +32,14 @@ int PyCodec_Register(PyObject *search_function) PyErr_SetString(PyExc_TypeError, "argument must be callable"); goto onError; } - return PyList_Append(interp->codec_search_path, search_function); +#ifdef Py_GIL_DISABLED + PyMutex_Lock(&interp->codecs.search_path_mutex); +#endif + int ret = PyList_Append(interp->codecs.search_path, search_function); +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&interp->codecs.search_path_mutex); +#endif + return ret; onError: return -1; @@ -55,22 +49,34 @@ int PyCodec_Unregister(PyObject *search_function) { PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *codec_search_path = interp->codec_search_path; - /* Do nothing if codec_search_path is not created yet or was cleared. */ - if (codec_search_path == NULL) { + if (interp->codecs.initialized != 1) { + /* Do nothing if codecs state was cleared (only possible during + interpreter shutdown). */ return 0; } + PyObject *codec_search_path = interp->codecs.search_path; assert(PyList_CheckExact(codec_search_path)); - Py_ssize_t n = PyList_GET_SIZE(codec_search_path); - for (Py_ssize_t i = 0; i < n; i++) { - PyObject *item = PyList_GET_ITEM(codec_search_path, i); + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(codec_search_path); i++) { +#ifdef Py_GIL_DISABLED + PyMutex_Lock(&interp->codecs.search_path_mutex); +#endif + PyObject *item = PyList_GetItemRef(codec_search_path, i); + int ret = 1; if (item == search_function) { - if (interp->codec_search_cache != NULL) { - assert(PyDict_CheckExact(interp->codec_search_cache)); - PyDict_Clear(interp->codec_search_cache); - } - return PyList_SetSlice(codec_search_path, i, i+1, NULL); + // We hold a reference to the item, so its destructor can't run + // while we hold search_path_mutex. + ret = PyList_SetSlice(codec_search_path, i, i+1, NULL); + } +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&interp->codecs.search_path_mutex); +#endif + Py_DECREF(item); + if (ret != 1) { + assert(interp->codecs.search_cache != NULL); + assert(PyDict_CheckExact(interp->codecs.search_cache)); + PyDict_Clear(interp->codecs.search_cache); + return ret; } } return 0; @@ -132,9 +138,7 @@ PyObject *_PyCodec_Lookup(const char *encoding) } PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) { - return NULL; - } + assert(interp->codecs.initialized); /* Convert the encoding to a normalized Python string: all characters are converted to lower case, spaces and hyphens are @@ -147,7 +151,7 @@ PyObject *_PyCodec_Lookup(const char *encoding) /* First, try to lookup the name in the registry dictionary */ PyObject *result; - if (PyDict_GetItemRef(interp->codec_search_cache, v, &result) < 0) { + if (PyDict_GetItemRef(interp->codecs.search_cache, v, &result) < 0) { goto onError; } if (result != NULL) { @@ -156,7 +160,7 @@ PyObject *_PyCodec_Lookup(const char *encoding) } /* Next, scan the search functions in order of registration */ - const Py_ssize_t len = PyList_Size(interp->codec_search_path); + const Py_ssize_t len = PyList_Size(interp->codecs.search_path); if (len < 0) goto onError; if (len == 0) { @@ -170,14 +174,15 @@ PyObject *_PyCodec_Lookup(const char *encoding) for (i = 0; i < len; i++) { PyObject *func; - func = PyList_GetItem(interp->codec_search_path, i); + func = PyList_GetItemRef(interp->codecs.search_path, i); if (func == NULL) goto onError; result = PyObject_CallOneArg(func, v); + Py_DECREF(func); if (result == NULL) goto onError; if (result == Py_None) { - Py_DECREF(result); + Py_CLEAR(result); continue; } if (!PyTuple_Check(result) || PyTuple_GET_SIZE(result) != 4) { @@ -188,7 +193,7 @@ PyObject *_PyCodec_Lookup(const char *encoding) } break; } - if (i == len) { + if (result == NULL) { /* XXX Perhaps we should cache misses too ? */ PyErr_Format(PyExc_LookupError, "unknown encoding: %s", encoding); @@ -196,7 +201,7 @@ PyObject *_PyCodec_Lookup(const char *encoding) } /* Cache and return the result */ - if (PyDict_SetItem(interp->codec_search_cache, v, result) < 0) { + if (PyDict_SetItem(interp->codecs.search_cache, v, result) < 0) { Py_DECREF(result); goto onError; } @@ -600,13 +605,12 @@ PyObject *_PyCodec_DecodeText(PyObject *object, int PyCodec_RegisterError(const char *name, PyObject *error) { PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) - return -1; + assert(interp->codecs.initialized); if (!PyCallable_Check(error)) { PyErr_SetString(PyExc_TypeError, "handler must be callable"); return -1; } - return PyDict_SetItemString(interp->codec_error_registry, + return PyDict_SetItemString(interp->codecs.error_registry, name, error); } @@ -616,13 +620,12 @@ int PyCodec_RegisterError(const char *name, PyObject *error) PyObject *PyCodec_LookupError(const char *name) { PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) - return NULL; + assert(interp->codecs.initialized); if (name==NULL) name = "strict"; PyObject *handler; - if (PyDict_GetItemStringRef(interp->codec_error_registry, name, &handler) < 0) { + if (PyDict_GetItemStringRef(interp->codecs.error_registry, name, &handler) < 0) { return NULL; } if (handler == NULL) { @@ -1375,7 +1378,8 @@ static PyObject *surrogateescape_errors(PyObject *self, PyObject *exc) return PyCodec_SurrogateEscapeErrors(exc); } -static int _PyCodecRegistry_Init(void) +PyStatus +_PyCodec_InitRegistry(PyInterpreterState *interp) { static struct { const char *name; @@ -1463,45 +1467,51 @@ static int _PyCodecRegistry_Init(void) } }; - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *mod; - - if (interp->codec_search_path != NULL) - return 0; - - interp->codec_search_path = PyList_New(0); - if (interp->codec_search_path == NULL) { - return -1; + assert(interp->codecs.initialized == 0); + interp->codecs.search_path = PyList_New(0); + if (interp->codecs.search_path == NULL) { + return PyStatus_NoMemory(); } - - interp->codec_search_cache = PyDict_New(); - if (interp->codec_search_cache == NULL) { - return -1; + interp->codecs.search_cache = PyDict_New(); + if (interp->codecs.search_cache == NULL) { + return PyStatus_NoMemory(); } - - interp->codec_error_registry = PyDict_New(); - if (interp->codec_error_registry == NULL) { - return -1; + interp->codecs.error_registry = PyDict_New(); + if (interp->codecs.error_registry == NULL) { + return PyStatus_NoMemory(); } - for (size_t i = 0; i < Py_ARRAY_LENGTH(methods); ++i) { PyObject *func = PyCFunction_NewEx(&methods[i].def, NULL, NULL); - if (!func) { - return -1; + if (func == NULL) { + return PyStatus_NoMemory(); } - int res = PyCodec_RegisterError(methods[i].name, func); + int res = PyDict_SetItemString(interp->codecs.error_registry, + methods[i].name, func); Py_DECREF(func); - if (res) { - return -1; + if (res < 0) { + return PyStatus_Error("Failed to insert into codec error registry"); } } - mod = PyImport_ImportModule("encodings"); + interp->codecs.initialized = 1; + + // Importing `encodings' will call back into this module to register codec + // search functions, so this is done after everything else is initialized. + PyObject *mod = PyImport_ImportModule("encodings"); if (mod == NULL) { - return -1; + return PyStatus_Error("Failed to import encodings module"); } Py_DECREF(mod); - interp->codecs_initialized = 1; - return 0; + + return PyStatus_Ok(); +} + +void +_PyCodec_Fini(PyInterpreterState *interp) +{ + Py_CLEAR(interp->codecs.search_path); + Py_CLEAR(interp->codecs.search_cache); + Py_CLEAR(interp->codecs.error_registry); + interp->codecs.initialized = 0; } diff --git a/Python/pystate.c b/Python/pystate.c index 3d6e76e88bd731..f442d87ba3150e 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -843,9 +843,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) } PyConfig_Clear(&interp->config); - Py_CLEAR(interp->codec_search_path); - Py_CLEAR(interp->codec_search_cache); - Py_CLEAR(interp->codec_error_registry); + _PyCodec_Fini(interp); assert(interp->imports.modules == NULL); assert(interp->imports.modules_by_index == NULL); diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 87b695de23e25e..c4dbdcfe80bb4a 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -344,7 +344,7 @@ Python/ceval.c - _PyEval_BinaryOps - Python/ceval.c - _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS - Python/codecs.c - Py_hexdigits - Python/codecs.c - ucnhash_capi - -Python/codecs.c _PyCodecRegistry_Init methods - +Python/codecs.c _PyCodec_InitRegistry methods - Python/compile.c - NO_LABEL - Python/compile.c - NO_LOCATION - Python/dynload_shlib.c - _PyImport_DynLoadFiletab - From f201628073f22a785a096eccb010e2f78601b60f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 2 May 2024 18:51:43 -0600 Subject: [PATCH 176/217] gh-117953: Other Cleanups in the Extensions Machinery (gh-118206) This change will make some later changes simpler. --- Lib/test/test_import/__init__.py | 116 +++++++++++++++- Modules/_testsinglephase.c | 168 ++++++++++++++++++++++- Python/import.c | 228 ++++++++++++++++++------------- 3 files changed, 410 insertions(+), 102 deletions(-) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index f88b51f916cc81..b2aae774a12205 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -2285,6 +2285,107 @@ def test_disallowed_reimport(self): class TestSinglePhaseSnapshot(ModuleSnapshot): + """A representation of a single-phase init module for testing. + + Fields from ModuleSnapshot: + + * id - id(mod) + * module - mod or a SimpleNamespace with __file__ & __spec__ + * ns - a shallow copy of mod.__dict__ + * ns_id - id(mod.__dict__) + * cached - sys.modules[name] (or None if not there or not snapshotable) + * cached_id - id(sys.modules[name]) (or None if not there) + + Extra fields: + + * summed - the result of calling "mod.sum(1, 2)" + * lookedup - the result of calling "mod.look_up_self()" + * lookedup_id - the object ID of self.lookedup + * state_initialized - the result of calling "mod.state_initialized()" + * init_count - (optional) the result of calling "mod.initialized_count()" + + Overridden methods from ModuleSnapshot: + + * from_module() + * parse() + + Other methods from ModuleSnapshot: + + * build_script() + * from_subinterp() + + ---- + + There are 5 modules in Modules/_testsinglephase.c: + + * _testsinglephase + * has global state + * extra loads skip the init function, copy def.m_base.m_copy + * counts calls to init function + * _testsinglephase_basic_wrapper + * _testsinglephase by another name (and separate init function symbol) + * _testsinglephase_basic_copy + * same as _testsinglephase but with own def (and init func) + * _testsinglephase_with_reinit + * has no global or module state + * mod.state_initialized returns None + * an extra load in the main interpreter calls the cached init func + * an extra load in legacy subinterpreters does a full load + * _testsinglephase_with_state + * has module state + * an extra load in the main interpreter calls the cached init func + * an extra load in legacy subinterpreters does a full load + + (See Modules/_testsinglephase.c for more info.) + + For all those modules, the snapshot after the initial load (not in + the global extensions cache) would look like the following: + + * initial load + * id: ID of nww module object + * ns: exactly what the module init put there + * ns_id: ID of new module's __dict__ + * cached_id: same as self.id + * summed: 3 (never changes) + * lookedup_id: same as self.id + * state_initialized: a timestamp between the time of the load + and the time of the snapshot + * init_count: 1 (None for _testsinglephase_with_reinit) + + For the other scenarios it varies. + + For the _testsinglephase, _testsinglephase_basic_wrapper, and + _testsinglephase_basic_copy modules, the snapshot should look + like the following: + + * reloaded + * id: no change + * ns: matches what the module init function put there, + including the IDs of all contained objects, + plus any extra attributes added before the reload + * ns_id: no change + * cached_id: no change + * lookedup_id: no change + * state_initialized: no change + * init_count: no change + * already loaded + * (same as initial load except for ns and state_initialized) + * ns: matches the initial load, incl. IDs of contained objects + * state_initialized: no change from initial load + + For _testsinglephase_with_reinit: + + * reloaded: same as initial load (old module & ns is discarded) + * already loaded: same as initial load (old module & ns is discarded) + + For _testsinglephase_with_state: + + * reloaded + * (same as initial load (old module & ns is discarded), + except init_count) + * init_count: increase by 1 + * already loaded: same as reloaded + """ @classmethod def from_module(cls, mod): @@ -2901,17 +3002,18 @@ def test_basic_multiple_interpreters_deleted_no_reset(self): # * module's global state was initialized but cleared # Start with an interpreter that gets destroyed right away. - base = self.import_in_subinterp(postscript=''' - # Attrs set after loading are not in m_copy. - mod.spam = 'spam, spam, mash, spam, eggs, and spam' - ''') + base = self.import_in_subinterp( + postscript=''' + # Attrs set after loading are not in m_copy. + mod.spam = 'spam, spam, mash, spam, eggs, and spam' + ''') self.check_common(base) self.check_fresh(base) # At this point: # * alive in 0 interpreters # * module def in _PyRuntime.imports.extensions - # * mod init func ran again + # * mod init func ran for the first time (since reset) # * m_copy is NULL (claered when the interpreter was destroyed) # * module's global state was initialized, not reset @@ -2923,7 +3025,7 @@ def test_basic_multiple_interpreters_deleted_no_reset(self): # At this point: # * alive in 1 interpreter (interp1) # * module def still in _PyRuntime.imports.extensions - # * mod init func ran again + # * mod init func ran for the second time (since reset) # * m_copy was copied from interp1 (was NULL) # * module's global state was updated, not reset @@ -2935,7 +3037,7 @@ def test_basic_multiple_interpreters_deleted_no_reset(self): # At this point: # * alive in 2 interpreters (interp1, interp2) # * module def still in _PyRuntime.imports.extensions - # * mod init func ran again + # * mod init func did not run again # * m_copy was copied from interp2 (was from interp1) # * module's global state was updated, not reset diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c index 092673a9ea43e1..ff533e44a82730 100644 --- a/Modules/_testsinglephase.c +++ b/Modules/_testsinglephase.c @@ -1,6 +1,172 @@ /* Testing module for single-phase initialization of extension modules - */ + +This file contains 5 distinct modules, meaning each as its own name +and its own init function (PyInit_...). The default import system will +only find the one matching the filename: _testsinglephase. To load the +others you must do so manually. For example: + +```python +name = '_testsinglephase_base_wrapper' +filename = _testsinglephase.__file__ +loader = importlib.machinery.ExtensionFileLoader(name, filename) +spec = importlib.util.spec_from_file_location(name, filename, loader=loader) +mod = importlib._bootstrap._load(spec) +``` + +Here are the 5 modules: + +* _testsinglephase + * def: _testsinglephase_basic, + * m_name: "_testsinglephase" + * m_size: -1 + * state + * process-global + * initialized_count (default to -1; will never be 0) + * module (see module state below) + * module state: no + * initial __dict__: see common initial __dict__ below + * init function + 1. create module + 2. clear .module + 3. initialize .module: see module state below + 4. initialize module: set initial __dict__ + 5. increment .initialized_count + * functions + * (3 common, see below) + * initialized_count() - return .module.initialized_count + * import system + * caches + * global extensions cache: yes + * def.m_base.m_copy: yes + * def.m_base.m_init: no + * per-interpreter cache: yes (all single-phase init modules) + * load in main interpreter + * initial (not already in global cache) + 1. get init function from shared object file + 2. run init function + 3. copy __dict__ into def.m_base.m_copy + 4. set entry in global cache + 5. set entry in per-interpreter cache + 6. set entry in sys.modules + * reload (already in sys.modules) + 1. get def from global cache + 2. get module from sys.modules + 3. update module with contents of def.m_base.m_copy + * already loaded in other interpreter (already in global cache) + * same as reload, but create new module and update *it* + * not in any sys.modules, still in global cache + * same as already loaded + * load in legacy (non-isolated) interpreter + * same as main interpreter + * unload: never (all single-phase init modules) +* _testsinglephase_basic_wrapper + * identical to _testsinglephase except module name +* _testsinglephase_basic_copy + * def: static local variable in init function + * m_name: "_testsinglephase_basic_copy" + * m_size: -1 + * state: same as _testsinglephase + * init function: same as _testsinglephase + * functions: same as _testsinglephase + * import system: same as _testsinglephase +* _testsinglephase_with_reinit + * def: _testsinglephase_with_reinit, + * m_name: "_testsinglephase_with_reinit" + * m_size: 0 + * state + * process-global state: no + * module state: no + * initial __dict__: see common initial __dict__ below + * init function + 1. create module + 2. initialize temporary module state (local var): see module state below + 3. initialize module: set initial __dict__ + * functions: see common functions below + * import system + * caches + * global extensions cache: only if loaded in main interpreter + * def.m_base.m_copy: no + * def.m_base.m_init: only if loaded in the main interpreter + * per-interpreter cache: yes (all single-phase init modules) + * load in main interpreter + * initial (not already in global cache) + * (same as _testsinglephase except step 3) + 1. get init function from shared object file + 2. run init function + 3. set def.m_base.m_init to the init function + 4. set entry in global cache + 5. set entry in per-interpreter cache + 6. set entry in sys.modules + * reload (already in sys.modules) + 1. get def from global cache + 2. call def->m_base.m_init to get a new module object + 3. replace the existing module in sys.modules + * already loaded in other interpreter (already in global cache) + * same as reload (since will only be in cache for main interp) + * not in any sys.modules, still in global cache + * same as already loaded + * load in legacy (non-isolated) interpreter + * initial (not already in global cache) + * (same as main interpreter except skip steps 3 & 4 there) + 1. get init function from shared object file + 2. run init function + ... + 5. set entry in per-interpreter cache + 6. set entry in sys.modules + * reload (already in sys.modules) + * same as initial (load from scratch) + * already loaded in other interpreter (already in global cache) + * same as initial (load from scratch) + * not in any sys.modules, still in global cache + * same as initial (load from scratch) + * unload: never (all single-phase init modules) +* _testsinglephase_with_state + * def: _testsinglephase_with_state, + * m_name: "_testsinglephase_with_state" + * m_size: sizeof(module_state) + * state + * process-global: no + * module state: see module state below + * initial __dict__: see common initial __dict__ below + * init function + 1. create module + 3. initialize module state: see module state below + 4. initialize module: set initial __dict__ + 5. increment .initialized_count + * functions: see common functions below + * import system: same as _testsinglephase_basic_copy + +Module state: + +* fields + * initialized - when the module was first initialized + * *error + * *int_const + * *str_const +* initialization + 1. set state.initialized to the current time + 2. set state.error to a new exception class + 3. set state->int_const to int(1969) + 4. set state->str_const to "something different" + +Common initial __dict__: + +* error: state.error +* int_const: state.int_const +* str_const: state.str_const +* _module_initialized: state.initialized + +Common functions: + +* look_up_self() - return the module from the per-interpreter "by-index" cache +* sum() - return a + b +* state_initialized() - return state->initialized (or None if m_size == 0) + +See Python/import.c, especially the long comments, for more about +single-phase init modules. +*/ + #ifndef Py_BUILD_CORE_BUILTIN # define Py_BUILD_CORE_MODULE 1 #endif diff --git a/Python/import.c b/Python/import.c index 0c51ffc6285a9c..f120a3841b495d 100644 --- a/Python/import.c +++ b/Python/import.c @@ -645,33 +645,33 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) K. PyModule_CreateInitialized() -> PyModule_SetDocString() L. PyModule_CreateInitialized(): set mod->md_def M. : initialize the module, etc. - N. _PyImport_RunModInitFunc(): set def->m_base.m_init - O. import_run_extension() + N. import_run_extension() -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() - P. import_run_extension(): set __file__ - Q. import_run_extension() -> update_global_state_for_extension() - R. update_global_state_for_extension(): + O. import_run_extension(): set __file__ + P. import_run_extension() -> update_global_state_for_extension() + Q. update_global_state_for_extension(): copy __dict__ into def->m_base.m_copy - S. update_global_state_for_extension(): + R. update_global_state_for_extension(): add it to _PyRuntime.imports.extensions - T. import_run_extension() -> finish_singlephase_extension() - U. finish_singlephase_extension(): + S. import_run_extension() -> finish_singlephase_extension() + T. finish_singlephase_extension(): add it to interp->imports.modules_by_index - V. finish_singlephase_extension(): add it to sys.modules + U. finish_singlephase_extension(): add it to sys.modules Step (Q) is skipped for core modules (sys/builtins). (6). subsequent times (found in _PyRuntime.imports.extensions): A. _imp_create_dynamic_impl() -> import_find_extension() - B. import_find_extension() - -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() - C. import_find_extension() -> import_add_module() - D. if name in sys.modules: use that module - E. else: - 1. import_add_module() -> PyModule_NewObject() - 2. import_add_module(): set it on sys.modules - F. import_find_extension(): copy the "m_copy" dict into __dict__ - G. import_find_extension(): add to modules_by_index + B. import_find_extension() -> reload_singlephase_extension() + C. reload_singlephase_extension() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + D. reload_singlephase_extension() -> import_add_module() + E. if name in sys.modules: use that module + F. else: + 1. import_add_module() -> PyModule_NewObject() + 2. import_add_module(): set it on sys.modules + G. reload_singlephase_extension(): copy the "m_copy" dict into __dict__ + H. reload_singlephase_extension(): add to modules_by_index (10). (every time): A. noop @@ -681,21 +681,23 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) (6). not main interpreter and never loaded there - every time (not found in _PyRuntime.imports.extensions): A-P. (same as for m_size == -1) - Q-S. (skipped) - T-V. (same as for m_size == -1) + Q. _PyImport_RunModInitFunc(): set def->m_base.m_init + R. (skipped) + S-U. (same as for m_size == -1) (6). main interpreter - first time (not found in _PyRuntime.imports.extensions): - A-R. (same as for m_size == -1) - S. (skipped) - T-V. (same as for m_size == -1) + A-P. (same as for m_size == -1) + Q. _PyImport_RunModInitFunc(): set def->m_base.m_init + R-U. (same as for m_size == -1) (6). subsequent times (found in _PyRuntime.imports.extensions): A. _imp_create_dynamic_impl() -> import_find_extension() - B. import_find_extension() - -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() - C. import_find_extension(): call def->m_base.m_init (see above) - D. import_find_extension(): add the module to sys.modules - E. import_find_extension(): add to modules_by_index + B. import_find_extension() -> reload_singlephase_extension() + C. reload_singlephase_extension() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + D. reload_singlephase_extension(): call def->m_base.m_init (see above) + E. reload_singlephase_extension(): add the module to sys.modules + F. reload_singlephase_extension(): add to modules_by_index (10). every time: A. noop @@ -984,84 +986,103 @@ hashtable_destroy_str(void *ptr) #define HTSEP ':' -static PyModuleDef * -_extensions_cache_get(PyObject *filename, PyObject *name) -{ - PyModuleDef *def = NULL; - void *key = NULL; - extensions_lock_acquire(); - +static int +_extensions_cache_init(void) +{ + _Py_hashtable_allocator_t alloc = {PyMem_RawMalloc, PyMem_RawFree}; + EXTENSIONS.hashtable = _Py_hashtable_new_full( + hashtable_hash_str, + hashtable_compare_str, + hashtable_destroy_str, // key + /* There's no need to decref the def since it's immortal. */ + NULL, // value + &alloc + ); if (EXTENSIONS.hashtable == NULL) { - goto finally; + PyErr_NoMemory(); + return -1; } + return 0; +} - key = hashtable_key_from_2_strings(filename, name, HTSEP); +static _Py_hashtable_entry_t * +_extensions_cache_find_unlocked(PyObject *path, PyObject *name, + void **p_key) +{ + if (EXTENSIONS.hashtable == NULL) { + return NULL; + } + void *key = hashtable_key_from_2_strings(path, name, HTSEP); if (key == NULL) { - goto finally; + return NULL; + } + _Py_hashtable_entry_t *entry = + _Py_hashtable_get_entry(EXTENSIONS.hashtable, key); + if (p_key != NULL) { + *p_key = key; } - _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry( - EXTENSIONS.hashtable, key); + else { + hashtable_destroy_str(key); + } + return entry; +} + +static PyModuleDef * +_extensions_cache_get(PyObject *path, PyObject *name) +{ + PyModuleDef *def = NULL; + extensions_lock_acquire(); + + _Py_hashtable_entry_t *entry = + _extensions_cache_find_unlocked(path, name, NULL); if (entry == NULL) { + /* It was never added. */ goto finally; } def = (PyModuleDef *)entry->value; finally: extensions_lock_release(); - if (key != NULL) { - PyMem_RawFree(key); - } return def; } static int -_extensions_cache_set(PyObject *filename, PyObject *name, PyModuleDef *def) +_extensions_cache_set(PyObject *path, PyObject *name, PyModuleDef *def) { int res = -1; + assert(def != NULL); extensions_lock_acquire(); if (EXTENSIONS.hashtable == NULL) { - _Py_hashtable_allocator_t alloc = {PyMem_RawMalloc, PyMem_RawFree}; - EXTENSIONS.hashtable = _Py_hashtable_new_full( - hashtable_hash_str, - hashtable_compare_str, - hashtable_destroy_str, // key - /* There's no need to decref the def since it's immortal. */ - NULL, // value - &alloc - ); - if (EXTENSIONS.hashtable == NULL) { - PyErr_NoMemory(); + if (_extensions_cache_init() < 0) { goto finally; } } - void *key = hashtable_key_from_2_strings(filename, name, HTSEP); - if (key == NULL) { - goto finally; - } - int already_set = 0; - _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry( - EXTENSIONS.hashtable, key); + void *key = NULL; + _Py_hashtable_entry_t *entry = + _extensions_cache_find_unlocked(path, name, &key); if (entry == NULL) { + /* It was never added. */ if (_Py_hashtable_set(EXTENSIONS.hashtable, key, def) < 0) { - PyMem_RawFree(key); PyErr_NoMemory(); goto finally; } + /* The hashtable owns the key now. */ + key = NULL; + } + else if (entry->value == NULL) { + /* It was previously deleted. */ + entry->value = def; } else { - if (entry->value == NULL) { - entry->value = def; - } - else { - /* We expect it to be static, so it must be the same pointer. */ - assert((PyModuleDef *)entry->value == def); - already_set = 1; - } - PyMem_RawFree(key); + /* We expect it to be static, so it must be the same pointer. */ + assert((PyModuleDef *)entry->value == def); + /* It was already added. */ + already_set = 1; } + if (!already_set) { /* We assume that all module defs are statically allocated and will never be freed. Otherwise, we would incref here. */ @@ -1071,13 +1092,15 @@ _extensions_cache_set(PyObject *filename, PyObject *name, PyModuleDef *def) finally: extensions_lock_release(); + if (key != NULL) { + hashtable_destroy_str(key); + } return res; } static void -_extensions_cache_delete(PyObject *filename, PyObject *name) +_extensions_cache_delete(PyObject *path, PyObject *name) { - void *key = NULL; extensions_lock_acquire(); if (EXTENSIONS.hashtable == NULL) { @@ -1085,13 +1108,8 @@ _extensions_cache_delete(PyObject *filename, PyObject *name) goto finally; } - key = hashtable_key_from_2_strings(filename, name, HTSEP); - if (key == NULL) { - goto finally; - } - - _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry( - EXTENSIONS.hashtable, key); + _Py_hashtable_entry_t *entry = + _extensions_cache_find_unlocked(path, name, NULL); if (entry == NULL) { /* It was never added. */ goto finally; @@ -1109,9 +1127,6 @@ _extensions_cache_delete(PyObject *filename, PyObject *name) finally: extensions_lock_release(); - if (key != NULL) { - PyMem_RawFree(key); - } } static void @@ -1359,15 +1374,11 @@ finish_singlephase_extension(PyThreadState *tstate, static PyObject * -import_find_extension(PyThreadState *tstate, - struct _Py_ext_module_loader_info *info) +reload_singlephase_extension(PyThreadState *tstate, PyModuleDef *def, + struct _Py_ext_module_loader_info *info) { - /* Only single-phase init modules will be in the cache. */ - PyModuleDef *def = _extensions_cache_get(info->path, info->name); - if (def == NULL) { - return NULL; - } assert_singlephase(def); + PyObject *mod = NULL; /* It may have been successfully imported previously in an interpreter that allows legacy modules @@ -1378,9 +1389,7 @@ import_find_extension(PyThreadState *tstate, return NULL; } - PyObject *mod, *mdict; PyObject *modules = get_modules_dict(tstate, true); - if (def->m_size == -1) { PyObject *m_copy = def->m_base.m_copy; /* Module does not support repeated initialization */ @@ -1390,6 +1399,7 @@ import_find_extension(PyThreadState *tstate, m_copy = get_core_module_dict( tstate->interp, info->name, info->path); if (m_copy == NULL) { + assert(!PyErr_Occurred()); return NULL; } } @@ -1397,7 +1407,7 @@ import_find_extension(PyThreadState *tstate, if (mod == NULL) { return NULL; } - mdict = PyModule_GetDict(mod); + PyObject *mdict = PyModule_GetDict(mod); if (mdict == NULL) { Py_DECREF(mod); return NULL; @@ -1416,6 +1426,7 @@ import_find_extension(PyThreadState *tstate, } else { if (def->m_base.m_init == NULL) { + assert(!PyErr_Occurred()); return NULL; } struct _Py_ext_module_loader_result res; @@ -1445,12 +1456,41 @@ import_find_extension(PyThreadState *tstate, return NULL; } } + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { PyMapping_DelItem(modules, info->name); Py_DECREF(mod); return NULL; } + return mod; +} + +static PyObject * +import_find_extension(PyThreadState *tstate, + struct _Py_ext_module_loader_info *info) +{ + /* Only single-phase init modules will be in the cache. */ + PyModuleDef *def = _extensions_cache_get(info->path, info->name); + if (def == NULL) { + return NULL; + } + assert_singlephase(def); + + /* It may have been successfully imported previously + in an interpreter that allows legacy modules + but is not allowed in the current interpreter. */ + const char *name_buf = PyUnicode_AsUTF8(info->name); + assert(name_buf != NULL); + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + return NULL; + } + + PyObject *mod = reload_singlephase_extension(tstate, def, info); + if (mod == NULL) { + return NULL; + } + int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; if (verbose) { PySys_FormatStderr("import %U # previously loaded (%R)\n", From 852263e1086748492602a90347ecc0a3925e1dda Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Fri, 3 May 2024 15:02:11 +0200 Subject: [PATCH 177/217] gh-117492: Clarify documentation of `typing.Never` (#117678) Co-authored-by: Jelle Zijlstra Co-authored-by: Erlend E. Aasland --- Doc/library/typing.rst | 46 ++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index d816e6368f40d2..573318bd76576b 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -852,14 +852,25 @@ using ``[]``. .. versionadded:: 3.11 .. data:: Never + NoReturn - The `bottom type `_, + :data:`!Never` and :data:`!NoReturn` represent the + `bottom type `_, a type that has no members. - This can be used to define a function that should never be - called, or a function that never returns:: + They can be used to indicate that a function never returns, + such as :func:`sys.exit`:: - from typing import Never + from typing import Never # or NoReturn + + def stop() -> Never: + raise RuntimeError('no way') + + Or to define a function that should never be + called, as there are no valid arguments, such as + :func:`assert_never`:: + + from typing import Never # or NoReturn def never_call_me(arg: Never) -> None: pass @@ -872,31 +883,18 @@ using ``[]``. case str(): print("It's a str") case _: - never_call_me(arg) # OK, arg is of type Never - - .. versionadded:: 3.11 - - On older Python versions, :data:`NoReturn` may be used to express the - same concept. ``Never`` was added to make the intended meaning more explicit. + never_call_me(arg) # OK, arg is of type Never (or NoReturn) -.. data:: NoReturn + :data:`!Never` and :data:`!NoReturn` have the same meaning in the type system + and static type checkers treat both equivalently. - Special type indicating that a function never returns. - - For example:: + .. versionadded:: 3.6.2 - from typing import NoReturn + Added :data:`NoReturn`. - def stop() -> NoReturn: - raise RuntimeError('no way') - - ``NoReturn`` can also be used as a - `bottom type `_, a type that - has no values. Starting in Python 3.11, the :data:`Never` type should - be used for this concept instead. Type checkers should treat the two - equivalently. + .. versionadded:: 3.11 - .. versionadded:: 3.6.2 + Added :data:`Never`. .. data:: Self From ca269e58c290be8ca11bb728004ea842d9f85e3a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 3 May 2024 06:17:32 -0700 Subject: [PATCH 178/217] gh-116126: Implement PEP 696 (#116129) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alex Waygood Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- Doc/library/ast.rst | 52 +- Doc/library/typing.rst | 82 +- Doc/reference/compound_stmts.rst | 31 +- Doc/reference/executionmodel.rst | 8 +- Doc/whatsnew/3.13.rst | 6 + Grammar/python.gram | 10 +- Include/internal/pycore_ast.h | 20 +- Include/internal/pycore_ast_state.h | 1 + Include/internal/pycore_intrinsics.h | 3 +- Include/internal/pycore_typevarobject.h | 2 + Lib/ast.py | 9 + Lib/test/test_ast.py | 42 +- Lib/test/test_type_params.py | 114 +- Lib/test/test_typing.py | 190 +++- Lib/test/test_unparse.py | 42 + Lib/typing.py | 95 +- ...-02-29-18-55-45.gh-issue-116129.wsFnIq.rst | 2 + Modules/_typingmodule.c | 3 + Objects/clinic/typevarobject.c.h | 170 ++- Objects/typevarobject.c | 327 +++++- Parser/Python.asdl | 6 +- Parser/parser.c | 991 ++++++++++-------- Python/Python-ast.c | 178 +++- Python/ast.c | 12 +- Python/compile.c | 89 +- Python/intrinsics.c | 1 + Python/symtable.c | 59 +- Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 + 28 files changed, 1924 insertions(+), 623 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-02-29-18-55-45.gh-issue-116129.wsFnIq.rst diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 09f2a404786fb6..e954c38c7c55b5 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1748,15 +1748,17 @@ Type parameters :ref:`Type parameters ` can exist on classes, functions, and type aliases. -.. class:: TypeVar(name, bound) +.. class:: TypeVar(name, bound, default_value) - A :class:`typing.TypeVar`. ``name`` is the name of the type variable. - ``bound`` is the bound or constraints, if any. If ``bound`` is a :class:`Tuple`, - it represents constraints; otherwise it represents the bound. + A :class:`typing.TypeVar`. *name* is the name of the type variable. + *bound* is the bound or constraints, if any. If *bound* is a :class:`Tuple`, + it represents constraints; otherwise it represents the bound. *default_value* + is the default value; if the :class:`!TypeVar` has no default, this + attribute will be set to ``None``. .. doctest:: - >>> print(ast.dump(ast.parse("type Alias[T: int] = list[T]"), indent=4)) + >>> print(ast.dump(ast.parse("type Alias[T: int = bool] = list[T]"), indent=4)) Module( body=[ TypeAlias( @@ -1764,7 +1766,8 @@ aliases. type_params=[ TypeVar( name='T', - bound=Name(id='int', ctx=Load()))], + bound=Name(id='int', ctx=Load()), + default_value=Name(id='bool', ctx=Load()))], value=Subscript( value=Name(id='list', ctx=Load()), slice=Name(id='T', ctx=Load()), @@ -1772,19 +1775,30 @@ aliases. .. versionadded:: 3.12 -.. class:: ParamSpec(name) + .. versionchanged:: 3.13 + Added the *default_value* parameter. + +.. class:: ParamSpec(name, default_value) - A :class:`typing.ParamSpec`. ``name`` is the name of the parameter specification. + A :class:`typing.ParamSpec`. *name* is the name of the parameter specification. + *default_value* is the default value; if the :class:`!ParamSpec` has no default, + this attribute will be set to ``None``. .. doctest:: - >>> print(ast.dump(ast.parse("type Alias[**P] = Callable[P, int]"), indent=4)) + >>> print(ast.dump(ast.parse("type Alias[**P = (int, str)] = Callable[P, int]"), indent=4)) Module( body=[ TypeAlias( name=Name(id='Alias', ctx=Store()), type_params=[ - ParamSpec(name='P')], + ParamSpec( + name='P', + default_value=Tuple( + elts=[ + Name(id='int', ctx=Load()), + Name(id='str', ctx=Load())], + ctx=Load()))], value=Subscript( value=Name(id='Callable', ctx=Load()), slice=Tuple( @@ -1796,19 +1810,26 @@ aliases. .. versionadded:: 3.12 -.. class:: TypeVarTuple(name) + .. versionchanged:: 3.13 + Added the *default_value* parameter. + +.. class:: TypeVarTuple(name, default_value) - A :class:`typing.TypeVarTuple`. ``name`` is the name of the type variable tuple. + A :class:`typing.TypeVarTuple`. *name* is the name of the type variable tuple. + *default_value* is the default value; if the :class:`!TypeVarTuple` has no + default, this attribute will be set to ``None``. .. doctest:: - >>> print(ast.dump(ast.parse("type Alias[*Ts] = tuple[*Ts]"), indent=4)) + >>> print(ast.dump(ast.parse("type Alias[*Ts = ()] = tuple[*Ts]"), indent=4)) Module( body=[ TypeAlias( name=Name(id='Alias', ctx=Store()), type_params=[ - TypeVarTuple(name='Ts')], + TypeVarTuple( + name='Ts', + default_value=Tuple(ctx=Load()))], value=Subscript( value=Name(id='tuple', ctx=Load()), slice=Tuple( @@ -1821,6 +1842,9 @@ aliases. .. versionadded:: 3.12 + .. versionchanged:: 3.13 + Added the *default_value* parameter. + Function and class definitions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 573318bd76576b..e06287250641e6 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1614,7 +1614,7 @@ without the dedicated syntax, as documented below. .. _typevar: -.. class:: TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False, infer_variance=False) +.. class:: TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False, infer_variance=False, default=typing.NoDefault) Type variable. @@ -1752,15 +1752,35 @@ without the dedicated syntax, as documented below. the constraints are evaluated only when the attribute is accessed, not when the type variable is created (see :ref:`lazy-evaluation`). + .. attribute:: __default__ + + The default value of the type variable, or :data:`typing.NoDefault` if it + has no default. + + .. versionadded:: 3.13 + + .. method:: has_default() + + Return whether or not the type variable has a default value. This is equivalent + to checking whether :attr:`__default__` is not the :data:`typing.NoDefault` + singleton, except that it does not force evaluation of the + :ref:`lazily evaluated ` default value. + + .. versionadded:: 3.13 + .. versionchanged:: 3.12 Type variables can now be declared using the :ref:`type parameter ` syntax introduced by :pep:`695`. The ``infer_variance`` parameter was added. + .. versionchanged:: 3.13 + + Support for default values was added. + .. _typevartuple: -.. class:: TypeVarTuple(name) +.. class:: TypeVarTuple(name, default=typing.NoDefault) Type variable tuple. A specialized form of :ref:`type variable ` that enables *variadic* generics. @@ -1870,6 +1890,22 @@ without the dedicated syntax, as documented below. The name of the type variable tuple. + .. attribute:: __default__ + + The default value of the type variable tuple, or :data:`typing.NoDefault` if it + has no default. + + .. versionadded:: 3.13 + + .. method:: has_default() + + Return whether or not the type variable tuple has a default value. This is equivalent + to checking whether :attr:`__default__` is not the :data:`typing.NoDefault` + singleton, except that it does not force evaluation of the + :ref:`lazily evaluated ` default value. + + .. versionadded:: 3.13 + .. versionadded:: 3.11 .. versionchanged:: 3.12 @@ -1877,7 +1913,11 @@ without the dedicated syntax, as documented below. Type variable tuples can now be declared using the :ref:`type parameter ` syntax introduced by :pep:`695`. -.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False) + .. versionchanged:: 3.13 + + Support for default values was added. + +.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False, default=typing.NoDefault) Parameter specification variable. A specialized version of :ref:`type variables `. @@ -1946,6 +1986,22 @@ without the dedicated syntax, as documented below. The name of the parameter specification. + .. attribute:: __default__ + + The default value of the parameter specification, or :data:`typing.NoDefault` if it + has no default. + + .. versionadded:: 3.13 + + .. method:: has_default() + + Return whether or not the parameter specification has a default value. This is equivalent + to checking whether :attr:`__default__` is not the :data:`typing.NoDefault` + singleton, except that it does not force evaluation of the + :ref:`lazily evaluated ` default value. + + .. versionadded:: 3.13 + Parameter specification variables created with ``covariant=True`` or ``contravariant=True`` can be used to declare covariant or contravariant generic types. The ``bound`` argument is also accepted, similar to @@ -1959,6 +2015,10 @@ without the dedicated syntax, as documented below. Parameter specifications can now be declared using the :ref:`type parameter ` syntax introduced by :pep:`695`. + .. versionchanged:: 3.13 + + Support for default values was added. + .. note:: Only parameter specification variables defined in global scope can be pickled. @@ -3171,6 +3231,22 @@ Introspection helpers .. versionadded:: 3.7.4 +.. data:: NoDefault + + A sentinel object used to indicate that a type parameter has no default + value. For example: + + .. doctest:: + + >>> T = TypeVar("T") + >>> T.__default__ is typing.NoDefault + True + >>> S = TypeVar("S", default=None) + >>> S.__default__ is None + True + + .. versionadded:: 3.13 + Constant -------- diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 374404bf33abbe..42cca0664df71d 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1620,15 +1620,18 @@ Type parameter lists .. versionadded:: 3.12 +.. versionchanged:: 3.13 + Support for default values was added (see :pep:`696`). + .. index:: single: type parameters .. productionlist:: python-grammar type_params: "[" `type_param` ("," `type_param`)* "]" type_param: `typevar` | `typevartuple` | `paramspec` - typevar: `identifier` (":" `expression`)? - typevartuple: "*" `identifier` - paramspec: "**" `identifier` + typevar: `identifier` (":" `expression`)? ("=" `expression`)? + typevartuple: "*" `identifier` ("=" `expression`)? + paramspec: "**" `identifier` ("=" `expression`)? :ref:`Functions ` (including :ref:`coroutines `), :ref:`classes ` and :ref:`type aliases ` may @@ -1694,19 +1697,31 @@ evaluated in a separate :ref:`annotation scope `. :data:`typing.TypeVarTuple`\ s and :data:`typing.ParamSpec`\ s cannot have bounds or constraints. +All three flavors of type parameters can also have a *default value*, which is used +when the type parameter is not explicitly provided. This is added by appending +a single equals sign (``=``) followed by an expression. Like the bounds and +constraints of type variables, the default value is not evaluated when the +object is created, but only when the type parameter's ``__default__`` attribute +is accessed. To this end, the default value is evaluated in a separate +:ref:`annotation scope `. If no default value is specified +for a type parameter, the ``__default__`` attribute is set to the special +sentinel object :data:`typing.NoDefault`. + The following example indicates the full set of allowed type parameter declarations:: def overly_generic[ SimpleTypeVar, + TypeVarWithDefault = int, TypeVarWithBound: int, TypeVarWithConstraints: (str, bytes), - *SimpleTypeVarTuple, - **SimpleParamSpec, + *SimpleTypeVarTuple = (int, float), + **SimpleParamSpec = (str, bytearray), ]( a: SimpleTypeVar, - b: TypeVarWithBound, - c: Callable[SimpleParamSpec, TypeVarWithConstraints], - *d: SimpleTypeVarTuple, + b: TypeVarWithDefault, + c: TypeVarWithBound, + d: Callable[SimpleParamSpec, TypeVarWithConstraints], + *e: SimpleTypeVarTuple, ): ... .. _generic-functions: diff --git a/Doc/reference/executionmodel.rst b/Doc/reference/executionmodel.rst index ed50faed6c940d..f24e1537af39ed 100644 --- a/Doc/reference/executionmodel.rst +++ b/Doc/reference/executionmodel.rst @@ -205,7 +205,7 @@ Annotation scopes are used in the following contexts: * Type parameter lists for :ref:`generic classes `. A generic class's base classes and keyword arguments are executed within the annotation scope, but its decorators are not. -* The bounds and constraints for type variables +* The bounds, constraints, and default values for type parameters (:ref:`lazily evaluated `). * The value of type aliases (:ref:`lazily evaluated `). @@ -232,13 +232,17 @@ Annotation scopes differ from function scopes in the following ways: .. versionadded:: 3.12 Annotation scopes were introduced in Python 3.12 as part of :pep:`695`. +.. versionchanged:: 3.13 + Annotation scopes are also used for type parameter defaults, as + introduced by :pep:`696`. + .. _lazy-evaluation: Lazy evaluation --------------- The values of type aliases created through the :keyword:`type` statement are -*lazily evaluated*. The same applies to the bounds and constraints of type +*lazily evaluated*. The same applies to the bounds, constraints, and default values of type variables created through the :ref:`type parameter syntax `. This means that they are not evaluated when the type alias or type variable is created. Instead, they are only evaluated when doing so is necessary to resolve diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index d59c4ee22e67d3..d996cf6c4d9b52 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -89,6 +89,8 @@ Interpreter improvements: New typing features: +* :pep:`696`: Type parameters (:data:`typing.TypeVar`, :data:`typing.ParamSpec`, + and :data:`typing.TypeVarTuple`) now support defaults. * :pep:`742`: :data:`typing.TypeIs` was added, providing more intuitive type narrowing behavior. @@ -850,6 +852,10 @@ typing an item of a :class:`typing.TypedDict` as read-only for type checkers. See :pep:`705` for more details. +* Add :data:`typing.NoDefault`, a sentinel object used to represent the defaults + of some parameters in the :mod:`typing` module. (Contributed by Jelle Zijlstra in + :gh:`116126`.) + unicodedata ----------- diff --git a/Grammar/python.gram b/Grammar/python.gram index 05d7837e3aa6db..1c1c53c4b73ace 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -647,21 +647,25 @@ type_params[asdl_type_param_seq*]: '[' t=type_param_seq ']' { type_param_seq[asdl_type_param_seq*]: a[asdl_type_param_seq*]=','.type_param+ [','] { a } type_param[type_param_ty] (memo): - | a=NAME b=[type_param_bound] { _PyAST_TypeVar(a->v.Name.id, b, EXTRA) } + | a=NAME b=[type_param_bound] c=[type_param_default] { _PyAST_TypeVar(a->v.Name.id, b, c, EXTRA) } | '*' a=NAME colon=':' e=expression { RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind ? "cannot use constraints with TypeVarTuple" : "cannot use bound with TypeVarTuple") } - | '*' a=NAME { _PyAST_TypeVarTuple(a->v.Name.id, EXTRA) } + | '*' a=NAME b=[type_param_starred_default] { _PyAST_TypeVarTuple(a->v.Name.id, b, EXTRA) } | '**' a=NAME colon=':' e=expression { RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind ? "cannot use constraints with ParamSpec" : "cannot use bound with ParamSpec") } - | '**' a=NAME { _PyAST_ParamSpec(a->v.Name.id, EXTRA) } + | '**' a=NAME b=[type_param_default] { _PyAST_ParamSpec(a->v.Name.id, b, EXTRA) } type_param_bound[expr_ty]: ':' e=expression { e } +type_param_default[expr_ty]: '=' e=expression { + CHECK_VERSION(expr_ty, 13, "Type parameter defaults are", e) } +type_param_starred_default[expr_ty]: '=' e=star_expression { + CHECK_VERSION(expr_ty, 13, "Type parameter defaults are", e) } # EXPRESSIONS # ----------- diff --git a/Include/internal/pycore_ast.h b/Include/internal/pycore_ast.h index f222d485e0b54b..f5bf1205a82be9 100644 --- a/Include/internal/pycore_ast.h +++ b/Include/internal/pycore_ast.h @@ -657,14 +657,17 @@ struct _type_param { struct { identifier name; expr_ty bound; + expr_ty default_value; } TypeVar; struct { identifier name; + expr_ty default_value; } ParamSpec; struct { identifier name; + expr_ty default_value; } TypeVarTuple; } v; @@ -892,14 +895,15 @@ pattern_ty _PyAST_MatchOr(asdl_pattern_seq * patterns, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena); type_ignore_ty _PyAST_TypeIgnore(int lineno, string tag, PyArena *arena); -type_param_ty _PyAST_TypeVar(identifier name, expr_ty bound, int lineno, int - col_offset, int end_lineno, int end_col_offset, - PyArena *arena); -type_param_ty _PyAST_ParamSpec(identifier name, int lineno, int col_offset, int - end_lineno, int end_col_offset, PyArena *arena); -type_param_ty _PyAST_TypeVarTuple(identifier name, int lineno, int col_offset, - int end_lineno, int end_col_offset, PyArena - *arena); +type_param_ty _PyAST_TypeVar(identifier name, expr_ty bound, expr_ty + default_value, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +type_param_ty _PyAST_ParamSpec(identifier name, expr_ty default_value, int + lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +type_param_ty _PyAST_TypeVarTuple(identifier name, expr_ty default_value, int + lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); PyObject* PyAST_mod2obj(mod_ty t); diff --git a/Include/internal/pycore_ast_state.h b/Include/internal/pycore_ast_state.h index f1b1786264803b..09ae95465495c0 100644 --- a/Include/internal/pycore_ast_state.h +++ b/Include/internal/pycore_ast_state.h @@ -184,6 +184,7 @@ struct ast_state { PyObject *conversion; PyObject *ctx; PyObject *decorator_list; + PyObject *default_value; PyObject *defaults; PyObject *elt; PyObject *elts; diff --git a/Include/internal/pycore_intrinsics.h b/Include/internal/pycore_intrinsics.h index 8fa88ea3f74caa..39c2a30f6e979d 100644 --- a/Include/internal/pycore_intrinsics.h +++ b/Include/internal/pycore_intrinsics.h @@ -28,8 +28,9 @@ #define INTRINSIC_TYPEVAR_WITH_BOUND 2 #define INTRINSIC_TYPEVAR_WITH_CONSTRAINTS 3 #define INTRINSIC_SET_FUNCTION_TYPE_PARAMS 4 +#define INTRINSIC_SET_TYPEPARAM_DEFAULT 5 -#define MAX_INTRINSIC_2 4 +#define MAX_INTRINSIC_2 5 typedef PyObject *(*intrinsic_func1)(PyThreadState* tstate, PyObject *value); typedef PyObject *(*intrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2); diff --git a/Include/internal/pycore_typevarobject.h b/Include/internal/pycore_typevarobject.h index c9fa97d6820757..80a2daf4efc16a 100644 --- a/Include/internal/pycore_typevarobject.h +++ b/Include/internal/pycore_typevarobject.h @@ -13,10 +13,12 @@ extern PyObject *_Py_make_paramspec(PyThreadState *, PyObject *); extern PyObject *_Py_make_typevartuple(PyThreadState *, PyObject *); extern PyObject *_Py_make_typealias(PyThreadState *, PyObject *); extern PyObject *_Py_subscript_generic(PyThreadState *, PyObject *); +extern PyObject *_Py_set_typeparam_default(PyThreadState *, PyObject *, PyObject *); extern int _Py_initialize_generic(PyInterpreterState *); extern void _Py_clear_generic_types(PyInterpreterState *); extern PyTypeObject _PyTypeAlias_Type; +extern PyObject _Py_NoDefaultStruct; #ifdef __cplusplus } diff --git a/Lib/ast.py b/Lib/ast.py index 9f386051659e76..d7e51aba595706 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -1124,12 +1124,21 @@ def visit_TypeVar(self, node): if node.bound: self.write(": ") self.traverse(node.bound) + if node.default_value: + self.write(" = ") + self.traverse(node.default_value) def visit_TypeVarTuple(self, node): self.write("*" + node.name) + if node.default_value: + self.write(" = ") + self.traverse(node.default_value) def visit_ParamSpec(self, node): self.write("**" + node.name) + if node.default_value: + self.write(" = ") + self.traverse(node.default_value) def visit_TypeAlias(self, node): self.fill("type ") diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 68024304dfb746..6d05c8f8f47c50 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -195,16 +195,19 @@ def to_tuple(t): "type X[T, *Ts, **P] = (T, Ts, P)", "type X[T: int, *Ts, **P] = (T, Ts, P)", "type X[T: (int, str), *Ts, **P] = (T, Ts, P)", + "type X[T: int = 1, *Ts = 2, **P =3] = (T, Ts, P)", # Generic classes "class X[T]: pass", "class X[T, *Ts, **P]: pass", "class X[T: int, *Ts, **P]: pass", "class X[T: (int, str), *Ts, **P]: pass", + "class X[T: int = 1, *Ts = 2, **P = 3]: pass", # Generic functions "def f[T](): pass", "def f[T, *Ts, **P](): pass", "def f[T: int, *Ts, **P](): pass", "def f[T: (int, str), *Ts, **P](): pass", + "def f[T: int = 1, *Ts = 2, **P = 3](): pass", ] # These are compiled through "single" @@ -1108,6 +1111,18 @@ def test_type_params_feature_version(self): with self.assertRaises(SyntaxError): ast.parse(sample, feature_version=(3, 11)) + def test_type_params_default_feature_version(self): + samples = [ + "type X[*Ts=int] = int", + "class X[T=int]: pass", + "def f[**P=int](): pass", + ] + for sample in samples: + with self.subTest(sample): + ast.parse(sample) + with self.assertRaises(SyntaxError): + ast.parse(sample, feature_version=(3, 12)) + def test_invalid_major_feature_version(self): with self.assertRaises(ValueError): ast.parse('pass', feature_version=(2, 7)) @@ -3261,18 +3276,21 @@ def main(): ('Module', [('FunctionDef', (1, 0, 1, 42), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], ('arg', (1, 29, 1, 35), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 38, 1, 42))], [], None, None, [])], []), ('Module', [('FunctionDef', (1, 0, 1, 40), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], ('arg', (1, 27, 1, 33), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 36, 1, 40))], [], None, None, [])], []), ('Module', [('TypeAlias', (1, 0, 1, 12), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [], ('Name', (1, 9, 1, 12), 'int', ('Load',)))], []), -('Module', [('TypeAlias', (1, 0, 1, 15), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None)], ('Name', (1, 12, 1, 15), 'int', ('Load',)))], []), -('Module', [('TypeAlias', (1, 0, 1, 32), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None), ('TypeVarTuple', (1, 10, 1, 13), 'Ts'), ('ParamSpec', (1, 15, 1, 18), 'P')], ('Tuple', (1, 22, 1, 32), [('Name', (1, 23, 1, 24), 'T', ('Load',)), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Name', (1, 30, 1, 31), 'P', ('Load',))], ('Load',)))], []), -('Module', [('TypeAlias', (1, 0, 1, 37), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 13), 'T', ('Name', (1, 10, 1, 13), 'int', ('Load',))), ('TypeVarTuple', (1, 15, 1, 18), 'Ts'), ('ParamSpec', (1, 20, 1, 23), 'P')], ('Tuple', (1, 27, 1, 37), [('Name', (1, 28, 1, 29), 'T', ('Load',)), ('Name', (1, 31, 1, 33), 'Ts', ('Load',)), ('Name', (1, 35, 1, 36), 'P', ('Load',))], ('Load',)))], []), -('Module', [('TypeAlias', (1, 0, 1, 44), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 20), 'T', ('Tuple', (1, 10, 1, 20), [('Name', (1, 11, 1, 14), 'int', ('Load',)), ('Name', (1, 16, 1, 19), 'str', ('Load',))], ('Load',))), ('TypeVarTuple', (1, 22, 1, 25), 'Ts'), ('ParamSpec', (1, 27, 1, 30), 'P')], ('Tuple', (1, 34, 1, 44), [('Name', (1, 35, 1, 36), 'T', ('Load',)), ('Name', (1, 38, 1, 40), 'Ts', ('Load',)), ('Name', (1, 42, 1, 43), 'P', ('Load',))], ('Load',)))], []), -('Module', [('ClassDef', (1, 0, 1, 16), 'X', [], [], [('Pass', (1, 12, 1, 16))], [], [('TypeVar', (1, 8, 1, 9), 'T', None)])], []), -('Module', [('ClassDef', (1, 0, 1, 26), 'X', [], [], [('Pass', (1, 22, 1, 26))], [], [('TypeVar', (1, 8, 1, 9), 'T', None), ('TypeVarTuple', (1, 11, 1, 14), 'Ts'), ('ParamSpec', (1, 16, 1, 19), 'P')])], []), -('Module', [('ClassDef', (1, 0, 1, 31), 'X', [], [], [('Pass', (1, 27, 1, 31))], [], [('TypeVar', (1, 8, 1, 14), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',))), ('TypeVarTuple', (1, 16, 1, 19), 'Ts'), ('ParamSpec', (1, 21, 1, 24), 'P')])], []), -('Module', [('ClassDef', (1, 0, 1, 38), 'X', [], [], [('Pass', (1, 34, 1, 38))], [], [('TypeVar', (1, 8, 1, 21), 'T', ('Tuple', (1, 11, 1, 21), [('Name', (1, 12, 1, 15), 'int', ('Load',)), ('Name', (1, 17, 1, 20), 'str', ('Load',))], ('Load',))), ('TypeVarTuple', (1, 23, 1, 26), 'Ts'), ('ParamSpec', (1, 28, 1, 31), 'P')])], []), -('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 12, 1, 16))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None)])], []), -('Module', [('FunctionDef', (1, 0, 1, 26), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None), ('TypeVarTuple', (1, 9, 1, 12), 'Ts'), ('ParamSpec', (1, 14, 1, 17), 'P')])], []), -('Module', [('FunctionDef', (1, 0, 1, 31), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 27, 1, 31))], [], None, None, [('TypeVar', (1, 6, 1, 12), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',))), ('TypeVarTuple', (1, 14, 1, 17), 'Ts'), ('ParamSpec', (1, 19, 1, 22), 'P')])], []), -('Module', [('FunctionDef', (1, 0, 1, 38), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 34, 1, 38))], [], None, None, [('TypeVar', (1, 6, 1, 19), 'T', ('Tuple', (1, 9, 1, 19), [('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Name', (1, 15, 1, 18), 'str', ('Load',))], ('Load',))), ('TypeVarTuple', (1, 21, 1, 24), 'Ts'), ('ParamSpec', (1, 26, 1, 29), 'P')])], []), +('Module', [('TypeAlias', (1, 0, 1, 15), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None, None)], ('Name', (1, 12, 1, 15), 'int', ('Load',)))], []), +('Module', [('TypeAlias', (1, 0, 1, 32), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None, None), ('TypeVarTuple', (1, 10, 1, 13), 'Ts', None), ('ParamSpec', (1, 15, 1, 18), 'P', None)], ('Tuple', (1, 22, 1, 32), [('Name', (1, 23, 1, 24), 'T', ('Load',)), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Name', (1, 30, 1, 31), 'P', ('Load',))], ('Load',)))], []), +('Module', [('TypeAlias', (1, 0, 1, 37), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 13), 'T', ('Name', (1, 10, 1, 13), 'int', ('Load',)), None), ('TypeVarTuple', (1, 15, 1, 18), 'Ts', None), ('ParamSpec', (1, 20, 1, 23), 'P', None)], ('Tuple', (1, 27, 1, 37), [('Name', (1, 28, 1, 29), 'T', ('Load',)), ('Name', (1, 31, 1, 33), 'Ts', ('Load',)), ('Name', (1, 35, 1, 36), 'P', ('Load',))], ('Load',)))], []), +('Module', [('TypeAlias', (1, 0, 1, 44), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 20), 'T', ('Tuple', (1, 10, 1, 20), [('Name', (1, 11, 1, 14), 'int', ('Load',)), ('Name', (1, 16, 1, 19), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 22, 1, 25), 'Ts', None), ('ParamSpec', (1, 27, 1, 30), 'P', None)], ('Tuple', (1, 34, 1, 44), [('Name', (1, 35, 1, 36), 'T', ('Load',)), ('Name', (1, 38, 1, 40), 'Ts', ('Load',)), ('Name', (1, 42, 1, 43), 'P', ('Load',))], ('Load',)))], []), +('Module', [('TypeAlias', (1, 0, 1, 48), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 17), 'T', ('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Constant', (1, 16, 1, 17), 1, None)), ('TypeVarTuple', (1, 19, 1, 26), 'Ts', ('Constant', (1, 25, 1, 26), 2, None)), ('ParamSpec', (1, 28, 1, 34), 'P', ('Constant', (1, 33, 1, 34), 3, None))], ('Tuple', (1, 38, 1, 48), [('Name', (1, 39, 1, 40), 'T', ('Load',)), ('Name', (1, 42, 1, 44), 'Ts', ('Load',)), ('Name', (1, 46, 1, 47), 'P', ('Load',))], ('Load',)))], []), +('Module', [('ClassDef', (1, 0, 1, 16), 'X', [], [], [('Pass', (1, 12, 1, 16))], [], [('TypeVar', (1, 8, 1, 9), 'T', None, None)])], []), +('Module', [('ClassDef', (1, 0, 1, 26), 'X', [], [], [('Pass', (1, 22, 1, 26))], [], [('TypeVar', (1, 8, 1, 9), 'T', None, None), ('TypeVarTuple', (1, 11, 1, 14), 'Ts', None), ('ParamSpec', (1, 16, 1, 19), 'P', None)])], []), +('Module', [('ClassDef', (1, 0, 1, 31), 'X', [], [], [('Pass', (1, 27, 1, 31))], [], [('TypeVar', (1, 8, 1, 14), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',)), None), ('TypeVarTuple', (1, 16, 1, 19), 'Ts', None), ('ParamSpec', (1, 21, 1, 24), 'P', None)])], []), +('Module', [('ClassDef', (1, 0, 1, 38), 'X', [], [], [('Pass', (1, 34, 1, 38))], [], [('TypeVar', (1, 8, 1, 21), 'T', ('Tuple', (1, 11, 1, 21), [('Name', (1, 12, 1, 15), 'int', ('Load',)), ('Name', (1, 17, 1, 20), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 23, 1, 26), 'Ts', None), ('ParamSpec', (1, 28, 1, 31), 'P', None)])], []), +('Module', [('ClassDef', (1, 0, 1, 43), 'X', [], [], [('Pass', (1, 39, 1, 43))], [], [('TypeVar', (1, 8, 1, 18), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',)), ('Constant', (1, 17, 1, 18), 1, None)), ('TypeVarTuple', (1, 20, 1, 27), 'Ts', ('Constant', (1, 26, 1, 27), 2, None)), ('ParamSpec', (1, 29, 1, 36), 'P', ('Constant', (1, 35, 1, 36), 3, None))])], []), +('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 12, 1, 16))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None, None)])], []), +('Module', [('FunctionDef', (1, 0, 1, 26), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None, None), ('TypeVarTuple', (1, 9, 1, 12), 'Ts', None), ('ParamSpec', (1, 14, 1, 17), 'P', None)])], []), +('Module', [('FunctionDef', (1, 0, 1, 31), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 27, 1, 31))], [], None, None, [('TypeVar', (1, 6, 1, 12), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',)), None), ('TypeVarTuple', (1, 14, 1, 17), 'Ts', None), ('ParamSpec', (1, 19, 1, 22), 'P', None)])], []), +('Module', [('FunctionDef', (1, 0, 1, 38), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 34, 1, 38))], [], None, None, [('TypeVar', (1, 6, 1, 19), 'T', ('Tuple', (1, 9, 1, 19), [('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Name', (1, 15, 1, 18), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 21, 1, 24), 'Ts', None), ('ParamSpec', (1, 26, 1, 29), 'P', None)])], []), +('Module', [('FunctionDef', (1, 0, 1, 43), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 39, 1, 43))], [], None, None, [('TypeVar', (1, 6, 1, 16), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Constant', (1, 15, 1, 16), 1, None)), ('TypeVarTuple', (1, 18, 1, 25), 'Ts', ('Constant', (1, 24, 1, 25), 2, None)), ('ParamSpec', (1, 27, 1, 34), 'P', ('Constant', (1, 33, 1, 34), 3, None))])], []), ] single_results = [ ('Interactive', [('Expr', (1, 0, 1, 3), ('BinOp', (1, 0, 1, 3), ('Constant', (1, 0, 1, 1), 1, None), ('Add',), ('Constant', (1, 2, 1, 3), 2, None)))]), diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index 4b86395ee74f75..4c5bf6bfd33c75 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -6,7 +6,7 @@ import weakref from test.support import requires_working_socket, check_syntax_error, run_code -from typing import Generic, Sequence, TypeVar, TypeVarTuple, ParamSpec, get_args +from typing import Generic, NoDefault, Sequence, TypeVar, TypeVarTuple, ParamSpec, get_args class TypeParamsInvalidTest(unittest.TestCase): @@ -412,6 +412,14 @@ def test_comprehension_02(self): func, = T.__bound__ self.assertEqual(func(), 1) + def test_comprehension_03(self): + def F[T: [lambda: T for T in (T, [1])[1]]](): return [lambda: T for T in T.__name__] + func, = F() + self.assertEqual(func(), "T") + T, = F.__type_params__ + func, = T.__bound__ + self.assertEqual(func(), 1) + def test_gen_exp_in_nested_class(self): code = """ from test.test_type_params import make_base @@ -591,10 +599,12 @@ class Foo[T: Foo, U: (Foo, Foo)]: self.assertEqual(type_params[0].__name__, "T") self.assertIs(type_params[0].__bound__, Foo) self.assertEqual(type_params[0].__constraints__, ()) + self.assertIs(type_params[0].__default__, NoDefault) self.assertEqual(type_params[1].__name__, "U") self.assertIs(type_params[1].__bound__, None) self.assertEqual(type_params[1].__constraints__, (Foo, Foo)) + self.assertIs(type_params[1].__default__, NoDefault) def test_evaluation_error(self): class Foo[T: Undefined, U: (Undefined,)]: @@ -605,6 +615,8 @@ class Foo[T: Undefined, U: (Undefined,)]: type_params[0].__bound__ self.assertEqual(type_params[0].__constraints__, ()) self.assertIs(type_params[1].__bound__, None) + self.assertIs(type_params[0].__default__, NoDefault) + self.assertIs(type_params[1].__default__, NoDefault) with self.assertRaises(NameError): type_params[1].__constraints__ @@ -1158,3 +1170,103 @@ class Inner[U](T): """ with self.assertRaises(RuntimeError): run_code(code) + + +class DefaultsTest(unittest.TestCase): + def test_defaults_on_func(self): + ns = run_code(""" + def func[T=int, **U=float, *V=None](): + pass + """) + + T, U, V = ns["func"].__type_params__ + self.assertIs(T.__default__, int) + self.assertIs(U.__default__, float) + self.assertIs(V.__default__, None) + + def test_defaults_on_class(self): + ns = run_code(""" + class C[T=int, **U=float, *V=None]: + pass + """) + + T, U, V = ns["C"].__type_params__ + self.assertIs(T.__default__, int) + self.assertIs(U.__default__, float) + self.assertIs(V.__default__, None) + + def test_defaults_on_type_alias(self): + ns = run_code(""" + type Alias[T = int, **U = float, *V = None] = int + """) + + T, U, V = ns["Alias"].__type_params__ + self.assertIs(T.__default__, int) + self.assertIs(U.__default__, float) + self.assertIs(V.__default__, None) + + def test_starred_invalid(self): + check_syntax_error(self, "type Alias[T = *int] = int") + check_syntax_error(self, "type Alias[**P = *int] = int") + + def test_starred_typevartuple(self): + ns = run_code(""" + default = tuple[int, str] + type Alias[*Ts = *default] = Ts + """) + + Ts, = ns["Alias"].__type_params__ + self.assertEqual(Ts.__default__, next(iter(ns["default"]))) + + def test_nondefault_after_default(self): + check_syntax_error(self, "def func[T=int, U](): pass", "non-default type parameter 'U' follows default type parameter") + check_syntax_error(self, "class C[T=int, U]: pass", "non-default type parameter 'U' follows default type parameter") + check_syntax_error(self, "type A[T=int, U] = int", "non-default type parameter 'U' follows default type parameter") + + def test_lazy_evaluation(self): + ns = run_code(""" + type Alias[T = Undefined, *U = Undefined, **V = Undefined] = int + """) + + T, U, V = ns["Alias"].__type_params__ + + with self.assertRaises(NameError): + T.__default__ + with self.assertRaises(NameError): + U.__default__ + with self.assertRaises(NameError): + V.__default__ + + ns["Undefined"] = "defined" + self.assertEqual(T.__default__, "defined") + self.assertEqual(U.__default__, "defined") + self.assertEqual(V.__default__, "defined") + + # Now it is cached + ns["Undefined"] = "redefined" + self.assertEqual(T.__default__, "defined") + self.assertEqual(U.__default__, "defined") + self.assertEqual(V.__default__, "defined") + + def test_symtable_key_regression_default(self): + # Test against the bugs that would happen if we used .default_ + # as the key in the symtable. + ns = run_code(""" + type X[T = [T for T in [T]]] = T + """) + + T, = ns["X"].__type_params__ + self.assertEqual(T.__default__, [T]) + + def test_symtable_key_regression_name(self): + # Test against the bugs that would happen if we used .name + # as the key in the symtable. + ns = run_code(""" + type X1[T = A] = T + type X2[T = B] = T + A = "A" + B = "B" + """) + + self.assertEqual(ns["X1"].__type_params__[0].__default__, "A") + self.assertEqual(ns["X2"].__type_params__[0].__default__, "B") diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 703fe84f3aad10..112db03ae87887 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -38,7 +38,7 @@ from typing import Self, LiteralString from typing import TypeAlias from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs -from typing import TypeGuard, TypeIs +from typing import TypeGuard, TypeIs, NoDefault import abc import textwrap import typing @@ -580,6 +580,174 @@ def test_constructor(self): self.assertIs(T.__bound__, None) +class TypeParameterDefaultsTests(BaseTestCase): + def test_typevar(self): + T = TypeVar('T', default=int) + self.assertEqual(T.__default__, int) + self.assertTrue(T.has_default()) + self.assertIsInstance(T, TypeVar) + + class A(Generic[T]): ... + Alias = Optional[T] + + def test_typevar_none(self): + U = TypeVar('U') + U_None = TypeVar('U_None', default=None) + self.assertIs(U.__default__, NoDefault) + self.assertFalse(U.has_default()) + self.assertIs(U_None.__default__, None) + self.assertTrue(U_None.has_default()) + + class X[T]: ... + T, = X.__type_params__ + self.assertIs(T.__default__, NoDefault) + self.assertFalse(T.has_default()) + + def test_paramspec(self): + P = ParamSpec('P', default=(str, int)) + self.assertEqual(P.__default__, (str, int)) + self.assertTrue(P.has_default()) + self.assertIsInstance(P, ParamSpec) + + class A(Generic[P]): ... + Alias = typing.Callable[P, None] + + P_default = ParamSpec('P_default', default=...) + self.assertIs(P_default.__default__, ...) + + def test_paramspec_none(self): + U = ParamSpec('U') + U_None = ParamSpec('U_None', default=None) + self.assertIs(U.__default__, NoDefault) + self.assertFalse(U.has_default()) + self.assertIs(U_None.__default__, None) + self.assertTrue(U_None.has_default()) + + class X[**P]: ... + P, = X.__type_params__ + self.assertIs(P.__default__, NoDefault) + self.assertFalse(P.has_default()) + + def test_typevartuple(self): + Ts = TypeVarTuple('Ts', default=Unpack[Tuple[str, int]]) + self.assertEqual(Ts.__default__, Unpack[Tuple[str, int]]) + self.assertTrue(Ts.has_default()) + self.assertIsInstance(Ts, TypeVarTuple) + + class A(Generic[Unpack[Ts]]): ... + Alias = Optional[Unpack[Ts]] + + def test_typevartuple_specialization(self): + T = TypeVar("T") + Ts = TypeVarTuple('Ts', default=Unpack[Tuple[str, int]]) + self.assertEqual(Ts.__default__, Unpack[Tuple[str, int]]) + class A(Generic[T, Unpack[Ts]]): ... + self.assertEqual(A[float].__args__, (float, str, int)) + self.assertEqual(A[float, range].__args__, (float, range)) + self.assertEqual(A[float, *tuple[int, ...]].__args__, (float, *tuple[int, ...])) + + def test_typevar_and_typevartuple_specialization(self): + T = TypeVar("T") + U = TypeVar("U", default=float) + Ts = TypeVarTuple('Ts', default=Unpack[Tuple[str, int]]) + self.assertEqual(Ts.__default__, Unpack[Tuple[str, int]]) + class A(Generic[T, U, Unpack[Ts]]): ... + self.assertEqual(A[int].__args__, (int, float, str, int)) + self.assertEqual(A[int, str].__args__, (int, str, str, int)) + self.assertEqual(A[int, str, range].__args__, (int, str, range)) + self.assertEqual(A[int, str, *tuple[int, ...]].__args__, (int, str, *tuple[int, ...])) + + def test_no_default_after_typevar_tuple(self): + T = TypeVar("T", default=int) + Ts = TypeVarTuple("Ts") + Ts_default = TypeVarTuple("Ts_default", default=Unpack[Tuple[str, int]]) + + with self.assertRaises(TypeError): + class X(Generic[*Ts, T]): ... + + with self.assertRaises(TypeError): + class Y(Generic[*Ts_default, T]): ... + + def test_paramspec_specialization(self): + T = TypeVar("T") + P = ParamSpec('P', default=[str, int]) + self.assertEqual(P.__default__, [str, int]) + class A(Generic[T, P]): ... + self.assertEqual(A[float].__args__, (float, (str, int))) + self.assertEqual(A[float, [range]].__args__, (float, (range,))) + + def test_typevar_and_paramspec_specialization(self): + T = TypeVar("T") + U = TypeVar("U", default=float) + P = ParamSpec('P', default=[str, int]) + self.assertEqual(P.__default__, [str, int]) + class A(Generic[T, U, P]): ... + self.assertEqual(A[float].__args__, (float, float, (str, int))) + self.assertEqual(A[float, int].__args__, (float, int, (str, int))) + self.assertEqual(A[float, int, [range]].__args__, (float, int, (range,))) + + def test_paramspec_and_typevar_specialization(self): + T = TypeVar("T") + P = ParamSpec('P', default=[str, int]) + U = TypeVar("U", default=float) + self.assertEqual(P.__default__, [str, int]) + class A(Generic[T, P, U]): ... + self.assertEqual(A[float].__args__, (float, (str, int), float)) + self.assertEqual(A[float, [range]].__args__, (float, (range,), float)) + self.assertEqual(A[float, [range], int].__args__, (float, (range,), int)) + + def test_typevartuple_none(self): + U = TypeVarTuple('U') + U_None = TypeVarTuple('U_None', default=None) + self.assertIs(U.__default__, NoDefault) + self.assertFalse(U.has_default()) + self.assertIs(U_None.__default__, None) + self.assertTrue(U_None.has_default()) + + class X[**Ts]: ... + Ts, = X.__type_params__ + self.assertIs(Ts.__default__, NoDefault) + self.assertFalse(Ts.has_default()) + + def test_no_default_after_non_default(self): + DefaultStrT = TypeVar('DefaultStrT', default=str) + T = TypeVar('T') + + with self.assertRaisesRegex( + TypeError, r"Type parameter ~T without a default follows type parameter with a default" + ): + Test = Generic[DefaultStrT, T] + + def test_need_more_params(self): + DefaultStrT = TypeVar('DefaultStrT', default=str) + T = TypeVar('T') + U = TypeVar('U') + + class A(Generic[T, U, DefaultStrT]): ... + A[int, bool] + A[int, bool, str] + + with self.assertRaisesRegex( + TypeError, r"Too few arguments for .+; actual 1, expected at least 2" + ): + Test = A[int] + + def test_pickle(self): + global U, U_co, U_contra, U_default # pickle wants to reference the class by name + U = TypeVar('U') + U_co = TypeVar('U_co', covariant=True) + U_contra = TypeVar('U_contra', contravariant=True) + U_default = TypeVar('U_default', default=int) + for proto in range(pickle.HIGHEST_PROTOCOL): + for typevar in (U, U_co, U_contra, U_default): + z = pickle.loads(pickle.dumps(typevar, proto)) + self.assertEqual(z.__name__, typevar.__name__) + self.assertEqual(z.__covariant__, typevar.__covariant__) + self.assertEqual(z.__contravariant__, typevar.__contravariant__) + self.assertEqual(z.__bound__, typevar.__bound__) + self.assertEqual(z.__default__, typevar.__default__) + + def template_replace(templates: list[str], replacements: dict[str, list[str]]) -> list[tuple[str]]: """Renders templates with possible combinations of replacements. @@ -10001,6 +10169,26 @@ class CustomerModel(ModelBase, init=False): self.assertIsInstance(CustomerModel, Decorated) +class NoDefaultTests(BaseTestCase): + def test_pickling(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(NoDefault, proto) + loaded = pickle.loads(s) + self.assertIs(NoDefault, loaded) + + def test_constructor(self): + self.assertIs(NoDefault, type(NoDefault)()) + with self.assertRaises(TypeError): + NoDefault(1) + + def test_repr(self): + self.assertEqual(repr(NoDefault), 'typing.NoDefault') + + def test_no_call(self): + with self.assertRaises(TypeError): + NoDefault() + + class AllTests(BaseTestCase): """Tests for __all__.""" diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index bb15f64c59dbd1..35394f29fbe49d 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -673,6 +673,20 @@ def test_quote_in_format_spec(self): self.check_ast_roundtrip("""f'\\'{x:\\"}' """) self.check_ast_roundtrip("""f'\\'{x:\\\\"}' """) + def test_type_params(self): + self.check_ast_roundtrip("type A = int") + self.check_ast_roundtrip("type A[T] = int") + self.check_ast_roundtrip("type A[T: int] = int") + self.check_ast_roundtrip("type A[T = int] = int") + self.check_ast_roundtrip("type A[T: int = int] = int") + self.check_ast_roundtrip("type A[**P] = int") + self.check_ast_roundtrip("type A[**P = int] = int") + self.check_ast_roundtrip("type A[*Ts] = int") + self.check_ast_roundtrip("type A[*Ts = int] = int") + self.check_ast_roundtrip("type A[*Ts = *int] = int") + self.check_ast_roundtrip("def f[T: int = int, **P = int, *Ts = *int]():\n pass") + self.check_ast_roundtrip("class C[T: int = int, **P = int, *Ts = *int]():\n pass") + class ManualASTCreationTestCase(unittest.TestCase): """Test that AST nodes created without a type_params field unparse correctly.""" @@ -723,6 +737,20 @@ def test_function_with_type_params_and_bound(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "def f[T: int]():\n pass") + def test_function_with_type_params_and_default(self): + node = ast.FunctionDef( + name="f", + args=ast.arguments(), + body=[ast.Pass()], + type_params=[ + ast.TypeVar("T", default_value=ast.Constant(value=1)), + ast.TypeVarTuple("Ts", default_value=ast.Starred(value=ast.Constant(value=1), ctx=ast.Load())), + ast.ParamSpec("P", default_value=ast.Constant(value=1)), + ], + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "def f[T = 1, *Ts = *1, **P = 1]():\n pass") + def test_async_function(self): node = ast.AsyncFunctionDef( name="f", @@ -746,6 +774,20 @@ def test_async_function_with_type_params(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "async def f[T]():\n pass") + def test_async_function_with_type_params_and_default(self): + node = ast.AsyncFunctionDef( + name="f", + args=ast.arguments(), + body=[ast.Pass()], + type_params=[ + ast.TypeVar("T", default_value=ast.Constant(value=1)), + ast.TypeVarTuple("Ts", default_value=ast.Starred(value=ast.Constant(value=1), ctx=ast.Load())), + ast.ParamSpec("P", default_value=ast.Constant(value=1)), + ], + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "async def f[T = 1, *Ts = *1, **P = 1]():\n pass") + class DirectoryTestCase(ASTTestCase): """Test roundtrip behaviour on all files in Lib and Lib/test.""" diff --git a/Lib/typing.py b/Lib/typing.py index eff65cfb68b866..3f6ff491e7b918 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -38,6 +38,7 @@ ParamSpecKwargs, TypeAliasType, Generic, + NoDefault, ) # Please keep __all__ alphabetized within each category. @@ -138,6 +139,7 @@ 'NewType', 'no_type_check', 'no_type_check_decorator', + 'NoDefault', 'NoReturn', 'NotRequired', 'overload', @@ -266,6 +268,10 @@ def _collect_parameters(args): >>> _collect_parameters((T, Callable[P, T])) (~T, ~P) """ + # required type parameter cannot appear after parameter with default + default_encountered = False + # or after TypeVarTuple + type_var_tuple_encountered = False parameters = [] for t in args: if isinstance(t, type): @@ -280,27 +286,58 @@ def _collect_parameters(args): parameters.append(collected) elif hasattr(t, '__typing_subst__'): if t not in parameters: + if type_var_tuple_encountered and t.has_default(): + raise TypeError('Type parameter with a default' + ' follows TypeVarTuple') + + if t.has_default(): + default_encountered = True + elif default_encountered: + raise TypeError(f'Type parameter {t!r} without a default' + ' follows type parameter with a default') + parameters.append(t) else: + if _is_unpacked_typevartuple(t): + type_var_tuple_encountered = True for x in getattr(t, '__parameters__', ()): if x not in parameters: parameters.append(x) return tuple(parameters) -def _check_generic(cls, parameters, elen): +def _check_generic_specialization(cls, arguments): """Check correct count for parameters of a generic cls (internal helper). This gives a nice error message in case of count mismatch. """ - if not elen: + expected_len = len(cls.__parameters__) + if not expected_len: raise TypeError(f"{cls} is not a generic class") - alen = len(parameters) - if alen != elen: - raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};" - f" actual {alen}, expected {elen}") + actual_len = len(arguments) + if actual_len != expected_len: + # deal with defaults + if actual_len < expected_len: + # If the parameter at index `actual_len` in the parameters list + # has a default, then all parameters after it must also have + # one, because we validated as much in _collect_parameters(). + # That means that no error needs to be raised here, despite + # the number of arguments being passed not matching the number + # of parameters: all parameters that aren't explicitly + # specialized in this call are parameters with default values. + if cls.__parameters__[actual_len].has_default(): + return + + expected_len -= sum(p.has_default() for p in cls.__parameters__) + expect_val = f"at least {expected_len}" + else: + expect_val = expected_len + + raise TypeError(f"Too {'many' if actual_len > expected_len else 'few'} arguments" + f" for {cls}; actual {actual_len}, expected {expect_val}") -def _unpack_args(args): + +def _unpack_args(*args): newargs = [] for arg in args: subargs = getattr(arg, '__typing_unpacked_tuple_args__', None) @@ -1089,11 +1126,15 @@ def _typevartuple_prepare_subst(self, alias, args): elif left + right > alen: raise TypeError(f"Too few arguments for {alias};" f" actual {alen}, expected at least {plen-1}") + if left == alen - right and self.has_default(): + replacement = _unpack_args(self.__default__) + else: + replacement = args[left: alen - right] return ( *args[:left], *([fillarg]*(typevartuple_index - left)), - tuple(args[left: alen - right]), + replacement, *([fillarg]*(plen - right - left - typevartuple_index - 1)), *args[alen - right:], ) @@ -1111,6 +1152,8 @@ def _paramspec_subst(self, arg): def _paramspec_prepare_subst(self, alias, args): params = alias.__parameters__ i = params.index(self) + if i == len(args) and self.has_default(): + args = [*args, self.__default__] if i >= len(args): raise TypeError(f"Too few arguments for {alias}") # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612. @@ -1124,33 +1167,33 @@ def _paramspec_prepare_subst(self, alias, args): @_tp_cache -def _generic_class_getitem(cls, params): +def _generic_class_getitem(cls, args): """Parameterizes a generic class. At least, parameterizing a generic class is the *main* thing this method does. For example, for some generic class `Foo`, this is called when we - do `Foo[int]` - there, with `cls=Foo` and `params=int`. + do `Foo[int]` - there, with `cls=Foo` and `args=int`. However, note that this method is also called when defining generic classes in the first place with `class Foo(Generic[T]): ...`. """ - if not isinstance(params, tuple): - params = (params,) + if not isinstance(args, tuple): + args = (args,) - params = tuple(_type_convert(p) for p in params) + args = tuple(_type_convert(p) for p in args) is_generic_or_protocol = cls in (Generic, Protocol) if is_generic_or_protocol: # Generic and Protocol can only be subscripted with unique type variables. - if not params: + if not args: raise TypeError( f"Parameter list to {cls.__qualname__}[...] cannot be empty" ) - if not all(_is_typevar_like(p) for p in params): + if not all(_is_typevar_like(p) for p in args): raise TypeError( f"Parameters to {cls.__name__}[...] must all be type variables " f"or parameter specification variables.") - if len(set(params)) != len(params): + if len(set(args)) != len(args): raise TypeError( f"Parameters to {cls.__name__}[...] must all be unique") else: @@ -1158,18 +1201,18 @@ def _generic_class_getitem(cls, params): for param in cls.__parameters__: prepare = getattr(param, '__typing_prepare_subst__', None) if prepare is not None: - params = prepare(cls, params) - _check_generic(cls, params, len(cls.__parameters__)) + args = prepare(cls, args) + _check_generic_specialization(cls, args) new_args = [] - for param, new_arg in zip(cls.__parameters__, params): + for param, new_arg in zip(cls.__parameters__, args): if isinstance(param, TypeVarTuple): new_args.extend(new_arg) else: new_args.append(new_arg) - params = tuple(new_args) + args = tuple(new_args) - return _GenericAlias(cls, params) + return _GenericAlias(cls, args) def _generic_init_subclass(cls, *args, **kwargs): @@ -1390,8 +1433,7 @@ def __getitem__(self, args): # Preprocess `args`. if not isinstance(args, tuple): args = (args,) - args = tuple(_type_convert(p) for p in args) - args = _unpack_args(args) + args = _unpack_args(*(_type_convert(p) for p in args)) new_args = self._determine_new_args(args) r = self.copy_with(new_args) return r @@ -1552,7 +1594,12 @@ def __getitem__(self, params): params = (params,) msg = "Parameters to generic types must be types." params = tuple(_type_check(p, msg) for p in params) - _check_generic(self, params, self._nparams) + actual_len = len(params) + if actual_len != self._nparams: + if not self._nparams: + raise TypeError(f"{self} is not a generic class") + raise TypeError(f"Too {'many' if actual_len > self._nparams else 'few'} arguments for {self};" + f" actual {actual_len}, expected {self._nparams}") return self.copy_with(params) def copy_with(self, params): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-02-29-18-55-45.gh-issue-116129.wsFnIq.rst b/Misc/NEWS.d/next/Core and Builtins/2024-02-29-18-55-45.gh-issue-116129.wsFnIq.rst new file mode 100644 index 00000000000000..e632ad58d6ded5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-02-29-18-55-45.gh-issue-116129.wsFnIq.rst @@ -0,0 +1,2 @@ +Implement :pep:`696`, adding support for defaults on type parameters. +Patch by Jelle Zijlstra. diff --git a/Modules/_typingmodule.c b/Modules/_typingmodule.c index 9ea72bf89ce0b2..180f3d7eb01794 100644 --- a/Modules/_typingmodule.c +++ b/Modules/_typingmodule.c @@ -63,6 +63,9 @@ _typing_exec(PyObject *m) if (PyModule_AddObjectRef(m, "TypeAliasType", (PyObject *)&_PyTypeAlias_Type) < 0) { return -1; } + if (PyModule_AddObjectRef(m, "NoDefault", (PyObject *)&_Py_NoDefaultStruct) < 0) { + return -1; + } return 0; } diff --git a/Objects/clinic/typevarobject.c.h b/Objects/clinic/typevarobject.c.h index 2bb0a98a2ed64c..0ba4ff48bc8804 100644 --- a/Objects/clinic/typevarobject.c.h +++ b/Objects/clinic/typevarobject.c.h @@ -9,16 +9,16 @@ preserve #include "pycore_modsupport.h" // _PyArg_UnpackKeywordsWithVararg() PyDoc_STRVAR(typevar_new__doc__, -"typevar(name, *constraints, bound=None, covariant=False,\n" -" contravariant=False, infer_variance=False)\n" +"typevar(name, *constraints, bound=None, default=typing.NoDefault,\n" +" covariant=False, contravariant=False, infer_variance=False)\n" "--\n" "\n" "Create a TypeVar."); static PyObject * typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints, - PyObject *bound, int covariant, int contravariant, - int infer_variance); + PyObject *bound, PyObject *default_value, int covariant, + int contravariant, int infer_variance); static PyObject * typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -26,14 +26,14 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 5 + #define NUM_KEYWORDS 6 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD PyObject *ob_item[NUM_KEYWORDS]; } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), }, + .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(default), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -42,20 +42,21 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"name", "bound", "covariant", "contravariant", "infer_variance", NULL}; + static const char * const _keywords[] = {"name", "bound", "default", "covariant", "contravariant", "infer_variance", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "typevar", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[6]; + PyObject *argsbuf[7]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; PyObject *name; PyObject *constraints = NULL; PyObject *bound = Py_None; + PyObject *default_value = &_Py_NoDefaultStruct; int covariant = 0; int contravariant = 0; int infer_variance = 0; @@ -80,7 +81,13 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } } if (fastargs[3]) { - covariant = PyObject_IsTrue(fastargs[3]); + default_value = fastargs[3]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[4]) { + covariant = PyObject_IsTrue(fastargs[4]); if (covariant < 0) { goto exit; } @@ -88,8 +95,8 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto skip_optional_kwonly; } } - if (fastargs[4]) { - contravariant = PyObject_IsTrue(fastargs[4]); + if (fastargs[5]) { + contravariant = PyObject_IsTrue(fastargs[5]); if (contravariant < 0) { goto exit; } @@ -97,12 +104,12 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto skip_optional_kwonly; } } - infer_variance = PyObject_IsTrue(fastargs[5]); + infer_variance = PyObject_IsTrue(fastargs[6]); if (infer_variance < 0) { goto exit; } skip_optional_kwonly: - return_value = typevar_new_impl(type, name, constraints, bound, covariant, contravariant, infer_variance); + return_value = typevar_new_impl(type, name, constraints, bound, default_value, covariant, contravariant, infer_variance); exit: Py_XDECREF(constraints); @@ -117,6 +124,36 @@ PyDoc_STRVAR(typevar_typing_subst__doc__, #define TYPEVAR_TYPING_SUBST_METHODDEF \ {"__typing_subst__", (PyCFunction)typevar_typing_subst, METH_O, typevar_typing_subst__doc__}, +PyDoc_STRVAR(typevar_typing_prepare_subst__doc__, +"__typing_prepare_subst__($self, alias, args, /)\n" +"--\n" +"\n"); + +#define TYPEVAR_TYPING_PREPARE_SUBST_METHODDEF \ + {"__typing_prepare_subst__", _PyCFunction_CAST(typevar_typing_prepare_subst), METH_FASTCALL, typevar_typing_prepare_subst__doc__}, + +static PyObject * +typevar_typing_prepare_subst_impl(typevarobject *self, PyObject *alias, + PyObject *args); + +static PyObject * +typevar_typing_prepare_subst(typevarobject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *alias; + PyObject *__clinic_args; + + if (!_PyArg_CheckPositional("__typing_prepare_subst__", nargs, 2, 2)) { + goto exit; + } + alias = args[0]; + __clinic_args = args[1]; + return_value = typevar_typing_prepare_subst_impl(self, alias, __clinic_args); + +exit: + return return_value; +} + PyDoc_STRVAR(typevar_reduce__doc__, "__reduce__($self, /)\n" "--\n" @@ -134,6 +171,23 @@ typevar_reduce(typevarobject *self, PyObject *Py_UNUSED(ignored)) return typevar_reduce_impl(self); } +PyDoc_STRVAR(typevar_has_default__doc__, +"has_default($self, /)\n" +"--\n" +"\n"); + +#define TYPEVAR_HAS_DEFAULT_METHODDEF \ + {"has_default", (PyCFunction)typevar_has_default, METH_NOARGS, typevar_has_default__doc__}, + +static PyObject * +typevar_has_default_impl(typevarobject *self); + +static PyObject * +typevar_has_default(typevarobject *self, PyObject *Py_UNUSED(ignored)) +{ + return typevar_has_default_impl(self); +} + PyDoc_STRVAR(paramspecargs_new__doc__, "paramspecargs(origin)\n" "--\n" @@ -243,15 +297,16 @@ paramspeckwargs_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } PyDoc_STRVAR(paramspec_new__doc__, -"paramspec(name, *, bound=None, covariant=False, contravariant=False,\n" -" infer_variance=False)\n" +"paramspec(name, *, bound=None, default=typing.NoDefault,\n" +" covariant=False, contravariant=False, infer_variance=False)\n" "--\n" "\n" "Create a ParamSpec object."); static PyObject * paramspec_new_impl(PyTypeObject *type, PyObject *name, PyObject *bound, - int covariant, int contravariant, int infer_variance); + PyObject *default_value, int covariant, int contravariant, + int infer_variance); static PyObject * paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -259,14 +314,14 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 5 + #define NUM_KEYWORDS 6 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD PyObject *ob_item[NUM_KEYWORDS]; } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), }, + .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(default), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -275,19 +330,20 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"name", "bound", "covariant", "contravariant", "infer_variance", NULL}; + static const char * const _keywords[] = {"name", "bound", "default", "covariant", "contravariant", "infer_variance", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "paramspec", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[5]; + PyObject *argsbuf[6]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; PyObject *name; PyObject *bound = Py_None; + PyObject *default_value = &_Py_NoDefaultStruct; int covariant = 0; int contravariant = 0; int infer_variance = 0; @@ -311,7 +367,13 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } } if (fastargs[2]) { - covariant = PyObject_IsTrue(fastargs[2]); + default_value = fastargs[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[3]) { + covariant = PyObject_IsTrue(fastargs[3]); if (covariant < 0) { goto exit; } @@ -319,8 +381,8 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto skip_optional_kwonly; } } - if (fastargs[3]) { - contravariant = PyObject_IsTrue(fastargs[3]); + if (fastargs[4]) { + contravariant = PyObject_IsTrue(fastargs[4]); if (contravariant < 0) { goto exit; } @@ -328,12 +390,12 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto skip_optional_kwonly; } } - infer_variance = PyObject_IsTrue(fastargs[4]); + infer_variance = PyObject_IsTrue(fastargs[5]); if (infer_variance < 0) { goto exit; } skip_optional_kwonly: - return_value = paramspec_new_impl(type, name, bound, covariant, contravariant, infer_variance); + return_value = paramspec_new_impl(type, name, bound, default_value, covariant, contravariant, infer_variance); exit: return return_value; @@ -394,14 +456,32 @@ paramspec_reduce(paramspecobject *self, PyObject *Py_UNUSED(ignored)) return paramspec_reduce_impl(self); } +PyDoc_STRVAR(paramspec_has_default__doc__, +"has_default($self, /)\n" +"--\n" +"\n"); + +#define PARAMSPEC_HAS_DEFAULT_METHODDEF \ + {"has_default", (PyCFunction)paramspec_has_default, METH_NOARGS, paramspec_has_default__doc__}, + +static PyObject * +paramspec_has_default_impl(paramspecobject *self); + +static PyObject * +paramspec_has_default(paramspecobject *self, PyObject *Py_UNUSED(ignored)) +{ + return paramspec_has_default_impl(self); +} + PyDoc_STRVAR(typevartuple__doc__, -"typevartuple(name)\n" +"typevartuple(name, *, default=typing.NoDefault)\n" "--\n" "\n" "Create a new TypeVarTuple with the given name."); static PyObject * -typevartuple_impl(PyTypeObject *type, PyObject *name); +typevartuple_impl(PyTypeObject *type, PyObject *name, + PyObject *default_value); static PyObject * typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -409,14 +489,14 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 1 + #define NUM_KEYWORDS 2 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD PyObject *ob_item[NUM_KEYWORDS]; } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(name), }, + .ob_item = { &_Py_ID(name), &_Py_ID(default), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -425,17 +505,19 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"name", NULL}; + static const char * const _keywords[] = {"name", "default", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "typevartuple", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[1]; + PyObject *argsbuf[2]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; PyObject *name; + PyObject *default_value = &_Py_NoDefaultStruct; fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); if (!fastargs) { @@ -446,7 +528,12 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto exit; } name = fastargs[0]; - return_value = typevartuple_impl(type, name); + if (!noptargs) { + goto skip_optional_kwonly; + } + default_value = fastargs[1]; +skip_optional_kwonly: + return_value = typevartuple_impl(type, name, default_value); exit: return return_value; @@ -507,6 +594,23 @@ typevartuple_reduce(typevartupleobject *self, PyObject *Py_UNUSED(ignored)) return typevartuple_reduce_impl(self); } +PyDoc_STRVAR(typevartuple_has_default__doc__, +"has_default($self, /)\n" +"--\n" +"\n"); + +#define TYPEVARTUPLE_HAS_DEFAULT_METHODDEF \ + {"has_default", (PyCFunction)typevartuple_has_default, METH_NOARGS, typevartuple_has_default__doc__}, + +static PyObject * +typevartuple_has_default_impl(typevartupleobject *self); + +static PyObject * +typevartuple_has_default(typevartupleobject *self, PyObject *Py_UNUSED(ignored)) +{ + return typevartuple_has_default_impl(self); +} + PyDoc_STRVAR(typealias_reduce__doc__, "__reduce__($self, /)\n" "--\n" @@ -591,4 +695,4 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=5a582d9d89ad787b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=73b39e550e4e336c input=a9049054013a1b77]*/ diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 7f80c9c61b8abc..cc916045266aea 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -23,6 +23,8 @@ typedef struct { PyObject *evaluate_bound; PyObject *constraints; PyObject *evaluate_constraints; + PyObject *default_value; + PyObject *evaluate_default; bool covariant; bool contravariant; bool infer_variance; @@ -31,12 +33,16 @@ typedef struct { typedef struct { PyObject_HEAD PyObject *name; + PyObject *default_value; + PyObject *evaluate_default; } typevartupleobject; typedef struct { PyObject_HEAD PyObject *name; PyObject *bound; + PyObject *default_value; + PyObject *evaluate_default; bool covariant; bool contravariant; bool infer_variance; @@ -53,6 +59,64 @@ typedef struct { #include "clinic/typevarobject.c.h" +/* NoDefault is a marker object to indicate that a parameter has no default. */ + +static PyObject * +NoDefault_repr(PyObject *op) +{ + return PyUnicode_FromString("typing.NoDefault"); +} + +static PyObject * +NoDefault_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) +{ + return PyUnicode_FromString("NoDefault"); +} + +static PyMethodDef notimplemented_methods[] = { + {"__reduce__", NoDefault_reduce, METH_NOARGS, NULL}, + {NULL, NULL} +}; + +static PyObject * +nodefault_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + if (PyTuple_GET_SIZE(args) || (kwargs && PyDict_GET_SIZE(kwargs))) { + PyErr_SetString(PyExc_TypeError, "NoDefaultType takes no arguments"); + return NULL; + } + return &_Py_NoDefaultStruct; +} + +static void +nodefault_dealloc(PyObject *nodefault) +{ + /* This should never get called, but we also don't want to SEGV if + * we accidentally decref NoDefault out of existence. Instead, + * since NoDefault is an immortal object, re-set the reference count. + */ + _Py_SetImmortal(nodefault); +} + +PyDoc_STRVAR(notimplemented_doc, +"NoDefaultType()\n" +"--\n\n" +"The type of the NoDefault singleton."); + +PyTypeObject _PyNoDefault_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "NoDefaultType", + .tp_dealloc = nodefault_dealloc, + .tp_repr = NoDefault_repr, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = notimplemented_doc, + .tp_methods = notimplemented_methods, + .tp_new = nodefault_new, +}; + +PyObject _Py_NoDefaultStruct = _PyObject_HEAD_INIT(&_PyNoDefault_Type); + + static PyObject * call_typing_func_object(const char *name, PyObject **args, size_t nargs) { @@ -200,6 +264,8 @@ typevar_dealloc(PyObject *self) Py_XDECREF(tv->evaluate_bound); Py_XDECREF(tv->constraints); Py_XDECREF(tv->evaluate_constraints); + Py_XDECREF(tv->default_value); + Py_XDECREF(tv->evaluate_default); PyObject_ClearManagedDict(self); PyObject_ClearWeakRefs(self); @@ -216,6 +282,8 @@ typevar_traverse(PyObject *self, visitproc visit, void *arg) Py_VISIT(tv->evaluate_bound); Py_VISIT(tv->constraints); Py_VISIT(tv->evaluate_constraints); + Py_VISIT(tv->default_value); + Py_VISIT(tv->evaluate_default); PyObject_VisitManagedDict(self, visit, arg); return 0; } @@ -227,6 +295,8 @@ typevar_clear(typevarobject *self) Py_CLEAR(self->evaluate_bound); Py_CLEAR(self->constraints); Py_CLEAR(self->evaluate_constraints); + Py_CLEAR(self->default_value); + Py_CLEAR(self->evaluate_default); PyObject_ClearManagedDict((PyObject *)self); return 0; } @@ -266,6 +336,20 @@ typevar_bound(typevarobject *self, void *Py_UNUSED(ignored)) return bound; } +static PyObject * +typevar_default(typevarobject *self, void *unused) +{ + if (self->default_value != NULL) { + return Py_NewRef(self->default_value); + } + if (self->evaluate_default == NULL) { + return &_Py_NoDefaultStruct; + } + PyObject *default_value = PyObject_CallNoArgs(self->evaluate_default); + self->default_value = Py_XNewRef(default_value); + return default_value; +} + static PyObject * typevar_constraints(typevarobject *self, void *Py_UNUSED(ignored)) { @@ -283,12 +367,14 @@ typevar_constraints(typevarobject *self, void *Py_UNUSED(ignored)) static PyGetSetDef typevar_getset[] = { {"__bound__", (getter)typevar_bound, NULL, NULL, NULL}, {"__constraints__", (getter)typevar_constraints, NULL, NULL, NULL}, + {"__default__", (getter)typevar_default, NULL, NULL, NULL}, {0} }; static typevarobject * typevar_alloc(PyObject *name, PyObject *bound, PyObject *evaluate_bound, PyObject *constraints, PyObject *evaluate_constraints, + PyObject *default_value, bool covariant, bool contravariant, bool infer_variance, PyObject *module) { @@ -305,6 +391,8 @@ typevar_alloc(PyObject *name, PyObject *bound, PyObject *evaluate_bound, tv->evaluate_bound = Py_XNewRef(evaluate_bound); tv->constraints = Py_XNewRef(constraints); tv->evaluate_constraints = Py_XNewRef(evaluate_constraints); + tv->default_value = Py_XNewRef(default_value); + tv->evaluate_default = NULL; tv->covariant = covariant; tv->contravariant = contravariant; @@ -328,6 +416,7 @@ typevar.__new__ as typevar_new name: object(subclass_of="&PyUnicode_Type") *constraints: object bound: object = None + default as default_value: object(c_default="&_Py_NoDefaultStruct") = typing.NoDefault covariant: bool = False contravariant: bool = False infer_variance: bool = False @@ -337,9 +426,9 @@ Create a TypeVar. static PyObject * typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints, - PyObject *bound, int covariant, int contravariant, - int infer_variance) -/*[clinic end generated code: output=1d200450ee99226d input=41ae33a916bfe76f]*/ + PyObject *bound, PyObject *default_value, int covariant, + int contravariant, int infer_variance) +/*[clinic end generated code: output=d2b248ff074eaab6 input=836f97f631d7293a]*/ { if (covariant && contravariant) { PyErr_SetString(PyExc_ValueError, @@ -386,6 +475,7 @@ typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints, PyObject *tv = (PyObject *)typevar_alloc(name, bound, NULL, constraints, NULL, + default_value, covariant, contravariant, infer_variance, module); Py_XDECREF(bound); @@ -410,6 +500,66 @@ typevar_typing_subst(typevarobject *self, PyObject *arg) return result; } +/*[clinic input] +typevar.__typing_prepare_subst__ as typevar_typing_prepare_subst + + alias: object + args: object + / + +[clinic start generated code]*/ + +static PyObject * +typevar_typing_prepare_subst_impl(typevarobject *self, PyObject *alias, + PyObject *args) +/*[clinic end generated code: output=82c3f4691e0ded22 input=201a750415d14ffb]*/ +{ + PyObject *params = PyObject_GetAttrString(alias, "__parameters__"); + if (params == NULL) { + return NULL; + } + Py_ssize_t i = PySequence_Index(params, (PyObject *)self); + if (i == -1) { + Py_DECREF(params); + return NULL; + } + Py_ssize_t args_len = PySequence_Length(args); + if (args_len == -1) { + Py_DECREF(params); + return NULL; + } + if (i < args_len) { + // We already have a value for our TypeVar + Py_DECREF(params); + return Py_NewRef(args); + } + else if (i == args_len) { + // If the TypeVar has a default, use it. + PyObject *dflt = typevar_default(self, NULL); + if (dflt == NULL) { + Py_DECREF(params); + return NULL; + } + if (dflt != &_Py_NoDefaultStruct) { + PyObject *new_args = PyTuple_Pack(1, dflt); + Py_DECREF(dflt); + if (new_args == NULL) { + Py_DECREF(params); + return NULL; + } + PyObject *result = PySequence_Concat(args, new_args); + Py_DECREF(params); + Py_DECREF(new_args); + return result; + } + } + Py_DECREF(params); + PyErr_Format(PyExc_TypeError, + "Too few arguments for %S; actual %d, expected at least %d", + alias, args_len, i + 1); + return NULL; +} + /*[clinic input] typevar.__reduce__ as typevar_reduce @@ -422,6 +572,23 @@ typevar_reduce_impl(typevarobject *self) return Py_NewRef(self->name); } + +/*[clinic input] +typevar.has_default as typevar_has_default + +[clinic start generated code]*/ + +static PyObject * +typevar_has_default_impl(typevarobject *self) +/*[clinic end generated code: output=76bf0b8dc98b97dd input=31024aa030761cf6]*/ +{ + if (self->evaluate_default != NULL || + (self->default_value != &_Py_NoDefaultStruct && self->default_value != NULL)) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + static PyObject * typevar_mro_entries(PyObject *self, PyObject *args) { @@ -432,7 +599,9 @@ typevar_mro_entries(PyObject *self, PyObject *args) static PyMethodDef typevar_methods[] = { TYPEVAR_TYPING_SUBST_METHODDEF + TYPEVAR_TYPING_PREPARE_SUBST_METHODDEF TYPEVAR_REDUCE_METHODDEF + TYPEVAR_HAS_DEFAULT_METHODDEF {"__mro_entries__", typevar_mro_entries, METH_O}, {0} }; @@ -457,12 +626,18 @@ variables::\n\ class StrOrBytesSequence[A: (str, bytes)]:\n\ ...\n\ \n\ +Type variables can also have defaults:\n\ +\n\ + class IntDefault[T = int]:\n\ + ...\n\ +\n\ However, if desired, reusable type variables can also be constructed\n\ manually, like so::\n\ \n\ T = TypeVar('T') # Can be anything\n\ S = TypeVar('S', bound=str) # Can be any subtype of str\n\ A = TypeVar('A', str, bytes) # Must be exactly str or bytes\n\ + D = TypeVar('D', default=int) # Defaults to int\n\ \n\ Type variables exist primarily for the benefit of static type\n\ checkers. They serve as the parameters for generic types as well\n\ @@ -739,6 +914,8 @@ paramspec_dealloc(PyObject *self) Py_DECREF(ps->name); Py_XDECREF(ps->bound); + Py_XDECREF(ps->default_value); + Py_XDECREF(ps->evaluate_default); PyObject_ClearManagedDict(self); PyObject_ClearWeakRefs(self); @@ -752,6 +929,8 @@ paramspec_traverse(PyObject *self, visitproc visit, void *arg) Py_VISIT(Py_TYPE(self)); paramspecobject *ps = (paramspecobject *)self; Py_VISIT(ps->bound); + Py_VISIT(ps->default_value); + Py_VISIT(ps->evaluate_default); PyObject_VisitManagedDict(self, visit, arg); return 0; } @@ -760,6 +939,8 @@ static int paramspec_clear(paramspecobject *self) { Py_CLEAR(self->bound); + Py_CLEAR(self->default_value); + Py_CLEAR(self->evaluate_default); PyObject_ClearManagedDict((PyObject *)self); return 0; } @@ -800,14 +981,29 @@ paramspec_kwargs(PyObject *self, void *unused) return (PyObject *)paramspecattr_new(tp, self); } +static PyObject * +paramspec_default(paramspecobject *self, void *unused) +{ + if (self->default_value != NULL) { + return Py_NewRef(self->default_value); + } + if (self->evaluate_default == NULL) { + return &_Py_NoDefaultStruct; + } + PyObject *default_value = PyObject_CallNoArgs(self->evaluate_default); + self->default_value = Py_XNewRef(default_value); + return default_value; +} + static PyGetSetDef paramspec_getset[] = { {"args", (getter)paramspec_args, NULL, PyDoc_STR("Represents positional arguments."), NULL}, {"kwargs", (getter)paramspec_kwargs, NULL, PyDoc_STR("Represents keyword arguments."), NULL}, + {"__default__", (getter)paramspec_default, NULL, "The default value for this ParamSpec.", NULL}, {0}, }; static paramspecobject * -paramspec_alloc(PyObject *name, PyObject *bound, bool covariant, +paramspec_alloc(PyObject *name, PyObject *bound, PyObject *default_value, bool covariant, bool contravariant, bool infer_variance, PyObject *module) { PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.paramspec_type; @@ -820,6 +1016,8 @@ paramspec_alloc(PyObject *name, PyObject *bound, bool covariant, ps->covariant = covariant; ps->contravariant = contravariant; ps->infer_variance = infer_variance; + ps->default_value = Py_XNewRef(default_value); + ps->evaluate_default = NULL; _PyObject_GC_TRACK(ps); if (module != NULL) { if (PyObject_SetAttrString((PyObject *)ps, "__module__", module) < 0) { @@ -837,6 +1035,7 @@ paramspec.__new__ as paramspec_new name: object(subclass_of="&PyUnicode_Type") * bound: object = None + default as default_value: object(c_default="&_Py_NoDefaultStruct") = typing.NoDefault covariant: bool = False contravariant: bool = False infer_variance: bool = False @@ -846,8 +1045,9 @@ Create a ParamSpec object. static PyObject * paramspec_new_impl(PyTypeObject *type, PyObject *name, PyObject *bound, - int covariant, int contravariant, int infer_variance) -/*[clinic end generated code: output=fd2daab79cba62da input=57c49c581979b952]*/ + PyObject *default_value, int covariant, int contravariant, + int infer_variance) +/*[clinic end generated code: output=47ca9d63fa5a094d input=495e1565bc067ab9]*/ { if (covariant && contravariant) { PyErr_SetString(PyExc_ValueError, "Bivariant types are not supported."); @@ -869,7 +1069,7 @@ paramspec_new_impl(PyTypeObject *type, PyObject *name, PyObject *bound, return NULL; } PyObject *ps = (PyObject *)paramspec_alloc( - name, bound, covariant, contravariant, infer_variance, module); + name, bound, default_value, covariant, contravariant, infer_variance, module); Py_XDECREF(bound); Py_DECREF(module); return ps; @@ -925,6 +1125,22 @@ paramspec_reduce_impl(paramspecobject *self) return Py_NewRef(self->name); } +/*[clinic input] +paramspec.has_default as paramspec_has_default + +[clinic start generated code]*/ + +static PyObject * +paramspec_has_default_impl(paramspecobject *self) +/*[clinic end generated code: output=daaae7467a6a4368 input=2112e97eeb76cd59]*/ +{ + if (self->evaluate_default != NULL || + (self->default_value != &_Py_NoDefaultStruct && self->default_value != NULL)) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + static PyObject * paramspec_mro_entries(PyObject *self, PyObject *args) { @@ -936,6 +1152,7 @@ paramspec_mro_entries(PyObject *self, PyObject *args) static PyMethodDef paramspec_methods[] = { PARAMSPEC_TYPING_SUBST_METHODDEF PARAMSPEC_TYPING_PREPARE_SUBST_METHODDEF + PARAMSPEC_HAS_DEFAULT_METHODDEF PARAMSPEC_REDUCE_METHODDEF {"__mro_entries__", paramspec_mro_entries, METH_O}, {0} @@ -950,10 +1167,17 @@ where the use of '**' creates a parameter specification::\n\ \n\ type IntFunc[**P] = Callable[P, int]\n\ \n\ +The following syntax creates a parameter specification that defaults\n\ +to a callable accepting two positional-only arguments of types int\n\ +and str:\n\ +\n\ + type IntFuncDefault[**P = (int, str)] = Callable[P, int]\n\ +\n\ For compatibility with Python 3.11 and earlier, ParamSpec objects\n\ can also be created as follows::\n\ \n\ P = ParamSpec('P')\n\ + DefaultP = ParamSpec('DefaultP', default=(int, str))\n\ \n\ Parameter specification variables exist primarily for the benefit of\n\ static type checkers. They are used to forward the parameter types of\n\ @@ -1021,6 +1245,8 @@ typevartuple_dealloc(PyObject *self) typevartupleobject *tvt = (typevartupleobject *)self; Py_DECREF(tvt->name); + Py_XDECREF(tvt->default_value); + Py_XDECREF(tvt->evaluate_default); PyObject_ClearManagedDict(self); PyObject_ClearWeakRefs(self); @@ -1060,7 +1286,7 @@ static PyMemberDef typevartuple_members[] = { }; static typevartupleobject * -typevartuple_alloc(PyObject *name, PyObject *module) +typevartuple_alloc(PyObject *name, PyObject *module, PyObject *default_value) { PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.typevartuple_type; typevartupleobject *tvt = PyObject_GC_New(typevartupleobject, tp); @@ -1068,6 +1294,8 @@ typevartuple_alloc(PyObject *name, PyObject *module) return NULL; } tvt->name = Py_NewRef(name); + tvt->default_value = Py_XNewRef(default_value); + tvt->evaluate_default = NULL; _PyObject_GC_TRACK(tvt); if (module != NULL) { if (PyObject_SetAttrString((PyObject *)tvt, "__module__", module) < 0) { @@ -1083,19 +1311,22 @@ typevartuple_alloc(PyObject *name, PyObject *module) typevartuple.__new__ name: object(subclass_of="&PyUnicode_Type") + * + default as default_value: object(c_default="&_Py_NoDefaultStruct") = typing.NoDefault Create a new TypeVarTuple with the given name. [clinic start generated code]*/ static PyObject * -typevartuple_impl(PyTypeObject *type, PyObject *name) -/*[clinic end generated code: output=09d417a28f976202 input=00d28abcf1fc96bb]*/ +typevartuple_impl(PyTypeObject *type, PyObject *name, + PyObject *default_value) +/*[clinic end generated code: output=9d6b76dfe95aae51 input=e149739929a866d0]*/ { PyObject *module = caller(); if (module == NULL) { return NULL; } - PyObject *result = (PyObject *)typevartuple_alloc(name, module); + PyObject *result = (PyObject *)typevartuple_alloc(name, module, default_value); Py_DECREF(module); return result; } @@ -1148,6 +1379,23 @@ typevartuple_reduce_impl(typevartupleobject *self) return Py_NewRef(self->name); } + +/*[clinic input] +typevartuple.has_default as typevartuple_has_default + +[clinic start generated code]*/ + +static PyObject * +typevartuple_has_default_impl(typevartupleobject *self) +/*[clinic end generated code: output=4895f602f56a5e29 input=9ef3250ddb2c1851]*/ +{ + if (self->evaluate_default != NULL || + (self->default_value != &_Py_NoDefaultStruct && self->default_value != NULL)) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + static PyObject * typevartuple_mro_entries(PyObject *self, PyObject *args) { @@ -1160,6 +1408,8 @@ static int typevartuple_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); + Py_VISIT(((typevartupleobject *)self)->default_value); + Py_VISIT(((typevartupleobject *)self)->evaluate_default); PyObject_VisitManagedDict(self, visit, arg); return 0; } @@ -1167,14 +1417,36 @@ typevartuple_traverse(PyObject *self, visitproc visit, void *arg) static int typevartuple_clear(PyObject *self) { + Py_CLEAR(((typevartupleobject *)self)->default_value); + Py_CLEAR(((typevartupleobject *)self)->evaluate_default); PyObject_ClearManagedDict(self); return 0; } +static PyObject * +typevartuple_default(typevartupleobject *self, void *unused) +{ + if (self->default_value != NULL) { + return Py_NewRef(self->default_value); + } + if (self->evaluate_default == NULL) { + return &_Py_NoDefaultStruct; + } + PyObject *default_value = PyObject_CallNoArgs(self->evaluate_default); + self->default_value = Py_XNewRef(default_value); + return default_value; +} + +static PyGetSetDef typevartuple_getset[] = { + {"__default__", (getter)typevartuple_default, NULL, "The default value for this TypeVarTuple.", NULL}, + {0}, +}; + static PyMethodDef typevartuple_methods[] = { TYPEVARTUPLE_TYPING_SUBST_METHODDEF TYPEVARTUPLE_TYPING_PREPARE_SUBST_METHODDEF TYPEVARTUPLE_REDUCE_METHODDEF + TYPEVARTUPLE_HAS_DEFAULT_METHODDEF {"__mro_entries__", typevartuple_mro_entries, METH_O}, {0} }; @@ -1190,10 +1462,15 @@ where a single '*' indicates a type variable tuple::\n\ def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]:\n\ return (*tup[1:], tup[0])\n\ \n\ +Type variables tuples can have default values:\n\ +\n\ + type AliasWithDefault[*Ts = (str, int)] = tuple[*Ts]\n\ +\n\ For compatibility with Python 3.11 and earlier, TypeVarTuple objects\n\ can also be created as follows::\n\ \n\ Ts = TypeVarTuple('Ts') # Can be given any name\n\ + DefaultTs = TypeVarTuple('Ts', default=(str, int))\n\ \n\ Just as a TypeVar (type variable) is a placeholder for a single type,\n\ a TypeVarTuple is a placeholder for an *arbitrary* number of types. For\n\ @@ -1218,6 +1495,7 @@ PyType_Slot typevartuple_slots[] = { {Py_tp_doc, (void *)typevartuple_doc}, {Py_tp_members, typevartuple_members}, {Py_tp_methods, typevartuple_methods}, + {Py_tp_getset, typevartuple_getset}, {Py_tp_new, typevartuple}, {Py_tp_iter, typevartuple_iter}, {Py_tp_repr, typevartuple_repr}, @@ -1241,21 +1519,21 @@ PyObject * _Py_make_typevar(PyObject *name, PyObject *evaluate_bound, PyObject *evaluate_constraints) { return (PyObject *)typevar_alloc(name, NULL, evaluate_bound, NULL, evaluate_constraints, - false, false, true, NULL); + NULL, false, false, true, NULL); } PyObject * _Py_make_paramspec(PyThreadState *Py_UNUSED(ignored), PyObject *v) { assert(PyUnicode_Check(v)); - return (PyObject *)paramspec_alloc(v, NULL, false, false, true, NULL); + return (PyObject *)paramspec_alloc(v, NULL, NULL, false, false, true, NULL); } PyObject * _Py_make_typevartuple(PyThreadState *Py_UNUSED(ignored), PyObject *v) { assert(PyUnicode_Check(v)); - return (PyObject *)typevartuple_alloc(v, NULL); + return (PyObject *)typevartuple_alloc(v, NULL, NULL); } static void @@ -1687,3 +1965,24 @@ void _Py_clear_generic_types(PyInterpreterState *interp) Py_CLEAR(interp->cached_objects.paramspecargs_type); Py_CLEAR(interp->cached_objects.paramspeckwargs_type); } + +PyObject * +_Py_set_typeparam_default(PyThreadState *ts, PyObject *typeparam, PyObject *evaluate_default) +{ + if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.typevar_type)) { + Py_XSETREF(((typevarobject *)typeparam)->evaluate_default, Py_NewRef(evaluate_default)); + return Py_NewRef(typeparam); + } + else if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.paramspec_type)) { + Py_XSETREF(((paramspecobject *)typeparam)->evaluate_default, Py_NewRef(evaluate_default)); + return Py_NewRef(typeparam); + } + else if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.typevartuple_type)) { + Py_XSETREF(((typevartupleobject *)typeparam)->evaluate_default, Py_NewRef(evaluate_default)); + return Py_NewRef(typeparam); + } + else { + PyErr_Format(PyExc_TypeError, "Expected a type param, got %R", typeparam); + return NULL; + } +} diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 0d154867276c36..80776ffe449393 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -145,8 +145,8 @@ module Python type_ignore = TypeIgnore(int lineno, string tag) - type_param = TypeVar(identifier name, expr? bound) - | ParamSpec(identifier name) - | TypeVarTuple(identifier name) + type_param = TypeVar(identifier name, expr? bound, expr? default_value) + | ParamSpec(identifier name, expr? default_value) + | TypeVarTuple(identifier name, expr? default_value) attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) } diff --git a/Parser/parser.c b/Parser/parser.c index 0715e9775a8f06..e34fcada15ebf5 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -186,431 +186,433 @@ static char *soft_keywords[] = { #define type_param_seq_type 1099 #define type_param_type 1100 #define type_param_bound_type 1101 -#define expressions_type 1102 -#define expression_type 1103 -#define yield_expr_type 1104 -#define star_expressions_type 1105 -#define star_expression_type 1106 -#define star_named_expressions_type 1107 -#define star_named_expression_type 1108 -#define assignment_expression_type 1109 -#define named_expression_type 1110 -#define disjunction_type 1111 -#define conjunction_type 1112 -#define inversion_type 1113 -#define comparison_type 1114 -#define compare_op_bitwise_or_pair_type 1115 -#define eq_bitwise_or_type 1116 -#define noteq_bitwise_or_type 1117 -#define lte_bitwise_or_type 1118 -#define lt_bitwise_or_type 1119 -#define gte_bitwise_or_type 1120 -#define gt_bitwise_or_type 1121 -#define notin_bitwise_or_type 1122 -#define in_bitwise_or_type 1123 -#define isnot_bitwise_or_type 1124 -#define is_bitwise_or_type 1125 -#define bitwise_or_type 1126 // Left-recursive -#define bitwise_xor_type 1127 // Left-recursive -#define bitwise_and_type 1128 // Left-recursive -#define shift_expr_type 1129 // Left-recursive -#define sum_type 1130 // Left-recursive -#define term_type 1131 // Left-recursive -#define factor_type 1132 -#define power_type 1133 -#define await_primary_type 1134 -#define primary_type 1135 // Left-recursive -#define slices_type 1136 -#define slice_type 1137 -#define atom_type 1138 -#define group_type 1139 -#define lambdef_type 1140 -#define lambda_params_type 1141 -#define lambda_parameters_type 1142 -#define lambda_slash_no_default_type 1143 -#define lambda_slash_with_default_type 1144 -#define lambda_star_etc_type 1145 -#define lambda_kwds_type 1146 -#define lambda_param_no_default_type 1147 -#define lambda_param_with_default_type 1148 -#define lambda_param_maybe_default_type 1149 -#define lambda_param_type 1150 -#define fstring_middle_type 1151 -#define fstring_replacement_field_type 1152 -#define fstring_conversion_type 1153 -#define fstring_full_format_spec_type 1154 -#define fstring_format_spec_type 1155 -#define fstring_type 1156 -#define string_type 1157 -#define strings_type 1158 -#define list_type 1159 -#define tuple_type 1160 -#define set_type 1161 -#define dict_type 1162 -#define double_starred_kvpairs_type 1163 -#define double_starred_kvpair_type 1164 -#define kvpair_type 1165 -#define for_if_clauses_type 1166 -#define for_if_clause_type 1167 -#define listcomp_type 1168 -#define setcomp_type 1169 -#define genexp_type 1170 -#define dictcomp_type 1171 -#define arguments_type 1172 -#define args_type 1173 -#define kwargs_type 1174 -#define starred_expression_type 1175 -#define kwarg_or_starred_type 1176 -#define kwarg_or_double_starred_type 1177 -#define star_targets_type 1178 -#define star_targets_list_seq_type 1179 -#define star_targets_tuple_seq_type 1180 -#define star_target_type 1181 -#define target_with_star_atom_type 1182 -#define star_atom_type 1183 -#define single_target_type 1184 -#define single_subscript_attribute_target_type 1185 -#define t_primary_type 1186 // Left-recursive -#define t_lookahead_type 1187 -#define del_targets_type 1188 -#define del_target_type 1189 -#define del_t_atom_type 1190 -#define type_expressions_type 1191 -#define func_type_comment_type 1192 -#define invalid_arguments_type 1193 -#define invalid_kwarg_type 1194 -#define expression_without_invalid_type 1195 -#define invalid_legacy_expression_type 1196 -#define invalid_expression_type 1197 -#define invalid_named_expression_type 1198 -#define invalid_assignment_type 1199 -#define invalid_ann_assign_target_type 1200 -#define invalid_del_stmt_type 1201 -#define invalid_block_type 1202 -#define invalid_comprehension_type 1203 -#define invalid_dict_comprehension_type 1204 -#define invalid_parameters_type 1205 -#define invalid_default_type 1206 -#define invalid_star_etc_type 1207 -#define invalid_kwds_type 1208 -#define invalid_parameters_helper_type 1209 -#define invalid_lambda_parameters_type 1210 -#define invalid_lambda_parameters_helper_type 1211 -#define invalid_lambda_star_etc_type 1212 -#define invalid_lambda_kwds_type 1213 -#define invalid_double_type_comments_type 1214 -#define invalid_with_item_type 1215 -#define invalid_for_target_type 1216 -#define invalid_group_type 1217 -#define invalid_import_type 1218 -#define invalid_import_from_targets_type 1219 -#define invalid_compound_stmt_type 1220 -#define invalid_with_stmt_type 1221 -#define invalid_with_stmt_indent_type 1222 -#define invalid_try_stmt_type 1223 -#define invalid_except_stmt_type 1224 -#define invalid_finally_stmt_type 1225 -#define invalid_except_stmt_indent_type 1226 -#define invalid_except_star_stmt_indent_type 1227 -#define invalid_match_stmt_type 1228 -#define invalid_case_block_type 1229 -#define invalid_as_pattern_type 1230 -#define invalid_class_pattern_type 1231 -#define invalid_class_argument_pattern_type 1232 -#define invalid_if_stmt_type 1233 -#define invalid_elif_stmt_type 1234 -#define invalid_else_stmt_type 1235 -#define invalid_while_stmt_type 1236 -#define invalid_for_stmt_type 1237 -#define invalid_def_raw_type 1238 -#define invalid_class_def_raw_type 1239 -#define invalid_double_starred_kvpairs_type 1240 -#define invalid_kvpair_type 1241 -#define invalid_starred_expression_type 1242 -#define invalid_replacement_field_type 1243 -#define invalid_conversion_character_type 1244 -#define invalid_arithmetic_type 1245 -#define invalid_factor_type 1246 -#define _loop0_1_type 1247 -#define _loop0_2_type 1248 -#define _loop1_3_type 1249 -#define _loop0_5_type 1250 -#define _gather_4_type 1251 -#define _tmp_6_type 1252 -#define _tmp_7_type 1253 -#define _tmp_8_type 1254 -#define _tmp_9_type 1255 -#define _tmp_10_type 1256 -#define _tmp_11_type 1257 -#define _tmp_12_type 1258 -#define _tmp_13_type 1259 -#define _loop1_14_type 1260 -#define _tmp_15_type 1261 -#define _tmp_16_type 1262 -#define _tmp_17_type 1263 -#define _loop0_19_type 1264 -#define _gather_18_type 1265 -#define _loop0_21_type 1266 -#define _gather_20_type 1267 -#define _tmp_22_type 1268 -#define _tmp_23_type 1269 -#define _loop0_24_type 1270 -#define _loop1_25_type 1271 -#define _loop0_27_type 1272 -#define _gather_26_type 1273 -#define _tmp_28_type 1274 -#define _loop0_30_type 1275 -#define _gather_29_type 1276 -#define _tmp_31_type 1277 -#define _loop1_32_type 1278 -#define _tmp_33_type 1279 -#define _tmp_34_type 1280 -#define _tmp_35_type 1281 -#define _loop0_36_type 1282 -#define _loop0_37_type 1283 -#define _loop0_38_type 1284 -#define _loop1_39_type 1285 -#define _loop0_40_type 1286 -#define _loop1_41_type 1287 -#define _loop1_42_type 1288 -#define _loop1_43_type 1289 -#define _loop0_44_type 1290 -#define _loop1_45_type 1291 -#define _loop0_46_type 1292 -#define _loop1_47_type 1293 -#define _loop0_48_type 1294 -#define _loop0_49_type 1295 -#define _loop1_50_type 1296 -#define _loop0_52_type 1297 -#define _gather_51_type 1298 -#define _loop0_54_type 1299 -#define _gather_53_type 1300 -#define _loop0_56_type 1301 -#define _gather_55_type 1302 -#define _loop0_58_type 1303 -#define _gather_57_type 1304 -#define _tmp_59_type 1305 -#define _loop1_60_type 1306 -#define _loop1_61_type 1307 -#define _tmp_62_type 1308 -#define _tmp_63_type 1309 -#define _loop1_64_type 1310 -#define _loop0_66_type 1311 -#define _gather_65_type 1312 -#define _tmp_67_type 1313 -#define _tmp_68_type 1314 -#define _tmp_69_type 1315 -#define _tmp_70_type 1316 -#define _loop0_72_type 1317 -#define _gather_71_type 1318 -#define _loop0_74_type 1319 -#define _gather_73_type 1320 -#define _tmp_75_type 1321 -#define _loop0_77_type 1322 -#define _gather_76_type 1323 -#define _loop0_79_type 1324 -#define _gather_78_type 1325 -#define _loop0_81_type 1326 -#define _gather_80_type 1327 -#define _loop1_82_type 1328 -#define _loop1_83_type 1329 -#define _loop0_85_type 1330 -#define _gather_84_type 1331 -#define _loop1_86_type 1332 -#define _loop1_87_type 1333 -#define _loop1_88_type 1334 -#define _tmp_89_type 1335 -#define _loop0_91_type 1336 -#define _gather_90_type 1337 -#define _tmp_92_type 1338 -#define _tmp_93_type 1339 -#define _tmp_94_type 1340 -#define _tmp_95_type 1341 -#define _tmp_96_type 1342 -#define _tmp_97_type 1343 -#define _loop0_98_type 1344 -#define _loop0_99_type 1345 -#define _loop0_100_type 1346 -#define _loop1_101_type 1347 -#define _loop0_102_type 1348 -#define _loop1_103_type 1349 -#define _loop1_104_type 1350 -#define _loop1_105_type 1351 -#define _loop0_106_type 1352 -#define _loop1_107_type 1353 -#define _loop0_108_type 1354 -#define _loop1_109_type 1355 -#define _loop0_110_type 1356 -#define _loop1_111_type 1357 -#define _loop0_112_type 1358 -#define _loop0_113_type 1359 -#define _loop1_114_type 1360 -#define _tmp_115_type 1361 -#define _loop0_117_type 1362 -#define _gather_116_type 1363 -#define _loop1_118_type 1364 -#define _loop0_119_type 1365 -#define _loop0_120_type 1366 -#define _tmp_121_type 1367 -#define _tmp_122_type 1368 -#define _loop0_124_type 1369 -#define _gather_123_type 1370 -#define _tmp_125_type 1371 -#define _loop0_127_type 1372 -#define _gather_126_type 1373 -#define _loop0_129_type 1374 -#define _gather_128_type 1375 -#define _loop0_131_type 1376 -#define _gather_130_type 1377 -#define _loop0_133_type 1378 -#define _gather_132_type 1379 -#define _loop0_134_type 1380 -#define _loop0_136_type 1381 -#define _gather_135_type 1382 -#define _loop1_137_type 1383 -#define _tmp_138_type 1384 -#define _loop0_140_type 1385 -#define _gather_139_type 1386 -#define _loop0_142_type 1387 -#define _gather_141_type 1388 -#define _loop0_144_type 1389 -#define _gather_143_type 1390 -#define _loop0_146_type 1391 -#define _gather_145_type 1392 -#define _loop0_148_type 1393 -#define _gather_147_type 1394 -#define _tmp_149_type 1395 -#define _tmp_150_type 1396 -#define _loop0_152_type 1397 -#define _gather_151_type 1398 -#define _tmp_153_type 1399 -#define _tmp_154_type 1400 -#define _tmp_155_type 1401 -#define _tmp_156_type 1402 -#define _tmp_157_type 1403 -#define _tmp_158_type 1404 -#define _tmp_159_type 1405 -#define _tmp_160_type 1406 -#define _tmp_161_type 1407 -#define _tmp_162_type 1408 -#define _loop0_163_type 1409 -#define _loop0_164_type 1410 -#define _loop0_165_type 1411 -#define _tmp_166_type 1412 -#define _tmp_167_type 1413 -#define _tmp_168_type 1414 -#define _tmp_169_type 1415 -#define _loop0_170_type 1416 -#define _loop0_171_type 1417 -#define _loop0_172_type 1418 -#define _loop1_173_type 1419 -#define _tmp_174_type 1420 -#define _loop0_175_type 1421 -#define _tmp_176_type 1422 -#define _loop0_177_type 1423 -#define _loop1_178_type 1424 -#define _tmp_179_type 1425 -#define _tmp_180_type 1426 -#define _tmp_181_type 1427 -#define _loop0_182_type 1428 -#define _tmp_183_type 1429 -#define _tmp_184_type 1430 -#define _loop1_185_type 1431 -#define _tmp_186_type 1432 -#define _loop0_187_type 1433 -#define _loop0_188_type 1434 -#define _loop0_189_type 1435 -#define _loop0_191_type 1436 -#define _gather_190_type 1437 -#define _tmp_192_type 1438 -#define _loop0_193_type 1439 -#define _tmp_194_type 1440 -#define _loop0_195_type 1441 -#define _loop1_196_type 1442 -#define _loop1_197_type 1443 -#define _tmp_198_type 1444 -#define _tmp_199_type 1445 -#define _loop0_200_type 1446 -#define _tmp_201_type 1447 -#define _tmp_202_type 1448 -#define _tmp_203_type 1449 -#define _loop0_205_type 1450 -#define _gather_204_type 1451 -#define _loop0_207_type 1452 -#define _gather_206_type 1453 -#define _loop0_209_type 1454 -#define _gather_208_type 1455 -#define _loop0_211_type 1456 -#define _gather_210_type 1457 -#define _loop0_213_type 1458 -#define _gather_212_type 1459 -#define _tmp_214_type 1460 -#define _loop0_215_type 1461 -#define _loop1_216_type 1462 -#define _tmp_217_type 1463 -#define _loop0_218_type 1464 -#define _loop1_219_type 1465 -#define _tmp_220_type 1466 -#define _tmp_221_type 1467 -#define _tmp_222_type 1468 -#define _tmp_223_type 1469 -#define _tmp_224_type 1470 -#define _tmp_225_type 1471 -#define _tmp_226_type 1472 -#define _tmp_227_type 1473 -#define _tmp_228_type 1474 -#define _tmp_229_type 1475 -#define _loop0_231_type 1476 -#define _gather_230_type 1477 -#define _tmp_232_type 1478 -#define _tmp_233_type 1479 -#define _tmp_234_type 1480 -#define _tmp_235_type 1481 -#define _tmp_236_type 1482 -#define _tmp_237_type 1483 -#define _tmp_238_type 1484 -#define _loop0_239_type 1485 -#define _tmp_240_type 1486 -#define _tmp_241_type 1487 -#define _tmp_242_type 1488 -#define _tmp_243_type 1489 -#define _tmp_244_type 1490 -#define _tmp_245_type 1491 -#define _tmp_246_type 1492 -#define _tmp_247_type 1493 -#define _tmp_248_type 1494 -#define _tmp_249_type 1495 -#define _tmp_250_type 1496 -#define _tmp_251_type 1497 -#define _tmp_252_type 1498 -#define _tmp_253_type 1499 -#define _tmp_254_type 1500 -#define _tmp_255_type 1501 -#define _loop0_256_type 1502 -#define _tmp_257_type 1503 -#define _tmp_258_type 1504 -#define _tmp_259_type 1505 -#define _tmp_260_type 1506 -#define _tmp_261_type 1507 -#define _tmp_262_type 1508 -#define _tmp_263_type 1509 -#define _tmp_264_type 1510 -#define _tmp_265_type 1511 -#define _tmp_266_type 1512 -#define _tmp_267_type 1513 -#define _tmp_268_type 1514 -#define _tmp_269_type 1515 -#define _tmp_270_type 1516 -#define _tmp_271_type 1517 -#define _tmp_272_type 1518 -#define _loop0_274_type 1519 -#define _gather_273_type 1520 -#define _tmp_275_type 1521 -#define _tmp_276_type 1522 -#define _tmp_277_type 1523 -#define _tmp_278_type 1524 -#define _tmp_279_type 1525 -#define _tmp_280_type 1526 +#define type_param_default_type 1102 +#define type_param_starred_default_type 1103 +#define expressions_type 1104 +#define expression_type 1105 +#define yield_expr_type 1106 +#define star_expressions_type 1107 +#define star_expression_type 1108 +#define star_named_expressions_type 1109 +#define star_named_expression_type 1110 +#define assignment_expression_type 1111 +#define named_expression_type 1112 +#define disjunction_type 1113 +#define conjunction_type 1114 +#define inversion_type 1115 +#define comparison_type 1116 +#define compare_op_bitwise_or_pair_type 1117 +#define eq_bitwise_or_type 1118 +#define noteq_bitwise_or_type 1119 +#define lte_bitwise_or_type 1120 +#define lt_bitwise_or_type 1121 +#define gte_bitwise_or_type 1122 +#define gt_bitwise_or_type 1123 +#define notin_bitwise_or_type 1124 +#define in_bitwise_or_type 1125 +#define isnot_bitwise_or_type 1126 +#define is_bitwise_or_type 1127 +#define bitwise_or_type 1128 // Left-recursive +#define bitwise_xor_type 1129 // Left-recursive +#define bitwise_and_type 1130 // Left-recursive +#define shift_expr_type 1131 // Left-recursive +#define sum_type 1132 // Left-recursive +#define term_type 1133 // Left-recursive +#define factor_type 1134 +#define power_type 1135 +#define await_primary_type 1136 +#define primary_type 1137 // Left-recursive +#define slices_type 1138 +#define slice_type 1139 +#define atom_type 1140 +#define group_type 1141 +#define lambdef_type 1142 +#define lambda_params_type 1143 +#define lambda_parameters_type 1144 +#define lambda_slash_no_default_type 1145 +#define lambda_slash_with_default_type 1146 +#define lambda_star_etc_type 1147 +#define lambda_kwds_type 1148 +#define lambda_param_no_default_type 1149 +#define lambda_param_with_default_type 1150 +#define lambda_param_maybe_default_type 1151 +#define lambda_param_type 1152 +#define fstring_middle_type 1153 +#define fstring_replacement_field_type 1154 +#define fstring_conversion_type 1155 +#define fstring_full_format_spec_type 1156 +#define fstring_format_spec_type 1157 +#define fstring_type 1158 +#define string_type 1159 +#define strings_type 1160 +#define list_type 1161 +#define tuple_type 1162 +#define set_type 1163 +#define dict_type 1164 +#define double_starred_kvpairs_type 1165 +#define double_starred_kvpair_type 1166 +#define kvpair_type 1167 +#define for_if_clauses_type 1168 +#define for_if_clause_type 1169 +#define listcomp_type 1170 +#define setcomp_type 1171 +#define genexp_type 1172 +#define dictcomp_type 1173 +#define arguments_type 1174 +#define args_type 1175 +#define kwargs_type 1176 +#define starred_expression_type 1177 +#define kwarg_or_starred_type 1178 +#define kwarg_or_double_starred_type 1179 +#define star_targets_type 1180 +#define star_targets_list_seq_type 1181 +#define star_targets_tuple_seq_type 1182 +#define star_target_type 1183 +#define target_with_star_atom_type 1184 +#define star_atom_type 1185 +#define single_target_type 1186 +#define single_subscript_attribute_target_type 1187 +#define t_primary_type 1188 // Left-recursive +#define t_lookahead_type 1189 +#define del_targets_type 1190 +#define del_target_type 1191 +#define del_t_atom_type 1192 +#define type_expressions_type 1193 +#define func_type_comment_type 1194 +#define invalid_arguments_type 1195 +#define invalid_kwarg_type 1196 +#define expression_without_invalid_type 1197 +#define invalid_legacy_expression_type 1198 +#define invalid_expression_type 1199 +#define invalid_named_expression_type 1200 +#define invalid_assignment_type 1201 +#define invalid_ann_assign_target_type 1202 +#define invalid_del_stmt_type 1203 +#define invalid_block_type 1204 +#define invalid_comprehension_type 1205 +#define invalid_dict_comprehension_type 1206 +#define invalid_parameters_type 1207 +#define invalid_default_type 1208 +#define invalid_star_etc_type 1209 +#define invalid_kwds_type 1210 +#define invalid_parameters_helper_type 1211 +#define invalid_lambda_parameters_type 1212 +#define invalid_lambda_parameters_helper_type 1213 +#define invalid_lambda_star_etc_type 1214 +#define invalid_lambda_kwds_type 1215 +#define invalid_double_type_comments_type 1216 +#define invalid_with_item_type 1217 +#define invalid_for_target_type 1218 +#define invalid_group_type 1219 +#define invalid_import_type 1220 +#define invalid_import_from_targets_type 1221 +#define invalid_compound_stmt_type 1222 +#define invalid_with_stmt_type 1223 +#define invalid_with_stmt_indent_type 1224 +#define invalid_try_stmt_type 1225 +#define invalid_except_stmt_type 1226 +#define invalid_finally_stmt_type 1227 +#define invalid_except_stmt_indent_type 1228 +#define invalid_except_star_stmt_indent_type 1229 +#define invalid_match_stmt_type 1230 +#define invalid_case_block_type 1231 +#define invalid_as_pattern_type 1232 +#define invalid_class_pattern_type 1233 +#define invalid_class_argument_pattern_type 1234 +#define invalid_if_stmt_type 1235 +#define invalid_elif_stmt_type 1236 +#define invalid_else_stmt_type 1237 +#define invalid_while_stmt_type 1238 +#define invalid_for_stmt_type 1239 +#define invalid_def_raw_type 1240 +#define invalid_class_def_raw_type 1241 +#define invalid_double_starred_kvpairs_type 1242 +#define invalid_kvpair_type 1243 +#define invalid_starred_expression_type 1244 +#define invalid_replacement_field_type 1245 +#define invalid_conversion_character_type 1246 +#define invalid_arithmetic_type 1247 +#define invalid_factor_type 1248 +#define _loop0_1_type 1249 +#define _loop0_2_type 1250 +#define _loop1_3_type 1251 +#define _loop0_5_type 1252 +#define _gather_4_type 1253 +#define _tmp_6_type 1254 +#define _tmp_7_type 1255 +#define _tmp_8_type 1256 +#define _tmp_9_type 1257 +#define _tmp_10_type 1258 +#define _tmp_11_type 1259 +#define _tmp_12_type 1260 +#define _tmp_13_type 1261 +#define _loop1_14_type 1262 +#define _tmp_15_type 1263 +#define _tmp_16_type 1264 +#define _tmp_17_type 1265 +#define _loop0_19_type 1266 +#define _gather_18_type 1267 +#define _loop0_21_type 1268 +#define _gather_20_type 1269 +#define _tmp_22_type 1270 +#define _tmp_23_type 1271 +#define _loop0_24_type 1272 +#define _loop1_25_type 1273 +#define _loop0_27_type 1274 +#define _gather_26_type 1275 +#define _tmp_28_type 1276 +#define _loop0_30_type 1277 +#define _gather_29_type 1278 +#define _tmp_31_type 1279 +#define _loop1_32_type 1280 +#define _tmp_33_type 1281 +#define _tmp_34_type 1282 +#define _tmp_35_type 1283 +#define _loop0_36_type 1284 +#define _loop0_37_type 1285 +#define _loop0_38_type 1286 +#define _loop1_39_type 1287 +#define _loop0_40_type 1288 +#define _loop1_41_type 1289 +#define _loop1_42_type 1290 +#define _loop1_43_type 1291 +#define _loop0_44_type 1292 +#define _loop1_45_type 1293 +#define _loop0_46_type 1294 +#define _loop1_47_type 1295 +#define _loop0_48_type 1296 +#define _loop0_49_type 1297 +#define _loop1_50_type 1298 +#define _loop0_52_type 1299 +#define _gather_51_type 1300 +#define _loop0_54_type 1301 +#define _gather_53_type 1302 +#define _loop0_56_type 1303 +#define _gather_55_type 1304 +#define _loop0_58_type 1305 +#define _gather_57_type 1306 +#define _tmp_59_type 1307 +#define _loop1_60_type 1308 +#define _loop1_61_type 1309 +#define _tmp_62_type 1310 +#define _tmp_63_type 1311 +#define _loop1_64_type 1312 +#define _loop0_66_type 1313 +#define _gather_65_type 1314 +#define _tmp_67_type 1315 +#define _tmp_68_type 1316 +#define _tmp_69_type 1317 +#define _tmp_70_type 1318 +#define _loop0_72_type 1319 +#define _gather_71_type 1320 +#define _loop0_74_type 1321 +#define _gather_73_type 1322 +#define _tmp_75_type 1323 +#define _loop0_77_type 1324 +#define _gather_76_type 1325 +#define _loop0_79_type 1326 +#define _gather_78_type 1327 +#define _loop0_81_type 1328 +#define _gather_80_type 1329 +#define _loop1_82_type 1330 +#define _loop1_83_type 1331 +#define _loop0_85_type 1332 +#define _gather_84_type 1333 +#define _loop1_86_type 1334 +#define _loop1_87_type 1335 +#define _loop1_88_type 1336 +#define _tmp_89_type 1337 +#define _loop0_91_type 1338 +#define _gather_90_type 1339 +#define _tmp_92_type 1340 +#define _tmp_93_type 1341 +#define _tmp_94_type 1342 +#define _tmp_95_type 1343 +#define _tmp_96_type 1344 +#define _tmp_97_type 1345 +#define _loop0_98_type 1346 +#define _loop0_99_type 1347 +#define _loop0_100_type 1348 +#define _loop1_101_type 1349 +#define _loop0_102_type 1350 +#define _loop1_103_type 1351 +#define _loop1_104_type 1352 +#define _loop1_105_type 1353 +#define _loop0_106_type 1354 +#define _loop1_107_type 1355 +#define _loop0_108_type 1356 +#define _loop1_109_type 1357 +#define _loop0_110_type 1358 +#define _loop1_111_type 1359 +#define _loop0_112_type 1360 +#define _loop0_113_type 1361 +#define _loop1_114_type 1362 +#define _tmp_115_type 1363 +#define _loop0_117_type 1364 +#define _gather_116_type 1365 +#define _loop1_118_type 1366 +#define _loop0_119_type 1367 +#define _loop0_120_type 1368 +#define _tmp_121_type 1369 +#define _tmp_122_type 1370 +#define _loop0_124_type 1371 +#define _gather_123_type 1372 +#define _tmp_125_type 1373 +#define _loop0_127_type 1374 +#define _gather_126_type 1375 +#define _loop0_129_type 1376 +#define _gather_128_type 1377 +#define _loop0_131_type 1378 +#define _gather_130_type 1379 +#define _loop0_133_type 1380 +#define _gather_132_type 1381 +#define _loop0_134_type 1382 +#define _loop0_136_type 1383 +#define _gather_135_type 1384 +#define _loop1_137_type 1385 +#define _tmp_138_type 1386 +#define _loop0_140_type 1387 +#define _gather_139_type 1388 +#define _loop0_142_type 1389 +#define _gather_141_type 1390 +#define _loop0_144_type 1391 +#define _gather_143_type 1392 +#define _loop0_146_type 1393 +#define _gather_145_type 1394 +#define _loop0_148_type 1395 +#define _gather_147_type 1396 +#define _tmp_149_type 1397 +#define _tmp_150_type 1398 +#define _loop0_152_type 1399 +#define _gather_151_type 1400 +#define _tmp_153_type 1401 +#define _tmp_154_type 1402 +#define _tmp_155_type 1403 +#define _tmp_156_type 1404 +#define _tmp_157_type 1405 +#define _tmp_158_type 1406 +#define _tmp_159_type 1407 +#define _tmp_160_type 1408 +#define _tmp_161_type 1409 +#define _tmp_162_type 1410 +#define _loop0_163_type 1411 +#define _loop0_164_type 1412 +#define _loop0_165_type 1413 +#define _tmp_166_type 1414 +#define _tmp_167_type 1415 +#define _tmp_168_type 1416 +#define _tmp_169_type 1417 +#define _loop0_170_type 1418 +#define _loop0_171_type 1419 +#define _loop0_172_type 1420 +#define _loop1_173_type 1421 +#define _tmp_174_type 1422 +#define _loop0_175_type 1423 +#define _tmp_176_type 1424 +#define _loop0_177_type 1425 +#define _loop1_178_type 1426 +#define _tmp_179_type 1427 +#define _tmp_180_type 1428 +#define _tmp_181_type 1429 +#define _loop0_182_type 1430 +#define _tmp_183_type 1431 +#define _tmp_184_type 1432 +#define _loop1_185_type 1433 +#define _tmp_186_type 1434 +#define _loop0_187_type 1435 +#define _loop0_188_type 1436 +#define _loop0_189_type 1437 +#define _loop0_191_type 1438 +#define _gather_190_type 1439 +#define _tmp_192_type 1440 +#define _loop0_193_type 1441 +#define _tmp_194_type 1442 +#define _loop0_195_type 1443 +#define _loop1_196_type 1444 +#define _loop1_197_type 1445 +#define _tmp_198_type 1446 +#define _tmp_199_type 1447 +#define _loop0_200_type 1448 +#define _tmp_201_type 1449 +#define _tmp_202_type 1450 +#define _tmp_203_type 1451 +#define _loop0_205_type 1452 +#define _gather_204_type 1453 +#define _loop0_207_type 1454 +#define _gather_206_type 1455 +#define _loop0_209_type 1456 +#define _gather_208_type 1457 +#define _loop0_211_type 1458 +#define _gather_210_type 1459 +#define _loop0_213_type 1460 +#define _gather_212_type 1461 +#define _tmp_214_type 1462 +#define _loop0_215_type 1463 +#define _loop1_216_type 1464 +#define _tmp_217_type 1465 +#define _loop0_218_type 1466 +#define _loop1_219_type 1467 +#define _tmp_220_type 1468 +#define _tmp_221_type 1469 +#define _tmp_222_type 1470 +#define _tmp_223_type 1471 +#define _tmp_224_type 1472 +#define _tmp_225_type 1473 +#define _tmp_226_type 1474 +#define _tmp_227_type 1475 +#define _tmp_228_type 1476 +#define _tmp_229_type 1477 +#define _loop0_231_type 1478 +#define _gather_230_type 1479 +#define _tmp_232_type 1480 +#define _tmp_233_type 1481 +#define _tmp_234_type 1482 +#define _tmp_235_type 1483 +#define _tmp_236_type 1484 +#define _tmp_237_type 1485 +#define _tmp_238_type 1486 +#define _loop0_239_type 1487 +#define _tmp_240_type 1488 +#define _tmp_241_type 1489 +#define _tmp_242_type 1490 +#define _tmp_243_type 1491 +#define _tmp_244_type 1492 +#define _tmp_245_type 1493 +#define _tmp_246_type 1494 +#define _tmp_247_type 1495 +#define _tmp_248_type 1496 +#define _tmp_249_type 1497 +#define _tmp_250_type 1498 +#define _tmp_251_type 1499 +#define _tmp_252_type 1500 +#define _tmp_253_type 1501 +#define _tmp_254_type 1502 +#define _tmp_255_type 1503 +#define _loop0_256_type 1504 +#define _tmp_257_type 1505 +#define _tmp_258_type 1506 +#define _tmp_259_type 1507 +#define _tmp_260_type 1508 +#define _tmp_261_type 1509 +#define _tmp_262_type 1510 +#define _tmp_263_type 1511 +#define _tmp_264_type 1512 +#define _tmp_265_type 1513 +#define _tmp_266_type 1514 +#define _tmp_267_type 1515 +#define _tmp_268_type 1516 +#define _tmp_269_type 1517 +#define _tmp_270_type 1518 +#define _tmp_271_type 1519 +#define _tmp_272_type 1520 +#define _loop0_274_type 1521 +#define _gather_273_type 1522 +#define _tmp_275_type 1523 +#define _tmp_276_type 1524 +#define _tmp_277_type 1525 +#define _tmp_278_type 1526 +#define _tmp_279_type 1527 +#define _tmp_280_type 1528 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -714,6 +716,8 @@ static asdl_type_param_seq* type_params_rule(Parser *p); static asdl_type_param_seq* type_param_seq_rule(Parser *p); static type_param_ty type_param_rule(Parser *p); static expr_ty type_param_bound_rule(Parser *p); +static expr_ty type_param_default_rule(Parser *p); +static expr_ty type_param_starred_default_rule(Parser *p); static expr_ty expressions_rule(Parser *p); static expr_ty expression_rule(Parser *p); static expr_ty yield_expr_rule(Parser *p); @@ -10646,11 +10650,11 @@ type_param_seq_rule(Parser *p) } // type_param: -// | NAME type_param_bound? +// | NAME type_param_bound? type_param_default? // | '*' NAME ':' expression -// | '*' NAME +// | '*' NAME type_param_starred_default? // | '**' NAME ':' expression -// | '**' NAME +// | '**' NAME type_param_default? static type_param_ty type_param_rule(Parser *p) { @@ -10676,21 +10680,24 @@ type_param_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // NAME type_param_bound? + { // NAME type_param_bound? type_param_default? if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> type_param[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME type_param_bound?")); + D(fprintf(stderr, "%*c> type_param[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME type_param_bound? type_param_default?")); expr_ty a; void *b; + void *c; if ( (a = _PyPegen_name_token(p)) // NAME && (b = type_param_bound_rule(p), !p->error_indicator) // type_param_bound? + && + (c = type_param_default_rule(p), !p->error_indicator) // type_param_default? ) { - D(fprintf(stderr, "%*c+ type_param[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME type_param_bound?")); + D(fprintf(stderr, "%*c+ type_param[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME type_param_bound? type_param_default?")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -10700,7 +10707,7 @@ type_param_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyAST_TypeVar ( a -> v . Name . id , b , EXTRA ); + _res = _PyAST_TypeVar ( a -> v . Name . id , b , c , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -10710,7 +10717,7 @@ type_param_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s type_param[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME type_param_bound?")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME type_param_bound? type_param_default?")); } { // '*' NAME ':' expression if (p->error_indicator) { @@ -10745,21 +10752,24 @@ type_param_rule(Parser *p) D(fprintf(stderr, "%*c%s type_param[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*' NAME ':' expression")); } - { // '*' NAME + { // '*' NAME type_param_starred_default? if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> type_param[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' NAME")); + D(fprintf(stderr, "%*c> type_param[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' NAME type_param_starred_default?")); Token * _literal; expr_ty a; + void *b; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && (a = _PyPegen_name_token(p)) // NAME + && + (b = type_param_starred_default_rule(p), !p->error_indicator) // type_param_starred_default? ) { - D(fprintf(stderr, "%*c+ type_param[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' NAME")); + D(fprintf(stderr, "%*c+ type_param[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' NAME type_param_starred_default?")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -10769,7 +10779,7 @@ type_param_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyAST_TypeVarTuple ( a -> v . Name . id , EXTRA ); + _res = _PyAST_TypeVarTuple ( a -> v . Name . id , b , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -10779,7 +10789,7 @@ type_param_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s type_param[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*' NAME")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*' NAME type_param_starred_default?")); } { // '**' NAME ':' expression if (p->error_indicator) { @@ -10814,21 +10824,24 @@ type_param_rule(Parser *p) D(fprintf(stderr, "%*c%s type_param[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' NAME ':' expression")); } - { // '**' NAME + { // '**' NAME type_param_default? if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> type_param[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**' NAME")); + D(fprintf(stderr, "%*c> type_param[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**' NAME type_param_default?")); Token * _literal; expr_ty a; + void *b; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' && (a = _PyPegen_name_token(p)) // NAME + && + (b = type_param_default_rule(p), !p->error_indicator) // type_param_default? ) { - D(fprintf(stderr, "%*c+ type_param[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' NAME")); + D(fprintf(stderr, "%*c+ type_param[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' NAME type_param_default?")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -10838,7 +10851,7 @@ type_param_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyAST_ParamSpec ( a -> v . Name . id , EXTRA ); + _res = _PyAST_ParamSpec ( a -> v . Name . id , b , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -10848,7 +10861,7 @@ type_param_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s type_param[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' NAME")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' NAME type_param_default?")); } _res = NULL; done: @@ -10903,6 +10916,98 @@ type_param_bound_rule(Parser *p) return _res; } +// type_param_default: '=' expression +static expr_ty +type_param_default_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + expr_ty _res = NULL; + int _mark = p->mark; + { // '=' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> type_param_default[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'=' expression")); + Token * _literal; + expr_ty e; + if ( + (_literal = _PyPegen_expect_token(p, 22)) // token='=' + && + (e = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ type_param_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' expression")); + _res = CHECK_VERSION ( expr_ty , 13 , "Type parameter defaults are" , e ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s type_param_default[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'=' expression")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// type_param_starred_default: '=' star_expression +static expr_ty +type_param_starred_default_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + expr_ty _res = NULL; + int _mark = p->mark; + { // '=' star_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> type_param_starred_default[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'=' star_expression")); + Token * _literal; + expr_ty e; + if ( + (_literal = _PyPegen_expect_token(p, 22)) // token='=' + && + (e = star_expression_rule(p)) // star_expression + ) + { + D(fprintf(stderr, "%*c+ type_param_starred_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' star_expression")); + _res = CHECK_VERSION ( expr_ty , 13 , "Type parameter defaults are" , e ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s type_param_starred_default[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'=' star_expression")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // expressions: expression ((',' expression))+ ','? | expression ',' | expression static expr_ty expressions_rule(Parser *p) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 60b46263a0d329..cc7734e0dbbf9f 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -203,6 +203,7 @@ void _PyAST_Fini(PyInterpreterState *interp) Py_CLEAR(state->conversion); Py_CLEAR(state->ctx); Py_CLEAR(state->decorator_list); + Py_CLEAR(state->default_value); Py_CLEAR(state->defaults); Py_CLEAR(state->elt); Py_CLEAR(state->elts); @@ -311,6 +312,7 @@ static int init_identifiers(struct ast_state *state) if ((state->conversion = PyUnicode_InternFromString("conversion")) == NULL) return -1; if ((state->ctx = PyUnicode_InternFromString("ctx")) == NULL) return -1; if ((state->decorator_list = PyUnicode_InternFromString("decorator_list")) == NULL) return -1; + if ((state->default_value = PyUnicode_InternFromString("default_value")) == NULL) return -1; if ((state->defaults = PyUnicode_InternFromString("defaults")) == NULL) return -1; if ((state->elt = PyUnicode_InternFromString("elt")) == NULL) return -1; if ((state->elts = PyUnicode_InternFromString("elts")) == NULL) return -1; @@ -809,12 +811,15 @@ static PyObject* ast2obj_type_param(struct ast_state *state, struct validator static const char * const TypeVar_fields[]={ "name", "bound", + "default_value", }; static const char * const ParamSpec_fields[]={ "name", + "default_value", }; static const char * const TypeVarTuple_fields[]={ "name", + "default_value", }; @@ -4913,6 +4918,22 @@ add_ast_annotations(struct ast_state *state) return 0; } } + { + PyObject *type = state->expr_type; + type = _Py_union_type_or(type, Py_None); + cond = type != NULL; + if (!cond) { + Py_DECREF(TypeVar_annotations); + return 0; + } + cond = PyDict_SetItemString(TypeVar_annotations, "default_value", type) + == 0; + Py_DECREF(type); + if (!cond) { + Py_DECREF(TypeVar_annotations); + return 0; + } + } cond = PyObject_SetAttrString(state->TypeVar_type, "_field_types", TypeVar_annotations) == 0; if (!cond) { @@ -4938,6 +4959,22 @@ add_ast_annotations(struct ast_state *state) return 0; } } + { + PyObject *type = state->expr_type; + type = _Py_union_type_or(type, Py_None); + cond = type != NULL; + if (!cond) { + Py_DECREF(ParamSpec_annotations); + return 0; + } + cond = PyDict_SetItemString(ParamSpec_annotations, "default_value", + type) == 0; + Py_DECREF(type); + if (!cond) { + Py_DECREF(ParamSpec_annotations); + return 0; + } + } cond = PyObject_SetAttrString(state->ParamSpec_type, "_field_types", ParamSpec_annotations) == 0; if (!cond) { @@ -4964,6 +5001,22 @@ add_ast_annotations(struct ast_state *state) return 0; } } + { + PyObject *type = state->expr_type; + type = _Py_union_type_or(type, Py_None); + cond = type != NULL; + if (!cond) { + Py_DECREF(TypeVarTuple_annotations); + return 0; + } + cond = PyDict_SetItemString(TypeVarTuple_annotations, "default_value", + type) == 0; + Py_DECREF(type); + if (!cond) { + Py_DECREF(TypeVarTuple_annotations); + return 0; + } + } cond = PyObject_SetAttrString(state->TypeVarTuple_type, "_field_types", TypeVarTuple_annotations) == 0; if (!cond) { @@ -6243,28 +6296,37 @@ init_types(struct ast_state *state) if (!state->TypeIgnore_type) return -1; state->type_param_type = make_type(state, "type_param", state->AST_type, NULL, 0, - "type_param = TypeVar(identifier name, expr? bound)\n" - " | ParamSpec(identifier name)\n" - " | TypeVarTuple(identifier name)"); + "type_param = TypeVar(identifier name, expr? bound, expr? default_value)\n" + " | ParamSpec(identifier name, expr? default_value)\n" + " | TypeVarTuple(identifier name, expr? default_value)"); if (!state->type_param_type) return -1; if (add_attributes(state, state->type_param_type, type_param_attributes, 4) < 0) return -1; state->TypeVar_type = make_type(state, "TypeVar", state->type_param_type, - TypeVar_fields, 2, - "TypeVar(identifier name, expr? bound)"); + TypeVar_fields, 3, + "TypeVar(identifier name, expr? bound, expr? default_value)"); if (!state->TypeVar_type) return -1; if (PyObject_SetAttr(state->TypeVar_type, state->bound, Py_None) == -1) return -1; + if (PyObject_SetAttr(state->TypeVar_type, state->default_value, Py_None) == + -1) + return -1; state->ParamSpec_type = make_type(state, "ParamSpec", state->type_param_type, ParamSpec_fields, - 1, - "ParamSpec(identifier name)"); + 2, + "ParamSpec(identifier name, expr? default_value)"); if (!state->ParamSpec_type) return -1; + if (PyObject_SetAttr(state->ParamSpec_type, state->default_value, Py_None) + == -1) + return -1; state->TypeVarTuple_type = make_type(state, "TypeVarTuple", state->type_param_type, - TypeVarTuple_fields, 1, - "TypeVarTuple(identifier name)"); + TypeVarTuple_fields, 2, + "TypeVarTuple(identifier name, expr? default_value)"); if (!state->TypeVarTuple_type) return -1; + if (PyObject_SetAttr(state->TypeVarTuple_type, state->default_value, + Py_None) == -1) + return -1; if (!add_ast_annotations(state)) { return -1; @@ -8055,8 +8117,9 @@ _PyAST_TypeIgnore(int lineno, string tag, PyArena *arena) } type_param_ty -_PyAST_TypeVar(identifier name, expr_ty bound, int lineno, int col_offset, int - end_lineno, int end_col_offset, PyArena *arena) +_PyAST_TypeVar(identifier name, expr_ty bound, expr_ty default_value, int + lineno, int col_offset, int end_lineno, int end_col_offset, + PyArena *arena) { type_param_ty p; if (!name) { @@ -8070,6 +8133,7 @@ _PyAST_TypeVar(identifier name, expr_ty bound, int lineno, int col_offset, int p->kind = TypeVar_kind; p->v.TypeVar.name = name; p->v.TypeVar.bound = bound; + p->v.TypeVar.default_value = default_value; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -8078,8 +8142,8 @@ _PyAST_TypeVar(identifier name, expr_ty bound, int lineno, int col_offset, int } type_param_ty -_PyAST_ParamSpec(identifier name, int lineno, int col_offset, int end_lineno, - int end_col_offset, PyArena *arena) +_PyAST_ParamSpec(identifier name, expr_ty default_value, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena *arena) { type_param_ty p; if (!name) { @@ -8092,6 +8156,7 @@ _PyAST_ParamSpec(identifier name, int lineno, int col_offset, int end_lineno, return NULL; p->kind = ParamSpec_kind; p->v.ParamSpec.name = name; + p->v.ParamSpec.default_value = default_value; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -8100,8 +8165,9 @@ _PyAST_ParamSpec(identifier name, int lineno, int col_offset, int end_lineno, } type_param_ty -_PyAST_TypeVarTuple(identifier name, int lineno, int col_offset, int - end_lineno, int end_col_offset, PyArena *arena) +_PyAST_TypeVarTuple(identifier name, expr_ty default_value, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena) { type_param_ty p; if (!name) { @@ -8114,6 +8180,7 @@ _PyAST_TypeVarTuple(identifier name, int lineno, int col_offset, int return NULL; p->kind = TypeVarTuple_kind; p->v.TypeVarTuple.name = name; + p->v.TypeVarTuple.default_value = default_value; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -10079,6 +10146,11 @@ ast2obj_type_param(struct ast_state *state, struct validator *vstate, void* _o) if (PyObject_SetAttr(result, state->bound, value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_expr(state, vstate, o->v.TypeVar.default_value); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->default_value, value) == -1) + goto failed; + Py_DECREF(value); break; case ParamSpec_kind: tp = (PyTypeObject *)state->ParamSpec_type; @@ -10089,6 +10161,11 @@ ast2obj_type_param(struct ast_state *state, struct validator *vstate, void* _o) if (PyObject_SetAttr(result, state->name, value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_expr(state, vstate, o->v.ParamSpec.default_value); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->default_value, value) == -1) + goto failed; + Py_DECREF(value); break; case TypeVarTuple_kind: tp = (PyTypeObject *)state->TypeVarTuple_type; @@ -10099,6 +10176,11 @@ ast2obj_type_param(struct ast_state *state, struct validator *vstate, void* _o) if (PyObject_SetAttr(result, state->name, value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_expr(state, vstate, o->v.TypeVarTuple.default_value); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->default_value, value) == -1) + goto failed; + Py_DECREF(value); break; } value = ast2obj_int(state, vstate, o->lineno); @@ -16935,6 +17017,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (isinstance) { identifier name; expr_ty bound; + expr_ty default_value; if (PyObject_GetOptionalAttr(obj, state->name, &tmp) < 0) { return -1; @@ -16970,8 +17053,25 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (res != 0) goto failed; Py_CLEAR(tmp); } - *out = _PyAST_TypeVar(name, bound, lineno, col_offset, end_lineno, - end_col_offset, arena); + if (PyObject_GetOptionalAttr(obj, state->default_value, &tmp) < 0) { + return -1; + } + if (tmp == NULL || tmp == Py_None) { + Py_CLEAR(tmp); + default_value = NULL; + } + else { + int res; + if (_Py_EnterRecursiveCall(" while traversing 'TypeVar' node")) { + goto failed; + } + res = obj2ast_expr(state, tmp, &default_value, arena); + _Py_LeaveRecursiveCall(); + if (res != 0) goto failed; + Py_CLEAR(tmp); + } + *out = _PyAST_TypeVar(name, bound, default_value, lineno, col_offset, + end_lineno, end_col_offset, arena); if (*out == NULL) goto failed; return 0; } @@ -16982,6 +17082,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, } if (isinstance) { identifier name; + expr_ty default_value; if (PyObject_GetOptionalAttr(obj, state->name, &tmp) < 0) { return -1; @@ -17000,8 +17101,25 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (res != 0) goto failed; Py_CLEAR(tmp); } - *out = _PyAST_ParamSpec(name, lineno, col_offset, end_lineno, - end_col_offset, arena); + if (PyObject_GetOptionalAttr(obj, state->default_value, &tmp) < 0) { + return -1; + } + if (tmp == NULL || tmp == Py_None) { + Py_CLEAR(tmp); + default_value = NULL; + } + else { + int res; + if (_Py_EnterRecursiveCall(" while traversing 'ParamSpec' node")) { + goto failed; + } + res = obj2ast_expr(state, tmp, &default_value, arena); + _Py_LeaveRecursiveCall(); + if (res != 0) goto failed; + Py_CLEAR(tmp); + } + *out = _PyAST_ParamSpec(name, default_value, lineno, col_offset, + end_lineno, end_col_offset, arena); if (*out == NULL) goto failed; return 0; } @@ -17012,6 +17130,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, } if (isinstance) { identifier name; + expr_ty default_value; if (PyObject_GetOptionalAttr(obj, state->name, &tmp) < 0) { return -1; @@ -17030,8 +17149,25 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (res != 0) goto failed; Py_CLEAR(tmp); } - *out = _PyAST_TypeVarTuple(name, lineno, col_offset, end_lineno, - end_col_offset, arena); + if (PyObject_GetOptionalAttr(obj, state->default_value, &tmp) < 0) { + return -1; + } + if (tmp == NULL || tmp == Py_None) { + Py_CLEAR(tmp); + default_value = NULL; + } + else { + int res; + if (_Py_EnterRecursiveCall(" while traversing 'TypeVarTuple' node")) { + goto failed; + } + res = obj2ast_expr(state, tmp, &default_value, arena); + _Py_LeaveRecursiveCall(); + if (res != 0) goto failed; + Py_CLEAR(tmp); + } + *out = _PyAST_TypeVarTuple(name, default_value, lineno, col_offset, + end_lineno, end_col_offset, arena); if (*out == NULL) goto failed; return 0; } diff --git a/Python/ast.c b/Python/ast.c index 71b09d889f17c1..1d1a48ec885686 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -1011,13 +1011,19 @@ validate_typeparam(struct validator *state, type_param_ty tp) case TypeVar_kind: ret = validate_name(tp->v.TypeVar.name) && (!tp->v.TypeVar.bound || - validate_expr(state, tp->v.TypeVar.bound, Load)); + validate_expr(state, tp->v.TypeVar.bound, Load)) && + (!tp->v.TypeVar.default_value || + validate_expr(state, tp->v.TypeVar.default_value, Load)); break; case ParamSpec_kind: - ret = validate_name(tp->v.ParamSpec.name); + ret = validate_name(tp->v.ParamSpec.name) && + (!tp->v.ParamSpec.default_value || + validate_expr(state, tp->v.ParamSpec.default_value, Load)); break; case TypeVarTuple_kind: - ret = validate_name(tp->v.TypeVarTuple.name); + ret = validate_name(tp->v.TypeVarTuple.name) && + (!tp->v.TypeVarTuple.default_value || + validate_expr(state, tp->v.TypeVarTuple.default_value, Load)); break; } return ret; diff --git a/Python/compile.c b/Python/compile.c index feedd988834397..ec47af151f9958 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2116,6 +2116,36 @@ wrap_in_stopiteration_handler(struct compiler *c) return SUCCESS; } +static int +compiler_type_param_bound_or_default(struct compiler *c, expr_ty e, + identifier name, void *key, + bool allow_starred) +{ + if (compiler_enter_scope(c, name, COMPILER_SCOPE_TYPEPARAMS, + key, e->lineno) == -1) { + return ERROR; + } + if (allow_starred && e->kind == Starred_kind) { + VISIT(c, expr, e->v.Starred.value); + ADDOP_I(c, LOC(e), UNPACK_SEQUENCE, (Py_ssize_t)1); + } + else { + VISIT(c, expr, e); + } + ADDOP_IN_SCOPE(c, LOC(e), RETURN_VALUE); + PyCodeObject *co = optimize_and_assemble(c, 1); + compiler_exit_scope(c); + if (co == NULL) { + return ERROR; + } + if (compiler_make_closure(c, LOC(e), co, 0) < 0) { + Py_DECREF(co); + return ERROR; + } + Py_DECREF(co); + return SUCCESS; +} + static int compiler_type_params(struct compiler *c, asdl_type_param_seq *type_params) { @@ -2123,6 +2153,7 @@ compiler_type_params(struct compiler *c, asdl_type_param_seq *type_params) return SUCCESS; } Py_ssize_t n = asdl_seq_LEN(type_params); + bool seen_default = false; for (Py_ssize_t i = 0; i < n; i++) { type_param_ty typeparam = asdl_seq_GET(type_params, i); @@ -2132,22 +2163,10 @@ compiler_type_params(struct compiler *c, asdl_type_param_seq *type_params) ADDOP_LOAD_CONST(c, loc, typeparam->v.TypeVar.name); if (typeparam->v.TypeVar.bound) { expr_ty bound = typeparam->v.TypeVar.bound; - if (compiler_enter_scope(c, typeparam->v.TypeVar.name, COMPILER_SCOPE_TYPEPARAMS, - (void *)typeparam, bound->lineno) == -1) { + if (compiler_type_param_bound_or_default(c, bound, typeparam->v.TypeVar.name, + (void *)typeparam, false) < 0) { return ERROR; } - VISIT_IN_SCOPE(c, expr, bound); - ADDOP_IN_SCOPE(c, loc, RETURN_VALUE); - PyCodeObject *co = optimize_and_assemble(c, 1); - compiler_exit_scope(c); - if (co == NULL) { - return ERROR; - } - if (compiler_make_closure(c, loc, co, 0) < 0) { - Py_DECREF(co); - return ERROR; - } - Py_DECREF(co); int intrinsic = bound->kind == Tuple_kind ? INTRINSIC_TYPEVAR_WITH_CONSTRAINTS @@ -2157,18 +2176,60 @@ compiler_type_params(struct compiler *c, asdl_type_param_seq *type_params) else { ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_TYPEVAR); } + if (typeparam->v.TypeVar.default_value) { + seen_default = true; + expr_ty default_ = typeparam->v.TypeVar.default_value; + if (compiler_type_param_bound_or_default(c, default_, typeparam->v.TypeVar.name, + (void *)((uintptr_t)typeparam + 1), false) < 0) { + return ERROR; + } + ADDOP_I(c, loc, CALL_INTRINSIC_2, INTRINSIC_SET_TYPEPARAM_DEFAULT); + } + else if (seen_default) { + return compiler_error(c, loc, "non-default type parameter '%U' " + "follows default type parameter", + typeparam->v.TypeVar.name); + } ADDOP_I(c, loc, COPY, 1); RETURN_IF_ERROR(compiler_nameop(c, loc, typeparam->v.TypeVar.name, Store)); break; case TypeVarTuple_kind: ADDOP_LOAD_CONST(c, loc, typeparam->v.TypeVarTuple.name); ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_TYPEVARTUPLE); + if (typeparam->v.TypeVarTuple.default_value) { + expr_ty default_ = typeparam->v.TypeVarTuple.default_value; + if (compiler_type_param_bound_or_default(c, default_, typeparam->v.TypeVarTuple.name, + (void *)typeparam, true) < 0) { + return ERROR; + } + ADDOP_I(c, loc, CALL_INTRINSIC_2, INTRINSIC_SET_TYPEPARAM_DEFAULT); + seen_default = true; + } + else if (seen_default) { + return compiler_error(c, loc, "non-default type parameter '%U' " + "follows default type parameter", + typeparam->v.TypeVarTuple.name); + } ADDOP_I(c, loc, COPY, 1); RETURN_IF_ERROR(compiler_nameop(c, loc, typeparam->v.TypeVarTuple.name, Store)); break; case ParamSpec_kind: ADDOP_LOAD_CONST(c, loc, typeparam->v.ParamSpec.name); ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_PARAMSPEC); + if (typeparam->v.ParamSpec.default_value) { + expr_ty default_ = typeparam->v.ParamSpec.default_value; + if (compiler_type_param_bound_or_default(c, default_, typeparam->v.ParamSpec.name, + (void *)typeparam, false) < 0) { + return ERROR; + } + ADDOP_I(c, loc, CALL_INTRINSIC_2, INTRINSIC_SET_TYPEPARAM_DEFAULT); + seen_default = true; + } + else if (seen_default) { + return compiler_error(c, loc, "non-default type parameter '%U' " + "follows default type parameter", + typeparam->v.ParamSpec.name); + } ADDOP_I(c, loc, COPY, 1); RETURN_IF_ERROR(compiler_nameop(c, loc, typeparam->v.ParamSpec.name, Store)); break; diff --git a/Python/intrinsics.c b/Python/intrinsics.c index d3146973b75178..5b10c3ce7d5d77 100644 --- a/Python/intrinsics.c +++ b/Python/intrinsics.c @@ -265,6 +265,7 @@ _PyIntrinsics_BinaryFunctions[] = { INTRINSIC_FUNC_ENTRY(INTRINSIC_TYPEVAR_WITH_BOUND, make_typevar_with_bound) INTRINSIC_FUNC_ENTRY(INTRINSIC_TYPEVAR_WITH_CONSTRAINTS, make_typevar_with_constraints) INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_FUNCTION_TYPE_PARAMS, _Py_set_function_type_params) + INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_TYPEPARAM_DEFAULT, _Py_set_typeparam_default) }; #undef INTRINSIC_FUNC_ENTRY diff --git a/Python/symtable.c b/Python/symtable.c index eecd159b2c3f17..2ec21a2d376da2 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -2275,6 +2275,24 @@ symtable_visit_expr(struct symtable *st, expr_ty e) VISIT_QUIT(st, 1); } +static int +symtable_visit_type_param_bound_or_default(struct symtable *st, expr_ty e, identifier name, void *key) +{ + if (e) { + int is_in_class = st->st_cur->ste_can_see_class_scope; + if (!symtable_enter_block(st, name, TypeVarBoundBlock, key, LOCATION(e))) + return 0; + st->st_cur->ste_can_see_class_scope = is_in_class; + if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(e))) { + VISIT_QUIT(st, 0); + } + VISIT(st, expr, e); + if (!symtable_exit_block(st)) + return 0; + } + return 1; +} + static int symtable_visit_type_param(struct symtable *st, type_param_ty tp) { @@ -2287,28 +2305,39 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) case TypeVar_kind: if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) VISIT_QUIT(st, 0); - if (tp->v.TypeVar.bound) { - int is_in_class = st->st_cur->ste_can_see_class_scope; - if (!symtable_enter_block(st, tp->v.TypeVar.name, - TypeVarBoundBlock, (void *)tp, - LOCATION(tp))) - VISIT_QUIT(st, 0); - st->st_cur->ste_can_see_class_scope = is_in_class; - if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(tp->v.TypeVar.bound))) { - VISIT_QUIT(st, 0); - } - VISIT(st, expr, tp->v.TypeVar.bound); - if (!symtable_exit_block(st)) - VISIT_QUIT(st, 0); + + // We must use a different key for the bound and default. The obvious choice would be to + // use the .bound and .default_value pointers, but that fails when the expression immediately + // inside the bound or default is a comprehension: we would reuse the same key for + // the comprehension scope. Therefore, use the address + 1 as the second key. + // The only requirement for the key is that it is unique and it matches the logic in + // compile.c where the scope is retrieved. + if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.bound, tp->v.TypeVar.name, + (void *)tp)) { + VISIT_QUIT(st, 0); + } + if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name, + (void *)((uintptr_t)tp + 1))) { + VISIT_QUIT(st, 0); } break; case TypeVarTuple_kind: - if (!symtable_add_def(st, tp->v.TypeVarTuple.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) + if (!symtable_add_def(st, tp->v.TypeVarTuple.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) { VISIT_QUIT(st, 0); + } + if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVarTuple.default_value, tp->v.TypeVarTuple.name, + (void *)tp)) { + VISIT_QUIT(st, 0); + } break; case ParamSpec_kind: - if (!symtable_add_def(st, tp->v.ParamSpec.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) + if (!symtable_add_def(st, tp->v.ParamSpec.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) { VISIT_QUIT(st, 0); + } + if (!symtable_visit_type_param_bound_or_default(st, tp->v.ParamSpec.default_value, tp->v.ParamSpec.name, + (void *)tp)) { + VISIT_QUIT(st, 0); + } break; } VISIT_QUIT(st, 1); diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 79a8f850ec1451..b58e9d9fae380f 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -92,6 +92,7 @@ Objects/typeobject.c - PyBaseObject_Type - Objects/typeobject.c - PySuper_Type - Objects/typeobject.c - PyType_Type - Objects/typevarobject.c - _PyTypeAlias_Type - +Objects/typevarobject.c - _PyNoDefault_Type - Objects/unicodeobject.c - PyUnicodeIter_Type - Objects/unicodeobject.c - PyUnicode_Type - Objects/weakrefobject.c - _PyWeakref_CallableProxyType - @@ -310,6 +311,7 @@ Objects/object.c - _Py_NotImplementedStruct - Objects/setobject.c - _dummy_struct - Objects/setobject.c - _PySet_Dummy - Objects/sliceobject.c - _Py_EllipsisObject - +Objects/typevarobject.c - _Py_NoDefaultStruct - Python/instrumentation.c - _PyInstrumentation_DISABLE - Python/instrumentation.c - _PyInstrumentation_MISSING - From 37ccf167869d101c4021c435868b7f89ccda8148 Mon Sep 17 00:00:00 2001 From: Alexander Kanavin Date: Fri, 3 May 2024 15:34:05 +0200 Subject: [PATCH 179/217] gh-101732: Modules/_ssl.c: use Y2038 compatible openssl function when available (GH-118425) --- .../Library/2024-04-30-12-59-04.gh-issue-101732.29zUDu.rst | 1 + Modules/_ssl.c | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-04-30-12-59-04.gh-issue-101732.29zUDu.rst diff --git a/Misc/NEWS.d/next/Library/2024-04-30-12-59-04.gh-issue-101732.29zUDu.rst b/Misc/NEWS.d/next/Library/2024-04-30-12-59-04.gh-issue-101732.29zUDu.rst new file mode 100644 index 00000000000000..354dfc46362062 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-30-12-59-04.gh-issue-101732.29zUDu.rst @@ -0,0 +1 @@ +Use a Y2038 compatible openssl time function when available. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index f7fdbf4b6f90cb..885a9c25967d2d 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -5329,7 +5329,11 @@ PySSLSession_clear(PySSLSession *self) static PyObject * PySSLSession_get_time(PySSLSession *self, void *closure) { +#if OPENSSL_VERSION_NUMBER >= 0x30300000L + return _PyLong_FromTime_t(SSL_SESSION_get_time_ex(self->session)); +#else return PyLong_FromLong(SSL_SESSION_get_time(self->session)); +#endif } PyDoc_STRVAR(PySSLSession_get_time_doc, From c8deb1e4b495bf97ab00c710dfd63f227e1fb645 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 3 May 2024 08:05:19 -0600 Subject: [PATCH 180/217] gh-118513: Fix sibling comprehensions with a name bound in one and global in the other (#118526) Co-authored-by: Jelle Zijlstra Co-authored-by: Kirill Podoprigora --- Lib/test/test_listcomps.py | 14 ++++ ...-05-02-21-19-35.gh-issue-118513.qHODjb.rst | 1 + Python/compile.c | 81 ++++++++++--------- 3 files changed, 57 insertions(+), 39 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-05-02-21-19-35.gh-issue-118513.qHODjb.rst diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py index 2868dd01545b95..df1debf35210ca 100644 --- a/Lib/test/test_listcomps.py +++ b/Lib/test/test_listcomps.py @@ -666,6 +666,20 @@ def test_code_replace_extended_arg(self): self._check_in_scopes(code, expected) self._check_in_scopes(code, expected, exec_func=self._replacing_exec) + def test_multiple_comprehension_name_reuse(self): + code = """ + [x for x in [1]] + y = [x for _ in [1]] + """ + self._check_in_scopes(code, {"y": [3]}, ns={"x": 3}) + + code = """ + x = 2 + [x for x in [1]] + y = [x for _ in [1]] + """ + self._check_in_scopes(code, {"x": 2, "y": [3]}, ns={"x": 3}, scopes=["class"]) + self._check_in_scopes(code, {"x": 2, "y": [2]}, ns={"x": 3}, scopes=["function", "module"]) __test__ = {'doctests' : doctests} diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-02-21-19-35.gh-issue-118513.qHODjb.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-21-19-35.gh-issue-118513.qHODjb.rst new file mode 100644 index 00000000000000..b7155b4474d140 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-21-19-35.gh-issue-118513.qHODjb.rst @@ -0,0 +1 @@ +Fix incorrect :exc:`UnboundLocalError` when two comprehensions in the same function both reference the same name, and in one comprehension the name is bound while in the other it's an implicit global. diff --git a/Python/compile.c b/Python/compile.c index ec47af151f9958..35a7848f021c31 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5561,10 +5561,48 @@ push_inlined_comprehension_state(struct compiler *c, location loc, while (PyDict_Next(entry->ste_symbols, &pos, &k, &v)) { assert(PyLong_Check(v)); long symbol = PyLong_AS_LONG(v); - // only values bound in the comprehension (DEF_LOCAL) need to be handled - // at all; DEF_LOCAL | DEF_NONLOCAL can occur in the case of an - // assignment expression to a nonlocal in the comprehension, these don't - // need handling here since they shouldn't be isolated + long scope = (symbol >> SCOPE_OFFSET) & SCOPE_MASK; + PyObject *outv = PyDict_GetItemWithError(c->u->u_ste->ste_symbols, k); + if (outv == NULL) { + if (PyErr_Occurred()) { + return ERROR; + } + outv = _PyLong_GetZero(); + } + assert(PyLong_CheckExact(outv)); + long outsc = (PyLong_AS_LONG(outv) >> SCOPE_OFFSET) & SCOPE_MASK; + // If a name has different scope inside than outside the comprehension, + // we need to temporarily handle it with the right scope while + // compiling the comprehension. If it's free in the comprehension + // scope, no special handling; it should be handled the same as the + // enclosing scope. (If it's free in outer scope and cell in inner + // scope, we can't treat it as both cell and free in the same function, + // but treating it as free throughout is fine; it's *_DEREF + // either way.) + if ((scope != outsc && scope != FREE && !(scope == CELL && outsc == FREE)) + || in_class_block) { + if (state->temp_symbols == NULL) { + state->temp_symbols = PyDict_New(); + if (state->temp_symbols == NULL) { + return ERROR; + } + } + // update the symbol to the in-comprehension version and save + // the outer version; we'll restore it after running the + // comprehension + Py_INCREF(outv); + if (PyDict_SetItem(c->u->u_ste->ste_symbols, k, v) < 0) { + Py_DECREF(outv); + return ERROR; + } + if (PyDict_SetItem(state->temp_symbols, k, outv) < 0) { + Py_DECREF(outv); + return ERROR; + } + Py_DECREF(outv); + } + // locals handling for names bound in comprehension (DEF_LOCAL | + // DEF_NONLOCAL occurs in assignment expression to nonlocal) if ((symbol & DEF_LOCAL && !(symbol & DEF_NONLOCAL)) || in_class_block) { if (!_PyST_IsFunctionLike(c->u->u_ste)) { // non-function scope: override this name to use fast locals @@ -5589,41 +5627,6 @@ push_inlined_comprehension_state(struct compiler *c, location loc, } } } - long scope = (symbol >> SCOPE_OFFSET) & SCOPE_MASK; - PyObject *outv = PyDict_GetItemWithError(c->u->u_ste->ste_symbols, k); - if (outv == NULL) { - outv = _PyLong_GetZero(); - } - assert(PyLong_Check(outv)); - long outsc = (PyLong_AS_LONG(outv) >> SCOPE_OFFSET) & SCOPE_MASK; - if (scope != outsc && !(scope == CELL && outsc == FREE)) { - // If a name has different scope inside than outside the - // comprehension, we need to temporarily handle it with the - // right scope while compiling the comprehension. (If it's free - // in outer scope and cell in inner scope, we can't treat it as - // both cell and free in the same function, but treating it as - // free throughout is fine; it's *_DEREF either way.) - - if (state->temp_symbols == NULL) { - state->temp_symbols = PyDict_New(); - if (state->temp_symbols == NULL) { - return ERROR; - } - } - // update the symbol to the in-comprehension version and save - // the outer version; we'll restore it after running the - // comprehension - Py_INCREF(outv); - if (PyDict_SetItem(c->u->u_ste->ste_symbols, k, v) < 0) { - Py_DECREF(outv); - return ERROR; - } - if (PyDict_SetItem(state->temp_symbols, k, outv) < 0) { - Py_DECREF(outv); - return ERROR; - } - Py_DECREF(outv); - } // local names bound in comprehension must be isolated from // outer scope; push existing value (which may be NULL if // not defined) on stack From 24e643d4ef024a3561c927dc07c59c435bb27bcc Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 3 May 2024 11:05:30 -0400 Subject: [PATCH 181/217] gh-118527: Use `_Py_ID(__main__)` for main module name (#118528) Most module names are interned and immortalized, but the main module was not. This partially addresses a scaling bottleneck in the free-threaded when creating closure concurrently in the main module. --- Python/pylifecycle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 7726ccc979dbcc..9dc6e3f31128c1 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2348,7 +2348,7 @@ static PyStatus add_main_module(PyInterpreterState *interp) { PyObject *m, *d, *ann_dict; - m = PyImport_AddModule("__main__"); + m = PyImport_AddModuleObject(&_Py_ID(__main__)); if (m == NULL) return _PyStatus_ERR("can't create __main__ module"); From 2dae505e87e3815f087d4b07a71bb2c5cce22304 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 3 May 2024 11:09:57 -0400 Subject: [PATCH 182/217] gh-117514: Add `sys._is_gil_enabled()` function (#118514) The function returns `True` or `False` depending on whether the GIL is currently enabled. In the default build, it always returns `True` because the GIL is always enabled. --- Doc/library/sys.rst | 8 +++++ Lib/test/test_sys.py | 6 ++++ ...-05-02-16-04-51.gh-issue-117514.CJiuC0.rst | 4 +++ Python/clinic/sysmodule.c.h | 30 ++++++++++++++++++- Python/sysmodule.c | 20 +++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-05-02-16-04-51.gh-issue-117514.CJiuC0.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 19d6856efe5d09..91afa9d58e87e6 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1199,6 +1199,14 @@ always available. return value of :func:`intern` around to benefit from it. +.. function:: _is_gil_enabled() + + Return :const:`True` if the :term:`GIL` is enabled and :const:`False` if + it is disabled. + + .. versionadded:: 3.13 + + .. function:: is_finalizing() Return :const:`True` if the main Python interpreter is diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 14ec51eb757e00..df0a6f09c09c08 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1053,6 +1053,12 @@ def test_getallocatedblocks(self): c = sys.getallocatedblocks() self.assertIn(c, range(b - 50, b + 50)) + def test_is_gil_enabled(self): + if support.Py_GIL_DISABLED: + self.assertIs(type(sys._is_gil_enabled()), bool) + else: + self.assertTrue(sys._is_gil_enabled()) + def test_is_finalizing(self): self.assertIs(sys.is_finalizing(), False) # Don't use the atexit module because _Py_Finalizing is only set diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-02-16-04-51.gh-issue-117514.CJiuC0.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-16-04-51.gh-issue-117514.CJiuC0.rst new file mode 100644 index 00000000000000..fc162afb2750c6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-16-04-51.gh-issue-117514.CJiuC0.rst @@ -0,0 +1,4 @@ +Add ``sys._is_gil_enabled()`` function that returns whether the GIL is +currently enabled. In the default build it always returns ``True`` because +the GIL is always enabled. In the free-threaded build, it may return +``True`` or ``False``. diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 31f66e807a8547..0a8704c71e488c 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -1485,6 +1485,34 @@ sys__get_cpu_count_config(PyObject *module, PyObject *Py_UNUSED(ignored)) return return_value; } +PyDoc_STRVAR(sys__is_gil_enabled__doc__, +"_is_gil_enabled($module, /)\n" +"--\n" +"\n" +"Return True if the GIL is currently enabled and False otherwise."); + +#define SYS__IS_GIL_ENABLED_METHODDEF \ + {"_is_gil_enabled", (PyCFunction)sys__is_gil_enabled, METH_NOARGS, sys__is_gil_enabled__doc__}, + +static int +sys__is_gil_enabled_impl(PyObject *module); + +static PyObject * +sys__is_gil_enabled(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = sys__is_gil_enabled_impl(module); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + #ifndef SYS_GETWINDOWSVERSION_METHODDEF #define SYS_GETWINDOWSVERSION_METHODDEF #endif /* !defined(SYS_GETWINDOWSVERSION_METHODDEF) */ @@ -1528,4 +1556,4 @@ sys__get_cpu_count_config(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=518424ee03e353b0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=352ac7a0085e8a1f input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index d3fbfcd3e79636..f469f165e7fb84 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2393,6 +2393,25 @@ sys__get_cpu_count_config_impl(PyObject *module) return config->cpu_count; } +/*[clinic input] +sys._is_gil_enabled -> bool + +Return True if the GIL is currently enabled and False otherwise. +[clinic start generated code]*/ + +static int +sys__is_gil_enabled_impl(PyObject *module) +/*[clinic end generated code: output=57732cf53f5b9120 input=7e9c47f15a00e809]*/ +{ +#ifdef Py_GIL_DISABLED + PyInterpreterState *interp = _PyInterpreterState_GET(); + return interp->ceval.gil->enabled; +#else + return 1; +#endif +} + + static PerfMapState perf_map_state; PyAPI_FUNC(int) PyUnstable_PerfMapState_Init(void) { @@ -2565,6 +2584,7 @@ static PyMethodDef sys_methods[] = { SYS__STATS_DUMP_METHODDEF #endif SYS__GET_CPU_COUNT_CONFIG_METHODDEF + SYS__IS_GIL_ENABLED_METHODDEF {NULL, NULL} // sentinel }; From 3e818afb9b7c557aa633aeb3d5c4959750feeab0 Mon Sep 17 00:00:00 2001 From: mpage Date: Fri, 3 May 2024 08:14:26 -0700 Subject: [PATCH 183/217] gh-118495: Skip test using threads after forking when running with TSAN (#118530) This is unsupported. Note that `skip_unless_reliable_fork()` checks for the conditions used by the decorators that were removed, along with checking for TSAN. --- Lib/test/test_threading.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 0047e8a8798d4e..329767aa82e336 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -49,7 +49,7 @@ def skip_unless_reliable_fork(test): if support.HAVE_ASAN_FORK_BUG: return unittest.skip("libasan has a pthread_create() dead lock related to thread+fork")(test) if support.check_sanitizer(thread=True): - return unittest.skip("TSAN doesn't support threads after fork") + return unittest.skip("TSAN doesn't support threads after fork")(test) return test @@ -781,8 +781,7 @@ def func(): "current is main True\n" ) - @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") - @support.requires_fork() + @skip_unless_reliable_fork @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()") def test_main_thread_after_fork_from_foreign_thread(self, create_dummy=False): code = """if 1: From c2627d6eea924daf80f374c18a5fd73ef61283fa Mon Sep 17 00:00:00 2001 From: Brett Simmers Date: Fri, 3 May 2024 08:30:55 -0700 Subject: [PATCH 184/217] gh-116322: Add Py_mod_gil module slot (#116882) This PR adds the ability to enable the GIL if it was disabled at interpreter startup, and modifies the multi-phase module initialization path to enable the GIL when loading a module, unless that module's spec includes a slot indicating it can run safely without the GIL. PEP 703 called the constant for the slot `Py_mod_gil_not_used`; I went with `Py_MOD_GIL_NOT_USED` for consistency with gh-104148. A warning will be issued up to once per interpreter for the first GIL-using module that is loaded. If `-v` is given, a shorter message will be printed to stderr every time a GIL-using module is loaded (including the first one that issues a warning). --- Doc/c-api/module.rst | 38 ++++++++++++++++ Include/internal/pycore_moduleobject.h | 3 ++ Include/moduleobject.h | 16 ++++++- .../extension/_test_nonmodule_cases.py | 44 +++++++++++++++++++ .../test_importlib/extension/test_loader.py | 35 +++++---------- Lib/test/test_sys.py | 5 ++- ...-03-12-13-51-09.gh-issue-116322.q8TcDQ.rst | 5 +++ Modules/_abc.c | 1 + Modules/_asynciomodule.c | 1 + Modules/_bisectmodule.c | 1 + Modules/_blake2/blake2module.c | 1 + Modules/_bz2module.c | 1 + Modules/_codecsmodule.c | 1 + Modules/_collectionsmodule.c | 1 + Modules/_contextvarsmodule.c | 1 + Modules/_csv.c | 1 + Modules/_ctypes/_ctypes.c | 1 + Modules/_ctypes/_ctypes_test.c | 5 ++- Modules/_curses_panel.c | 1 + Modules/_cursesmodule.c | 3 ++ Modules/_datetimemodule.c | 3 ++ Modules/_dbmmodule.c | 1 + Modules/_decimal/_decimal.c | 1 + Modules/_elementtree.c | 1 + Modules/_functoolsmodule.c | 1 + Modules/_gdbmmodule.c | 1 + Modules/_hashopenssl.c | 1 + Modules/_heapqmodule.c | 1 + Modules/_interpchannelsmodule.c | 1 + Modules/_interpqueuesmodule.c | 1 + Modules/_interpretersmodule.c | 1 + Modules/_io/_iomodule.c | 1 + Modules/_json.c | 1 + Modules/_localemodule.c | 1 + Modules/_lsprof.c | 1 + Modules/_lzmamodule.c | 1 + Modules/_multiprocessing/multiprocessing.c | 1 + Modules/_multiprocessing/posixshmem.c | 5 ++- Modules/_opcode.c | 1 + Modules/_operator.c | 1 + Modules/_pickle.c | 1 + Modules/_posixsubprocess.c | 1 + Modules/_queuemodule.c | 1 + Modules/_randommodule.c | 1 + Modules/_scproxy.c | 5 ++- Modules/_sqlite/module.c | 1 + Modules/_sre/sre.c | 1 + Modules/_ssl.c | 1 + Modules/_stat.c | 1 + Modules/_statisticsmodule.c | 5 ++- Modules/_struct.c | 1 + Modules/_suggestions.c | 14 ++++-- Modules/_sysconfig.c | 1 + Modules/_testbuffer.c | 3 ++ Modules/_testcapimodule.c | 3 ++ Modules/_testclinic.c | 3 ++ Modules/_testclinic_limited.c | 3 ++ Modules/_testexternalinspection.c | 6 +++ Modules/_testimportmultiple.c | 26 ++++++----- Modules/_testinternalcapi.c | 1 + Modules/_testlimitedcapi.c | 3 ++ Modules/_testmultiphase.c | 21 ++++++++- Modules/_testsinglephase.c | 9 ++++ Modules/_threadmodule.c | 1 + Modules/_tkinter.c | 3 ++ Modules/_tracemalloc.c | 3 ++ Modules/_typingmodule.c | 1 + Modules/_uuidmodule.c | 5 ++- Modules/_weakref.c | 1 + Modules/_winapi.c | 1 + Modules/_xxtestfuzz/_xxtestfuzz.c | 9 +++- Modules/_zoneinfo.c | 1 + Modules/arraymodule.c | 1 + Modules/atexitmodule.c | 1 + Modules/binascii.c | 1 + Modules/cjkcodecs/cjkcodecs.h | 1 + Modules/cjkcodecs/multibytecodec.c | 1 + Modules/cmathmodule.c | 1 + Modules/errnomodule.c | 5 ++- Modules/faulthandler.c | 1 + Modules/fcntlmodule.c | 1 + Modules/gcmodule.c | 1 + Modules/grpmodule.c | 1 + Modules/itertoolsmodule.c | 1 + Modules/mathmodule.c | 1 + Modules/md5module.c | 1 + Modules/mmapmodule.c | 1 + Modules/overlapped.c | 1 + Modules/posixmodule.c | 1 + Modules/pwdmodule.c | 1 + Modules/pyexpat.c | 1 + Modules/readline.c | 3 ++ Modules/resource.c | 1 + Modules/selectmodule.c | 1 + Modules/sha1module.c | 1 + Modules/sha2module.c | 1 + Modules/sha3module.c | 1 + Modules/signalmodule.c | 1 + Modules/socketmodule.c | 1 + Modules/symtablemodule.c | 1 + Modules/syslogmodule.c | 1 + Modules/termios.c | 1 + Modules/timemodule.c | 1 + Modules/unicodedata.c | 1 + Modules/xxlimited.c | 5 ++- Modules/xxlimited_35.c | 4 ++ Modules/xxmodule.c | 1 + Modules/xxsubtype.c | 1 + Modules/zlibmodule.c | 1 + Objects/moduleobject.c | 35 +++++++++++++++ Objects/unicodeobject.c | 1 + PC/_testconsole.c | 5 ++- PC/msvcrtmodule.c | 1 + PC/winreg.c | 1 + PC/winsound.c | 5 ++- Parser/asdl_c.py | 1 + Python/Python-ast.c | 1 + Python/Python-tokenize.c | 1 + Python/_warnings.c | 1 + Python/bltinmodule.c | 3 ++ Python/import.c | 1 + Python/marshal.c | 1 + Python/sysmodule.c | 3 ++ 123 files changed, 376 insertions(+), 62 deletions(-) create mode 100644 Lib/test/test_importlib/extension/_test_nonmodule_cases.py create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-03-12-13-51-09.gh-issue-116322.q8TcDQ.rst diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 979b22261efa3b..86308d921f47f2 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -411,6 +411,31 @@ The available slot types are: .. versionadded:: 3.12 +.. c:macro:: Py_mod_gil + + Specifies one of the following values: + + .. c:macro:: Py_MOD_GIL_USED + + The module depends on the presence of the global interpreter lock (GIL), + and may access global state without synchronization. + + .. c:macro:: Py_MOD_GIL_NOT_USED + + The module is safe to run without an active GIL. + + This slot is ignored by Python builds not configured with + :option:`--disable-gil`. Otherwise, it determines whether or not importing + this module will cause the GIL to be automatically enabled. See + :envvar:`PYTHON_GIL` and :option:`-X gil <-X>` for more detail. + + Multiple ``Py_mod_gil`` slots may not be specified in one module definition. + + If ``Py_mod_gil`` is not specified, the import machinery defaults to + ``Py_MOD_GIL_USED``. + + .. versionadded: 3.13 + See :PEP:`489` for more details on multi-phase initialization. Low-level module creation functions @@ -609,6 +634,19 @@ state: .. versionadded:: 3.9 +.. c:function:: int PyModule_ExperimentalSetGIL(PyObject *module, void *gil) + + Indicate that *module* does or does not support running without the global + interpreter lock (GIL), using one of the values from + :c:macro:`Py_mod_gil`. It must be called during *module*'s initialization + function. If this function is not called during module initialization, the + import machinery assumes the module does not support running without the + GIL. This function is only available in Python builds configured with + :option:`--disable-gil`. + Return ``-1`` on error, ``0`` on success. + + .. versionadded:: 3.13 + Module lookup ^^^^^^^^^^^^^ diff --git a/Include/internal/pycore_moduleobject.h b/Include/internal/pycore_moduleobject.h index 5644bbe5e0552b..049677b292e235 100644 --- a/Include/internal/pycore_moduleobject.h +++ b/Include/internal/pycore_moduleobject.h @@ -22,6 +22,9 @@ typedef struct { PyObject *md_weaklist; // for logging purposes after md_dict is cleared PyObject *md_name; +#ifdef Py_GIL_DISABLED + void *md_gil; +#endif } PyModuleObject; static inline PyModuleDef* _PyModule_GetDef(PyObject *mod) { diff --git a/Include/moduleobject.h b/Include/moduleobject.h index 83f8c2030dbb8f..6afa3c7be37ee7 100644 --- a/Include/moduleobject.h +++ b/Include/moduleobject.h @@ -76,9 +76,13 @@ struct PyModuleDef_Slot { #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030c0000 # define Py_mod_multiple_interpreters 3 #endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000 +# define Py_mod_gil 4 +#endif + #ifndef Py_LIMITED_API -#define _Py_mod_LAST_SLOT 3 +#define _Py_mod_LAST_SLOT 4 #endif #endif /* New in 3.5 */ @@ -90,6 +94,16 @@ struct PyModuleDef_Slot { # define Py_MOD_PER_INTERPRETER_GIL_SUPPORTED ((void *)2) #endif +/* for Py_mod_gil: */ +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000 +# define Py_MOD_GIL_USED ((void *)0) +# define Py_MOD_GIL_NOT_USED ((void *)1) +#endif + +#if !defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED) +PyAPI_FUNC(int) PyModule_ExperimentalSetGIL(PyObject *module, void *gil); +#endif + struct PyModuleDef { PyModuleDef_Base m_base; const char* m_name; diff --git a/Lib/test/test_importlib/extension/_test_nonmodule_cases.py b/Lib/test/test_importlib/extension/_test_nonmodule_cases.py new file mode 100644 index 00000000000000..8ffd18d221df4f --- /dev/null +++ b/Lib/test/test_importlib/extension/_test_nonmodule_cases.py @@ -0,0 +1,44 @@ +import types +import unittest +from test.test_importlib import util + +machinery = util.import_importlib('importlib.machinery') + +from test.test_importlib.extension.test_loader import MultiPhaseExtensionModuleTests + + +class NonModuleExtensionTests: + setUp = MultiPhaseExtensionModuleTests.setUp + load_module_by_name = MultiPhaseExtensionModuleTests.load_module_by_name + + def _test_nonmodule(self): + # Test returning a non-module object from create works. + name = self.name + '_nonmodule' + mod = self.load_module_by_name(name) + self.assertNotEqual(type(mod), type(unittest)) + self.assertEqual(mod.three, 3) + + # issue 27782 + def test_nonmodule_with_methods(self): + # Test creating a non-module object with methods defined. + name = self.name + '_nonmodule_with_methods' + mod = self.load_module_by_name(name) + self.assertNotEqual(type(mod), type(unittest)) + self.assertEqual(mod.three, 3) + self.assertEqual(mod.bar(10, 1), 9) + + def test_null_slots(self): + # Test that NULL slots aren't a problem. + name = self.name + '_null_slots' + module = self.load_module_by_name(name) + self.assertIsInstance(module, types.ModuleType) + self.assertEqual(module.__name__, name) + + +(Frozen_NonModuleExtensionTests, + Source_NonModuleExtensionTests + ) = util.test_both(NonModuleExtensionTests, machinery=machinery) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py index 7607f0e0857595..0dd21e079eba22 100644 --- a/Lib/test/test_importlib/extension/test_loader.py +++ b/Lib/test/test_importlib/extension/test_loader.py @@ -10,7 +10,8 @@ import warnings import importlib.util import importlib -from test.support import MISSING_C_DOCSTRINGS +from test import support +from test.support import MISSING_C_DOCSTRINGS, script_helper class LoaderTests: @@ -325,29 +326,6 @@ def test_unloadable_nonascii(self): self.load_module_by_name(name) self.assertEqual(cm.exception.name, name) - def test_nonmodule(self): - # Test returning a non-module object from create works. - name = self.name + '_nonmodule' - mod = self.load_module_by_name(name) - self.assertNotEqual(type(mod), type(unittest)) - self.assertEqual(mod.three, 3) - - # issue 27782 - def test_nonmodule_with_methods(self): - # Test creating a non-module object with methods defined. - name = self.name + '_nonmodule_with_methods' - mod = self.load_module_by_name(name) - self.assertNotEqual(type(mod), type(unittest)) - self.assertEqual(mod.three, 3) - self.assertEqual(mod.bar(10, 1), 9) - - def test_null_slots(self): - # Test that NULL slots aren't a problem. - name = self.name + '_null_slots' - module = self.load_module_by_name(name) - self.assertIsInstance(module, types.ModuleType) - self.assertEqual(module.__name__, name) - def test_bad_modules(self): # Test SystemError is raised for misbehaving extensions. for name_base in [ @@ -401,5 +379,14 @@ def test_nonascii(self): ) = util.test_both(MultiPhaseExtensionModuleTests, machinery=machinery) +class NonModuleExtensionTests(unittest.TestCase): + def test_nonmodule_cases(self): + # The test cases in this file cause the GIL to be enabled permanently + # in free-threaded builds, so they are run in a subprocess to isolate + # this effect. + script = support.findfile("test_importlib/extension/_test_nonmodule_cases.py") + script_helper.run_test_script(script) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index df0a6f09c09c08..73912767ae25b7 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1606,7 +1606,10 @@ def get_gen(): yield 1 check(int(PyLong_BASE**2-1), vsize('') + 2*self.longdigit) check(int(PyLong_BASE**2), vsize('') + 3*self.longdigit) # module - check(unittest, size('PnPPP')) + if support.Py_GIL_DISABLED: + check(unittest, size('PPPPPP')) + else: + check(unittest, size('PPPPP')) # None check(None, size('')) # NotImplementedType diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-03-12-13-51-09.gh-issue-116322.q8TcDQ.rst b/Misc/NEWS.d/next/Core and Builtins/2024-03-12-13-51-09.gh-issue-116322.q8TcDQ.rst new file mode 100644 index 00000000000000..2d3bf411a5a162 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-03-12-13-51-09.gh-issue-116322.q8TcDQ.rst @@ -0,0 +1,5 @@ +Extension modules may indicate to the runtime that they can run without the +GIL. Multi-phase init modules do so by calling providing +``Py_MOD_GIL_NOT_USED`` for the ``Py_mod_gil`` slot, while single-phase init +modules call ``PyModule_ExperimentalSetGIL(mod, Py_MOD_GIL_NOT_USED)`` from +their init function. diff --git a/Modules/_abc.c b/Modules/_abc.c index f2a523e6f2fc27..4f4b24b035db4a 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -970,6 +970,7 @@ _abcmodule_free(void *module) static PyModuleDef_Slot _abcmodule_slots[] = { {Py_mod_exec, _abcmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 0873d32a9ec1a5..a26714f9755df5 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3795,6 +3795,7 @@ module_exec(PyObject *mod) static struct PyModuleDef_Slot module_slots[] = { {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_bisectmodule.c b/Modules/_bisectmodule.c index 9e0fd336419b44..56322c48b7cd35 100644 --- a/Modules/_bisectmodule.c +++ b/Modules/_bisectmodule.c @@ -462,6 +462,7 @@ bisect_modexec(PyObject *m) static PyModuleDef_Slot bisect_slots[] = { {Py_mod_exec, bisect_modexec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_blake2/blake2module.c b/Modules/_blake2/blake2module.c index 5df9fd3df493ee..78242214764f2b 100644 --- a/Modules/_blake2/blake2module.c +++ b/Modules/_blake2/blake2module.c @@ -137,6 +137,7 @@ blake2_exec(PyObject *m) static PyModuleDef_Slot _blake2_slots[] = { {Py_mod_exec, blake2_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c index 3d0d4ee5e79c2b..661847ad26702e 100644 --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -802,6 +802,7 @@ _bz2_free(void *module) static struct PyModuleDef_Slot _bz2_slots[] = { {Py_mod_exec, _bz2_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_codecsmodule.c b/Modules/_codecsmodule.c index c31c1b6d6f2bbc..32373f0799bfeb 100644 --- a/Modules/_codecsmodule.c +++ b/Modules/_codecsmodule.c @@ -1050,6 +1050,7 @@ static PyMethodDef _codecs_functions[] = { static PyModuleDef_Slot _codecs_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 309d63c9bf7cbe..b865351c93d2d8 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2817,6 +2817,7 @@ collections_exec(PyObject *module) { static struct PyModuleDef_Slot collections_slots[] = { {Py_mod_exec, collections_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_contextvarsmodule.c b/Modules/_contextvarsmodule.c index f621c1de6d42d6..3f96f07909b69a 100644 --- a/Modules/_contextvarsmodule.c +++ b/Modules/_contextvarsmodule.c @@ -45,6 +45,7 @@ _contextvars_exec(PyObject *m) static struct PyModuleDef_Slot _contextvars_slots[] = { {Py_mod_exec, _contextvars_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_csv.c b/Modules/_csv.c index ac948f417cebf5..9d6b66d4938687 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -1796,6 +1796,7 @@ csv_exec(PyObject *module) { static PyModuleDef_Slot csv_slots[] = { {Py_mod_exec, csv_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 3cb0b24668eb2a..1b1a0ea549f1e1 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5948,6 +5948,7 @@ module_free(void *module) static PyModuleDef_Slot module_slots[] = { {Py_mod_exec, _ctypes_mod_exec}, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index 1dd3ef19052470..f46f6362ddd03b 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -1,7 +1,7 @@ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif // gh-85283: On Windows, Py_LIMITED_API requires Py_BUILD_CORE to not attempt @@ -1167,6 +1167,7 @@ _testfunc_pylist_append(PyObject *list, PyObject *item) static struct PyModuleDef_Slot _ctypes_test_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c index 2ec8f34c5c220b..125c72dbbe7712 100644 --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -697,6 +697,7 @@ static PyModuleDef_Slot _curses_slots[] = { // XXX gh-103092: fix isolation. {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, //{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index d04d1e973af030..8bf6824b6d83ff 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4743,6 +4743,9 @@ PyInit__curses(void) m = PyModule_Create(&_cursesmodule); if (m == NULL) return NULL; +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(m, Py_MOD_GIL_NOT_USED); +#endif /* Add some symbolic constants to the module */ d = PyModule_GetDict(m); diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 06004e258b2eff..00015c5d8c23cb 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6984,6 +6984,9 @@ PyInit__datetime(void) PyObject *mod = PyModule_Create(&datetimemodule); if (mod == NULL) return NULL; +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(mod, Py_MOD_GIL_NOT_USED); +#endif if (_datetime_exec(mod) < 0) { Py_DECREF(mod); diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index ee33fe625be3d7..1be4234aad3291 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -616,6 +616,7 @@ _dbm_module_free(void *module) static PyModuleDef_Slot _dbmmodule_slots[] = { {Py_mod_exec, _dbm_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index fe6711143b845b..2daa24c823a542 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -6157,6 +6157,7 @@ decimal_free(void *module) static struct PyModuleDef_Slot _decimal_slots[] = { {Py_mod_exec, _decimal_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index aaa0cad76ae5c4..b11983d2caa2d1 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -4463,6 +4463,7 @@ module_exec(PyObject *m) static struct PyModuleDef_Slot elementtree_slots[] = { {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index e37473a582b55f..9dee7bf3062710 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1559,6 +1559,7 @@ _functools_free(void *module) static struct PyModuleDef_Slot _functools_slots[] = { {Py_mod_exec, _functools_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index db868c18160fda..df7fba67810ed0 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -825,6 +825,7 @@ _gdbm_module_free(void *module) static PyModuleDef_Slot _gdbm_module_slots[] = { {Py_mod_exec, _gdbm_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index d0b46810dc1489..14d9c186151232 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -2289,6 +2289,7 @@ static PyModuleDef_Slot hashlib_slots[] = { {Py_mod_exec, hashlib_init_constructors}, {Py_mod_exec, hashlib_exception}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_heapqmodule.c b/Modules/_heapqmodule.c index 9d4ec256ee9e3e..695ce22f8049df 100644 --- a/Modules/_heapqmodule.c +++ b/Modules/_heapqmodule.c @@ -681,6 +681,7 @@ heapq_exec(PyObject *m) static struct PyModuleDef_Slot heapq_slots[] = { {Py_mod_exec, heapq_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index 43c96584790fa0..ff8dacf5bd1ad0 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -3326,6 +3326,7 @@ module_exec(PyObject *mod) static struct PyModuleDef_Slot module_slots[] = { {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_interpqueuesmodule.c b/Modules/_interpqueuesmodule.c index 46801bd416495a..556953db6b8039 100644 --- a/Modules/_interpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -1830,6 +1830,7 @@ module_exec(PyObject *mod) static struct PyModuleDef_Slot module_slots[] = { {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 8fea56977ef3fe..86a4113dcc16f1 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -1519,6 +1519,7 @@ module_exec(PyObject *mod) static struct PyModuleDef_Slot module_slots[] = { {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 173f5b55e5f732..269070fe2b0a42 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -720,6 +720,7 @@ iomodule_exec(PyObject *m) static struct PyModuleDef_Slot iomodule_slots[] = { {Py_mod_exec, iomodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_json.c b/Modules/_json.c index c55299899e77fe..fc39f624b723f5 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1780,6 +1780,7 @@ _json_exec(PyObject *module) static PyModuleDef_Slot _json_slots[] = { {Py_mod_exec, _json_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index fe8e4c5e30035b..d4923442478b3e 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -860,6 +860,7 @@ _locale_exec(PyObject *module) static struct PyModuleDef_Slot _locale_slots[] = { {Py_mod_exec, _locale_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index a76c3dea555783..18be01df5c1377 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -1006,6 +1006,7 @@ _lsprof_exec(PyObject *module) static PyModuleDef_Slot _lsprofslots[] = { {Py_mod_exec, _lsprof_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index f6bfbfa62687b8..97f3a8f03da9a8 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -1604,6 +1604,7 @@ static PyMethodDef lzma_methods[] = { static PyModuleDef_Slot lzma_slots[] = { {Py_mod_exec, lzma_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_multiprocessing/multiprocessing.c b/Modules/_multiprocessing/multiprocessing.c index 1f6ab718a36984..cee8cf7b9a83c0 100644 --- a/Modules/_multiprocessing/multiprocessing.c +++ b/Modules/_multiprocessing/multiprocessing.c @@ -277,6 +277,7 @@ multiprocessing_exec(PyObject *module) static PyModuleDef_Slot multiprocessing_slots[] = { {Py_mod_exec, multiprocessing_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_multiprocessing/posixshmem.c b/Modules/_multiprocessing/posixshmem.c index d332a4e9d9ea0b..aeb2d79de6f9ed 100644 --- a/Modules/_multiprocessing/posixshmem.c +++ b/Modules/_multiprocessing/posixshmem.c @@ -2,10 +2,10 @@ posixshmem - A Python extension that provides shm_open() and shm_unlink() */ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif #include @@ -128,6 +128,7 @@ static PyMethodDef module_methods[ ] = { static PyModuleDef_Slot module_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_opcode.c b/Modules/_opcode.c index 85e0ffec900e89..cc72cb170ceaed 100644 --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -406,6 +406,7 @@ _opcode_exec(PyObject *m) { static PyModuleDef_Slot module_slots[] = { {Py_mod_exec, _opcode_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_operator.c b/Modules/_operator.c index 306d4508f52a68..5d3f88327d19ad 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -1928,6 +1928,7 @@ operator_exec(PyObject *module) static struct PyModuleDef_Slot operator_slots[] = { {Py_mod_exec, operator_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_pickle.c b/Modules/_pickle.c index d7ffb04c28c2ac..754a326822e0f0 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -7863,6 +7863,7 @@ _pickle_exec(PyObject *m) static PyModuleDef_Slot pickle_slots[] = { {Py_mod_exec, _pickle_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index b160cd78177a17..daec4ad708dea4 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -1317,6 +1317,7 @@ static PyMethodDef module_methods[] = { static PyModuleDef_Slot _posixsubprocess_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index 5db9b645849fcd..aee8db802d8c3f 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -594,6 +594,7 @@ queuemodule_exec(PyObject *module) static PyModuleDef_Slot queuemodule_slots[] = { {Py_mod_exec, queuemodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index 56b891dfe0f85f..140640ae8fbf3a 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -642,6 +642,7 @@ _random_exec(PyObject *module) static PyModuleDef_Slot _random_slots[] = { {Py_mod_exec, _random_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_scproxy.c b/Modules/_scproxy.c index 042738b4ab83a2..e9170f2ce1ae87 100644 --- a/Modules/_scproxy.c +++ b/Modules/_scproxy.c @@ -3,10 +3,10 @@ * using the SystemConfiguration framework. */ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif #include @@ -239,6 +239,7 @@ static PyMethodDef mod_methods[] = { static PyModuleDef_Slot _scproxy_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 46fed9f13281f3..2c25ee32e58189 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -758,6 +758,7 @@ module_exec(PyObject *module) static struct PyModuleDef_Slot module_slots[] = { {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 00fbd9674b8cdd..c1eff63d921de9 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -3272,6 +3272,7 @@ sre_exec(PyObject *m) static PyModuleDef_Slot sre_slots[] = { {Py_mod_exec, sre_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 885a9c25967d2d..9d50b576ba337f 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -6515,6 +6515,7 @@ static PyModuleDef_Slot sslmodule_slots[] = { {Py_mod_exec, sslmodule_init_strings}, {Py_mod_exec, sslmodule_init_lock}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_stat.c b/Modules/_stat.c index 8059ec2f1f066d..a4f15e8e65e894 100644 --- a/Modules/_stat.c +++ b/Modules/_stat.c @@ -679,6 +679,7 @@ stat_exec(PyObject *module) static PyModuleDef_Slot stat_slots[] = { {Py_mod_exec, stat_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_statisticsmodule.c b/Modules/_statisticsmodule.c index 78a6552c4c9ec0..b84f731ad6a1da 100644 --- a/Modules/_statisticsmodule.c +++ b/Modules/_statisticsmodule.c @@ -1,9 +1,9 @@ /* statistics accelerator C extension: _statistics module. */ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif #include "Python.h" @@ -136,6 +136,7 @@ PyDoc_STRVAR(statistics_doc, static struct PyModuleDef_Slot _statisticsmodule_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_struct.c b/Modules/_struct.c index fa2cd37e003e0a..905dcbdeeddc5a 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -2593,6 +2593,7 @@ _structmodule_exec(PyObject *m) static PyModuleDef_Slot _structmodule_slots[] = { {Py_mod_exec, _structmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_suggestions.c b/Modules/_suggestions.c index 30b524d70c1211..80c7179c4c251c 100644 --- a/Modules/_suggestions.c +++ b/Modules/_suggestions.c @@ -49,15 +49,21 @@ static PyMethodDef module_methods[] = { {NULL, NULL, 0, NULL} // Sentinel }; +static PyModuleDef_Slot module_slots[] = { + {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL}, +}; + static struct PyModuleDef suggestions_module = { PyModuleDef_HEAD_INIT, "_suggestions", NULL, - -1, - module_methods + 0, + module_methods, + module_slots, }; PyMODINIT_FUNC PyInit__suggestions(void) { - return PyModule_Create(&suggestions_module); + return PyModuleDef_Init(&suggestions_module); } - diff --git a/Modules/_sysconfig.c b/Modules/_sysconfig.c index c76b9e6b3ebafa..c50c5cfabc2f1f 100644 --- a/Modules/_sysconfig.c +++ b/Modules/_sysconfig.c @@ -80,6 +80,7 @@ static struct PyMethodDef sysconfig_methods[] = { static PyModuleDef_Slot sysconfig_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_testbuffer.c b/Modules/_testbuffer.c index 9e77744efad728..35d4ffecad6b15 100644 --- a/Modules/_testbuffer.c +++ b/Modules/_testbuffer.c @@ -2901,6 +2901,9 @@ PyInit__testbuffer(void) if (mod == NULL) { return NULL; } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(mod, Py_MOD_GIL_NOT_USED); +#endif if (_testbuffer_exec(mod) < 0) { Py_DECREF(mod); return NULL; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index f5892fc5ed2a2c..beae13cd74c731 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3935,6 +3935,9 @@ PyInit__testcapi(void) m = PyModule_Create(&_testcapimodule); if (m == NULL) return NULL; +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(m, Py_MOD_GIL_NOT_USED); +#endif Py_SET_TYPE(&_HashInheritanceTester_Type, &PyType_Type); if (PyType_Ready(&_HashInheritanceTester_Type) < 0) { diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 2e9d00ac2189eb..c7af552f029b81 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -1955,6 +1955,9 @@ PyInit__testclinic(void) if (m == NULL) { return NULL; } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(m, Py_MOD_GIL_NOT_USED); +#endif if (PyModule_AddType(m, &TestClass) < 0) { goto error; } diff --git a/Modules/_testclinic_limited.c b/Modules/_testclinic_limited.c index 29f1b7c13e4c50..d5f98085f84225 100644 --- a/Modules/_testclinic_limited.c +++ b/Modules/_testclinic_limited.c @@ -146,5 +146,8 @@ PyInit__testclinic_limited(void) if (m == NULL) { return NULL; } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(m, Py_MOD_GIL_NOT_USED); +#endif return m; } diff --git a/Modules/_testexternalinspection.c b/Modules/_testexternalinspection.c index e2f96cdad5c58e..d9c65fe253f8d3 100644 --- a/Modules/_testexternalinspection.c +++ b/Modules/_testexternalinspection.c @@ -627,6 +627,12 @@ PyMODINIT_FUNC PyInit__testexternalinspection(void) { PyObject* mod = PyModule_Create(&module); + if (mod == NULL) { + return NULL; + } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(mod, Py_MOD_GIL_NOT_USED); +#endif int rc = PyModule_AddIntConstant(mod, "PROCESS_VM_READV_SUPPORTED", HAVE_PROCESS_VM_READV); if (rc < 0) { Py_DECREF(mod); diff --git a/Modules/_testimportmultiple.c b/Modules/_testimportmultiple.c index a65ca513a12516..c147596f88a3a8 100644 --- a/Modules/_testimportmultiple.c +++ b/Modules/_testimportmultiple.c @@ -6,18 +6,24 @@ #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x03020000 +# define Py_LIMITED_API 0x030d0000 #endif #include +static PyModuleDef_Slot shared_slots[] = { + {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL}, +}; + static struct PyModuleDef _testimportmultiple = { PyModuleDef_HEAD_INIT, "_testimportmultiple", "_testimportmultiple doc", - -1, - NULL, + 0, NULL, + shared_slots, NULL, NULL, NULL @@ -25,16 +31,16 @@ static struct PyModuleDef _testimportmultiple = { PyMODINIT_FUNC PyInit__testimportmultiple(void) { - return PyModule_Create(&_testimportmultiple); + return PyModuleDef_Init(&_testimportmultiple); } static struct PyModuleDef _foomodule = { PyModuleDef_HEAD_INIT, "_testimportmultiple_foo", "_testimportmultiple_foo doc", - -1, - NULL, + 0, NULL, + shared_slots, NULL, NULL, NULL @@ -42,21 +48,21 @@ static struct PyModuleDef _foomodule = { PyMODINIT_FUNC PyInit__testimportmultiple_foo(void) { - return PyModule_Create(&_foomodule); + return PyModuleDef_Init(&_foomodule); } static struct PyModuleDef _barmodule = { PyModuleDef_HEAD_INIT, "_testimportmultiple_bar", "_testimportmultiple_bar doc", - -1, - NULL, + 0, NULL, + shared_slots, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__testimportmultiple_bar(void){ - return PyModule_Create(&_barmodule); + return PyModuleDef_Init(&_barmodule); } diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index f7952a119f0bd9..de98af32b5dff7 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -2142,6 +2142,7 @@ module_exec(PyObject *module) static struct PyModuleDef_Slot module_slots[] = { {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c index 598071fe0ddbad..f88476f4be2054 100644 --- a/Modules/_testlimitedcapi.c +++ b/Modules/_testlimitedcapi.c @@ -25,6 +25,9 @@ PyInit__testlimitedcapi(void) if (mod == NULL) { return NULL; } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(mod, Py_MOD_GIL_NOT_USED); +#endif if (_PyTestLimitedCAPI_Init_Abstract(mod) < 0) { return NULL; diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index 21c5f696a4f2ec..ca3d83233c5dd0 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -431,6 +431,7 @@ static int execfunc(PyObject *m) static PyModuleDef_Slot main_slots[] = { {Py_mod_exec, execfunc}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; @@ -519,13 +520,18 @@ PyInit__testmultiphase_nonmodule_with_methods(void) /**** Non-ASCII-named modules ****/ +static PyModuleDef_Slot nonascii_slots[] = { + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL}, +}; + static PyModuleDef def_nonascii_latin = { \ PyModuleDef_HEAD_INIT, /* m_base */ "_testmultiphase_nonascii_latin", /* m_name */ PyDoc_STR("Module named in Czech"), /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_slots */ + nonascii_slots, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -543,7 +549,7 @@ static PyModuleDef def_nonascii_kana = { \ PyDoc_STR("Module named in Japanese"), /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_slots */ + nonascii_slots, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -757,6 +763,7 @@ static PyModuleDef_Slot slots_nonmodule_with_exec_slots[] = { {Py_mod_create, createfunc_nonmodule}, {Py_mod_exec, execfunc}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; @@ -778,6 +785,7 @@ execfunc_err(PyObject *mod) static PyModuleDef_Slot slots_exec_err[] = { {Py_mod_exec, execfunc_err}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; @@ -800,6 +808,7 @@ execfunc_raise(PyObject *spec) static PyModuleDef_Slot slots_exec_raise[] = { {Py_mod_exec, execfunc_raise}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; @@ -822,6 +831,7 @@ execfunc_unreported_exception(PyObject *mod) static PyModuleDef_Slot slots_exec_unreported_exception[] = { {Py_mod_exec, execfunc_unreported_exception}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; @@ -857,6 +867,7 @@ meth_state_access_exec(PyObject *m) static PyModuleDef_Slot meth_state_access_slots[] = { {Py_mod_exec, meth_state_access_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; @@ -889,6 +900,9 @@ PyInit__test_module_state_shared(void) if (module == NULL) { return NULL; } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(module, Py_MOD_GIL_NOT_USED); +#endif if (PyModule_AddObjectRef(module, "Error", PyExc_Exception) < 0) { Py_DECREF(module); @@ -903,6 +917,7 @@ PyInit__test_module_state_shared(void) static PyModuleDef_Slot slots_multiple_multiple_interpreters_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; @@ -920,6 +935,7 @@ PyInit__testmultiphase_multiple_multiple_interpreters_slots(void) static PyModuleDef_Slot non_isolated_slots[] = { {Py_mod_exec, execfunc}, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; @@ -940,6 +956,7 @@ static PyModuleDef_Slot shared_gil_only_slots[] = { We put it here explicitly to draw attention to the contrast with Py_MOD_PER_INTERPRETER_GIL_SUPPORTED. */ {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c index ff533e44a82730..c0eb266751e9e6 100644 --- a/Modules/_testsinglephase.c +++ b/Modules/_testsinglephase.c @@ -471,6 +471,9 @@ init__testsinglephase_basic(PyModuleDef *def) if (module == NULL) { return NULL; } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(module, Py_MOD_GIL_NOT_USED); +#endif module_state *state = &global_state.module; // It may have been set by a previous run or under a different name. @@ -562,6 +565,9 @@ PyInit__testsinglephase_with_reinit(void) if (module == NULL) { return NULL; } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(module, Py_MOD_GIL_NOT_USED); +#endif assert(get_module_state(module) == NULL); @@ -624,6 +630,9 @@ PyInit__testsinglephase_with_state(void) if (module == NULL) { return NULL; } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(module, Py_MOD_GIL_NOT_USED); +#endif module_state *state = get_module_state(module); assert(state != NULL); diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index f5e3b42600675e..39d309729d88b8 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -2544,6 +2544,7 @@ The 'threading' module provides a more convenient interface."); static PyModuleDef_Slot thread_module_slots[] = { {Py_mod_exec, thread_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index e3789867dc085f..ecb7ca8de62ef1 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -3205,6 +3205,9 @@ PyInit__tkinter(void) m = PyModule_Create(&_tkintermodule); if (m == NULL) return NULL; +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(m, Py_MOD_GIL_NOT_USED); +#endif Tkinter_TclError = PyErr_NewException("_tkinter.TclError", NULL, NULL); if (PyModule_AddObjectRef(m, "TclError", Tkinter_TclError)) { diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 6dba3cac01c1c8..55028dc3a65ff4 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -219,6 +219,9 @@ PyInit__tracemalloc(void) m = PyModule_Create(&module_def); if (m == NULL) return NULL; +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(m, Py_MOD_GIL_NOT_USED); +#endif if (_PyTraceMalloc_Init() < 0) { Py_DECREF(m); diff --git a/Modules/_typingmodule.c b/Modules/_typingmodule.c index 180f3d7eb01794..09fbb3c5e8b91d 100644 --- a/Modules/_typingmodule.c +++ b/Modules/_typingmodule.c @@ -72,6 +72,7 @@ _typing_exec(PyObject *m) static struct PyModuleDef_Slot _typingmodule_slots[] = { {Py_mod_exec, _typing_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_uuidmodule.c b/Modules/_uuidmodule.c index 052cb9fef3b21c..c5e78b1510b5e3 100644 --- a/Modules/_uuidmodule.c +++ b/Modules/_uuidmodule.c @@ -3,10 +3,10 @@ * DCE compatible Universally Unique Identifier library. */ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif #include "Python.h" @@ -111,6 +111,7 @@ static PyMethodDef uuid_methods[] = { static PyModuleDef_Slot uuid_slots[] = { {Py_mod_exec, uuid_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_weakref.c b/Modules/_weakref.c index 1ea3ed5e40b761..a5c15c0f10b930 100644 --- a/Modules/_weakref.c +++ b/Modules/_weakref.c @@ -171,6 +171,7 @@ weakref_exec(PyObject *module) static struct PyModuleDef_Slot weakref_slots[] = { {Py_mod_exec, weakref_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 23e3c0d87f0319..cd5dd503abe61f 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -3189,6 +3189,7 @@ static int winapi_exec(PyObject *m) static PyModuleDef_Slot winapi_slots[] = { {Py_mod_exec, winapi_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/_xxtestfuzz/_xxtestfuzz.c b/Modules/_xxtestfuzz/_xxtestfuzz.c index a2dbabce71ed67..2952d7043e01fe 100644 --- a/Modules/_xxtestfuzz/_xxtestfuzz.c +++ b/Modules/_xxtestfuzz/_xxtestfuzz.c @@ -28,13 +28,18 @@ static PyMethodDef module_methods[] = { {NULL}, }; +static PyModuleDef_Slot module_slots[] = { + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL}, +}; + static struct PyModuleDef _fuzzmodule = { PyModuleDef_HEAD_INIT, "_fuzz", NULL, 0, module_methods, - NULL, + module_slots, NULL, NULL, NULL @@ -43,5 +48,5 @@ static struct PyModuleDef _fuzzmodule = { PyMODINIT_FUNC PyInit__xxtestfuzz(void) { - return PyModule_Create(&_fuzzmodule); + return PyModuleDef_Init(&_fuzzmodule); } diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index fcd4af64df0be9..38c3f0c45d803f 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -2760,6 +2760,7 @@ zoneinfomodule_exec(PyObject *m) static PyModuleDef_Slot zoneinfomodule_slots[] = { {Py_mod_exec, zoneinfomodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 317f4974814945..a3b833d47cd9ea 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -3220,6 +3220,7 @@ array_modexec(PyObject *m) static PyModuleDef_Slot arrayslots[] = { {Py_mod_exec, array_modexec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c index 8e908da2534c55..297a8d74ba3bf4 100644 --- a/Modules/atexitmodule.c +++ b/Modules/atexitmodule.c @@ -322,6 +322,7 @@ Two public functions, register and unregister, are defined.\n\ static PyModuleDef_Slot atexitmodule_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/binascii.c b/Modules/binascii.c index 86493241a1fb7e..250f03a9531eed 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -1278,6 +1278,7 @@ binascii_exec(PyObject *module) static PyModuleDef_Slot binascii_slots[] = { {Py_mod_exec, binascii_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/cjkcodecs/cjkcodecs.h b/Modules/cjkcodecs/cjkcodecs.h index 766f82983025e4..2b446ba5226ac0 100644 --- a/Modules/cjkcodecs/cjkcodecs.h +++ b/Modules/cjkcodecs/cjkcodecs.h @@ -503,6 +503,7 @@ static struct PyMethodDef _cjk_methods[] = { static PyModuleDef_Slot _cjk_slots[] = { {Py_mod_exec, _cjk_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index e5433d7dd85306..1c671adb4ff188 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -2094,6 +2094,7 @@ static struct PyMethodDef _multibytecodec_methods[] = { static PyModuleDef_Slot _multibytecodec_slots[] = { {Py_mod_exec, _multibytecodec_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 57bc55632be485..d901b350bc5343 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -1363,6 +1363,7 @@ cmath_exec(PyObject *mod) static PyModuleDef_Slot cmath_slots[] = { {Py_mod_exec, cmath_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/errnomodule.c b/Modules/errnomodule.c index 97e5f0180d76fb..3f96f2f846d612 100644 --- a/Modules/errnomodule.c +++ b/Modules/errnomodule.c @@ -1,9 +1,9 @@ /* Errno module */ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif #include "Python.h" @@ -951,6 +951,7 @@ errno_exec(PyObject *module) static PyModuleDef_Slot errno_slots[] = { {Py_mod_exec, errno_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index c70d43a36b5cc7..cfa3cbdc34bc86 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -1292,6 +1292,7 @@ static PyModuleDef_Slot faulthandler_slots[] = { {Py_mod_exec, PyExec_faulthandler}, // XXX gh-103092: fix isolation. //{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index e24e5f98f4bc4d..b6eeec2c66f6e5 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -745,6 +745,7 @@ fcntl_exec(PyObject *module) static PyModuleDef_Slot fcntl_slots[] = { {Py_mod_exec, fcntl_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 8a1b483eddae35..57e4aae9ed557e 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -535,6 +535,7 @@ gcmodule_exec(PyObject *module) static PyModuleDef_Slot gcmodule_slots[] = { {Py_mod_exec, gcmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c index a1fa6cf20f71fd..f7d3e12f347ec2 100644 --- a/Modules/grpmodule.c +++ b/Modules/grpmodule.c @@ -342,6 +342,7 @@ grpmodule_exec(PyObject *module) static PyModuleDef_Slot grpmodule_slots[] = { {Py_mod_exec, grpmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 21ce3ecfad0354..8641c2f87e6db2 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -4781,6 +4781,7 @@ itertoolsmodule_exec(PyObject *mod) static struct PyModuleDef_Slot itertoolsmodule_slots[] = { {Py_mod_exec, itertoolsmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 8ba0431f4a47b7..a3cbfc383761a0 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -4177,6 +4177,7 @@ static PyMethodDef math_methods[] = { static PyModuleDef_Slot math_slots[] = { {Py_mod_exec, math_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/md5module.c b/Modules/md5module.c index 9cbf11feaa9c32..ef9163e8be5b6c 100644 --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -375,6 +375,7 @@ md5_exec(PyObject *m) static PyModuleDef_Slot _md5_slots[] = { {Py_mod_exec, md5_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 0cce7c27f9b16a..dfc16ff4370349 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -1801,6 +1801,7 @@ mmap_exec(PyObject *module) static PyModuleDef_Slot mmap_slots[] = { {Py_mod_exec, mmap_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/overlapped.c b/Modules/overlapped.c index b9881d91ded244..77ee70ae133c85 100644 --- a/Modules/overlapped.c +++ b/Modules/overlapped.c @@ -2070,6 +2070,7 @@ overlapped_exec(PyObject *module) static PyModuleDef_Slot overlapped_slots[] = { {Py_mod_exec, overlapped_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index e1a14e772c4bd0..9f4be98b35186e 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -18071,6 +18071,7 @@ posixmodule_exec(PyObject *m) static PyModuleDef_Slot posixmodile_slots[] = { {Py_mod_exec, posixmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index f58735aff99799..2240e2078b2d98 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -344,6 +344,7 @@ pwdmodule_exec(PyObject *module) static PyModuleDef_Slot pwdmodule_slots[] = { {Py_mod_exec, pwdmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index f04f96bc2f7601..f67d480f19de00 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -2117,6 +2117,7 @@ pyexpat_free(void *module) static PyModuleDef_Slot pyexpat_slots[] = { {Py_mod_exec, pyexpat_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/readline.c b/Modules/readline.c index c5c34535de0154..f59f8a9834caaf 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1552,6 +1552,9 @@ PyInit_readline(void) if (m == NULL) return NULL; +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(m, Py_MOD_GIL_NOT_USED); +#endif if (PyModule_AddIntConstant(m, "_READLINE_VERSION", RL_READLINE_VERSION) < 0) { diff --git a/Modules/resource.c b/Modules/resource.c index 8ee07bd0c8054c..3fe18e7c98e3d8 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -513,6 +513,7 @@ resource_exec(PyObject *module) static struct PyModuleDef_Slot resource_slots[] = { {Py_mod_exec, resource_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 6ea141ab1f9189..3eaee22c652c28 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -2802,6 +2802,7 @@ _select_exec(PyObject *m) static PyModuleDef_Slot _select_slots[] = { {Py_mod_exec, _select_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/sha1module.c b/Modules/sha1module.c index 345a6c215eb167..34a427a39b5cf8 100644 --- a/Modules/sha1module.c +++ b/Modules/sha1module.c @@ -371,6 +371,7 @@ _sha1_exec(PyObject *module) static PyModuleDef_Slot _sha1_slots[] = { {Py_mod_exec, _sha1_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/sha2module.c b/Modules/sha2module.c index 60be4228a00a03..7d6a1e40243f9d 100644 --- a/Modules/sha2module.c +++ b/Modules/sha2module.c @@ -866,6 +866,7 @@ static int sha2_exec(PyObject *module) static PyModuleDef_Slot _sha2_slots[] = { {Py_mod_exec, sha2_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/sha3module.c b/Modules/sha3module.c index c30e924a7072f7..084332c1efa0e0 100644 --- a/Modules/sha3module.c +++ b/Modules/sha3module.c @@ -602,6 +602,7 @@ _sha3_exec(PyObject *m) static PyModuleDef_Slot _sha3_slots[] = { {Py_mod_exec, _sha3_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 08fedeacd96d28..7de5ebe0899b35 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1698,6 +1698,7 @@ _signal_module_free(void *module) static PyModuleDef_Slot signal_slots[] = { {Py_mod_exec, signal_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 7720d59e46590e..daec560ddfcac7 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -8896,6 +8896,7 @@ socket_exec(PyObject *m) static struct PyModuleDef_Slot socket_slots[] = { {Py_mod_exec, socket_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c index ddc9ac3324356d..b4dbb54c3b47b0 100644 --- a/Modules/symtablemodule.c +++ b/Modules/symtablemodule.c @@ -110,6 +110,7 @@ symtable_init_constants(PyObject *m) static PyModuleDef_Slot symtable_slots[] = { {Py_mod_exec, symtable_init_constants}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/syslogmodule.c b/Modules/syslogmodule.c index cb3f2b03990cb8..14e7ca591a076b 100644 --- a/Modules/syslogmodule.c +++ b/Modules/syslogmodule.c @@ -439,6 +439,7 @@ syslog_exec(PyObject *module) static PyModuleDef_Slot syslog_slots[] = { {Py_mod_exec, syslog_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/termios.c b/Modules/termios.c index a29474d650127f..f2c5a4bafa7012 100644 --- a/Modules/termios.c +++ b/Modules/termios.c @@ -1364,6 +1364,7 @@ termios_exec(PyObject *mod) static PyModuleDef_Slot termios_slots[] = { {Py_mod_exec, termios_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 3211c7530da0b2..0511339978897a 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -2128,6 +2128,7 @@ time_module_free(void *module) static struct PyModuleDef_Slot time_slots[] = { {Py_mod_exec, time_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 6ae35b9372b830..333ffe68a454e4 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -1668,6 +1668,7 @@ unicodedata_exec(PyObject *module) static PyModuleDef_Slot unicodedata_slots[] = { {Py_mod_exec, unicodedata_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/xxlimited.c b/Modules/xxlimited.c index 3357b8076b67b1..d86741e1dfc18c 100644 --- a/Modules/xxlimited.c +++ b/Modules/xxlimited.c @@ -62,10 +62,10 @@ pass */ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif #include "Python.h" @@ -395,6 +395,7 @@ xx_modexec(PyObject *m) static PyModuleDef_Slot xx_slots[] = { {Py_mod_exec, xx_modexec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Modules/xxlimited_35.c b/Modules/xxlimited_35.c index 52690d9d10a81f..1063e54217b746 100644 --- a/Modules/xxlimited_35.c +++ b/Modules/xxlimited_35.c @@ -297,6 +297,10 @@ xx_modexec(PyObject *m) static PyModuleDef_Slot xx_slots[] = { {Py_mod_exec, xx_modexec}, +#ifdef Py_GIL_DISABLED + // These definitions are in the limited API, but not until 3.13. + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, +#endif {0, NULL} }; diff --git a/Modules/xxmodule.c b/Modules/xxmodule.c index 1e4e0ea3743ce3..a46bf8f0e64ee2 100644 --- a/Modules/xxmodule.c +++ b/Modules/xxmodule.c @@ -384,6 +384,7 @@ xx_exec(PyObject *m) static struct PyModuleDef_Slot xx_slots[] = { {Py_mod_exec, xx_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/xxsubtype.c b/Modules/xxsubtype.c index 560f43e5b3a643..9c548f44558d41 100644 --- a/Modules/xxsubtype.c +++ b/Modules/xxsubtype.c @@ -288,6 +288,7 @@ xxsubtype_exec(PyObject* m) static struct PyModuleDef_Slot xxsubtype_slots[] = { {Py_mod_exec, xxsubtype_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index fe9a6d8d4150ab..b115f67f228ba7 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -2106,6 +2106,7 @@ zlib_exec(PyObject *mod) static PyModuleDef_Slot zlib_slots[] = { {Py_mod_exec, zlib_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 2f6adb9a2e12be..d877edaf5453e1 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -249,6 +249,9 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version) } } m->md_def = module; +#ifdef Py_GIL_DISABLE + m->md_gil = Py_MOD_GIL_USED; +#endif return (PyObject*)m; } @@ -261,6 +264,8 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio PyObject *m = NULL; int has_multiple_interpreters_slot = 0; void *multiple_interpreters = (void *)0; + int has_gil_slot = 0; + void *gil_slot = Py_MOD_GIL_USED; int has_execution_slots = 0; const char *name; int ret; @@ -315,6 +320,17 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio multiple_interpreters = cur_slot->value; has_multiple_interpreters_slot = 1; break; + case Py_mod_gil: + if (has_gil_slot) { + PyErr_Format( + PyExc_SystemError, + "module %s has more than one 'gil' slot", + name); + goto error; + } + gil_slot = cur_slot->value; + has_gil_slot = 1; + break; default: assert(cur_slot->slot < 0 || cur_slot->slot > _Py_mod_LAST_SLOT); PyErr_Format( @@ -374,6 +390,11 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio if (PyModule_Check(m)) { ((PyModuleObject*)m)->md_state = NULL; ((PyModuleObject*)m)->md_def = def; +#ifdef Py_GIL_DISABLED + ((PyModuleObject*)m)->md_gil = gil_slot; +#else + (void)gil_slot; +#endif } else { if (def->m_size > 0 || def->m_traverse || def->m_clear || def->m_free) { PyErr_Format( @@ -415,6 +436,19 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio return NULL; } +#ifdef Py_GIL_DISABLED +int +PyModule_ExperimentalSetGIL(PyObject *module, void *gil) +{ + if (!PyModule_Check(module)) { + PyErr_BadInternalCall(); + return -1; + } + ((PyModuleObject *)module)->md_gil = gil; + return 0; +} +#endif + int PyModule_ExecDef(PyObject *module, PyModuleDef *def) { @@ -470,6 +504,7 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def) } break; case Py_mod_multiple_interpreters: + case Py_mod_gil: /* handled in PyModule_FromDefAndSpec2 */ break; default: diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 67b1282de790e0..057b417074ebea 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15541,6 +15541,7 @@ static PyMethodDef _string_methods[] = { static PyModuleDef_Slot module_slots[] = { {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/PC/_testconsole.c b/PC/_testconsole.c index f1ace003df483b..0dcea866f65d35 100644 --- a/PC/_testconsole.c +++ b/PC/_testconsole.c @@ -1,10 +1,10 @@ /* Testing module for multi-phase initialization of extension modules (PEP 489) */ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif #include "Python.h" @@ -31,6 +31,7 @@ static int execfunc(PyObject *m) PyModuleDef_Slot testconsole_slots[] = { {Py_mod_exec, execfunc}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/PC/msvcrtmodule.c b/PC/msvcrtmodule.c index 5ff703217b421f..b170e06b47dd59 100644 --- a/PC/msvcrtmodule.c +++ b/PC/msvcrtmodule.c @@ -656,6 +656,7 @@ exec_module(PyObject* m) static PyModuleDef_Slot msvcrt_slots[] = { {Py_mod_exec, exec_module}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/PC/winreg.c b/PC/winreg.c index 8096d17e43b7bc..efdf8addc06186 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -2179,6 +2179,7 @@ exec_module(PyObject *m) static PyModuleDef_Slot winreg_slots[] = { {Py_mod_exec, exec_module}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/PC/winsound.c b/PC/winsound.c index a6b2dac6ac1466..094c77ae34d678 100644 --- a/PC/winsound.c +++ b/PC/winsound.c @@ -35,10 +35,10 @@ winsound.PlaySound(None, 0) */ -// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED +// Need limited C API version 3.13 for Py_mod_gil #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030d0000 #endif #include @@ -246,6 +246,7 @@ exec_module(PyObject *module) static PyModuleDef_Slot sound_slots[] = { {Py_mod_exec, exec_module}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index c4df2c52c032bc..1f0be456655b25 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1443,6 +1443,7 @@ def visitModule(self, mod): static PyModuleDef_Slot astmodule_slots[] = { {Py_mod_exec, astmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Python/Python-ast.c b/Python/Python-ast.c index cc7734e0dbbf9f..1953142f6def44 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -17588,6 +17588,7 @@ astmodule_exec(PyObject *m) static PyModuleDef_Slot astmodule_slots[] = { {Py_mod_exec, astmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Python/Python-tokenize.c b/Python/Python-tokenize.c index a7891709b3b44a..41e8107e205b46 100644 --- a/Python/Python-tokenize.c +++ b/Python/Python-tokenize.c @@ -322,6 +322,7 @@ static PyMethodDef tokenize_methods[] = { static PyModuleDef_Slot tokenizemodule_slots[] = { {Py_mod_exec, tokenizemodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Python/_warnings.c b/Python/_warnings.c index 2ba704dcaa79b2..793cbc657f3184 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -1498,6 +1498,7 @@ warnings_module_exec(PyObject *module) static PyModuleDef_Slot warnings_slots[] = { {Py_mod_exec, warnings_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 722353ebcbfc3d..88d858dc944863 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -3124,6 +3124,9 @@ _PyBuiltin_Init(PyInterpreterState *interp) mod = _PyModule_CreateInitialized(&builtinsmodule, PYTHON_API_VERSION); if (mod == NULL) return NULL; +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(mod, Py_MOD_GIL_NOT_USED); +#endif dict = PyModule_GetDict(mod); #ifdef Py_TRACE_REFS diff --git a/Python/import.c b/Python/import.c index f120a3841b495d..4f91f03f091edd 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4212,6 +4212,7 @@ imp_module_exec(PyObject *module) static PyModuleDef_Slot imp_slots[] = { {Py_mod_exec, imp_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Python/marshal.c b/Python/marshal.c index 4bd8bb1d3a9308..ca22d6d679a230 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -1952,6 +1952,7 @@ marshal_module_exec(PyObject *mod) static PyModuleDef_Slot marshalmodule_slots[] = { {Py_mod_exec, marshal_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} }; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index f469f165e7fb84..645b76fccf602c 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3756,6 +3756,9 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) if (sysmod == NULL) { return _PyStatus_ERR("failed to create a module object"); } +#ifdef Py_GIL_DISABLED + PyModule_ExperimentalSetGIL(sysmod, Py_MOD_GIL_NOT_USED); +#endif PyObject *sysdict = PyModule_GetDict(sysmod); if (sysdict == NULL) { From 608192ee2f6c60aaae1c6f2f64ea45ad2667ff2d Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 3 May 2024 11:33:05 -0400 Subject: [PATCH 185/217] gh-118527: Use deferred reference counting for C functions on modules (#118529) This addresses a scaling bottleneck in the free-threaded build when calling functions like `math.floor()` concurrently from multiple threads. --- Objects/moduleobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index d877edaf5453e1..a570b13e120863 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -183,6 +183,7 @@ _add_methods_to_object(PyObject *module, PyObject *name, PyMethodDef *functions) if (func == NULL) { return -1; } + _PyObject_SetDeferredRefcount(func); if (PyObject_SetAttrString(module, fdef->ml_name, func) != 0) { Py_DECREF(func); return -1; From 9d67b72a4952766fdba803eb6eadd41dfee29dff Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 3 May 2024 11:48:27 -0400 Subject: [PATCH 186/217] docs: clarify csv.DictReader's treatment of the first data row (#118549) --- Doc/library/csv.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 4ee7820585d3a2..d17468023c6de1 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -156,8 +156,10 @@ The :mod:`csv` module defines the following classes: The *fieldnames* parameter is a :term:`sequence`. If *fieldnames* is omitted, the values in the first row of file *f* will be used as the - fieldnames. Regardless of how the fieldnames are determined, the - dictionary preserves their original ordering. + fieldnames and will be omitted from the results. If + *fieldnames* is provided, they will be used and the first row will be + included in the results. Regardless of how the fieldnames are determined, + the dictionary preserves their original ordering. If a row has more fields than fieldnames, the remaining data is put in a list and stored with the fieldname specified by *restkey* (which defaults From 13245027526bf1b21fae6e7ca62ceec2e39fcfb7 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 3 May 2024 18:00:43 +0200 Subject: [PATCH 187/217] gh-115119: Update Windows installer to mpdecimal 4.0.0 (#118506) --- .../2024-05-02-09-28-04.gh-issue-115119.cUKMXo.rst | 1 + Misc/externals.spdx.json | 8 ++++---- PCbuild/get_externals.bat | 2 +- PCbuild/python.props | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-05-02-09-28-04.gh-issue-115119.cUKMXo.rst diff --git a/Misc/NEWS.d/next/Windows/2024-05-02-09-28-04.gh-issue-115119.cUKMXo.rst b/Misc/NEWS.d/next/Windows/2024-05-02-09-28-04.gh-issue-115119.cUKMXo.rst new file mode 100644 index 00000000000000..74e9b956a0feaa --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-05-02-09-28-04.gh-issue-115119.cUKMXo.rst @@ -0,0 +1 @@ +Update Windows installer to use libmpdecimal 4.0.0. diff --git a/Misc/externals.spdx.json b/Misc/externals.spdx.json index bef0ce7d7e6175..58f8e0afd71f1b 100644 --- a/Misc/externals.spdx.json +++ b/Misc/externals.spdx.json @@ -48,14 +48,14 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "93118043651ffa33dcaaab445bae4f8929fca25d2d749079b78e97f220c3d8b1" + "checksumValue": "338fac3fb8cdd60f406b6326431338756f58a8af94229ffd9bf1e7c2b1ad71ca" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/mpdecimal-2.5.1.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/mpdecimal-4.0.0.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:bytereef:mpdecimal:2.5.1:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:bytereef:mpdecimal:4.0.0:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], @@ -63,7 +63,7 @@ "name": "mpdecimal", "originator": "Organization: bytereef.org", "primaryPackagePurpose": "SOURCE", - "versionInfo": "2.5.1" + "versionInfo": "4.0.0" }, { "SPDXID": "SPDXRef-PACKAGE-openssl", diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 8f07c414272c47..761d3de93b777d 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,7 +54,7 @@ set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.13 -set libraries=%libraries% mpdecimal-2.5.1 +set libraries=%libraries% mpdecimal-4.0.0 set libraries=%libraries% sqlite-3.45.3.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.13.1 diff --git a/PCbuild/python.props b/PCbuild/python.props index 9f8b42d71cd2f3..86fe8531d7df55 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -74,7 +74,7 @@ $(ExternalsDir)libffi-3.4.4\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)\mpdecimal-2.5.1\ + $(ExternalsDir)\mpdecimal-4.0.0\ $(ExternalsDir)openssl-3.0.13\ $(ExternalsDir)openssl-bin-3.0.13\$(ArchName)\ $(opensslOutDir)include From dd8f05fee26cb98770744291a007ceb6b49a5089 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 3 May 2024 18:49:40 +0200 Subject: [PATCH 188/217] gh-115119: Update macOS installer to build with libmpdec 4.0.0 (GH-118382) Co-authored-by: Ned Deily --- Mac/BuildScript/README.rst | 2 ++ Mac/BuildScript/build-installer.py | 10 ++++++++++ .../2024-05-03-12-13-27.gh-issue-115119.ltDtoR.rst | 1 + 3 files changed, 13 insertions(+) create mode 100644 Misc/NEWS.d/next/macOS/2024-05-03-12-13-27.gh-issue-115119.ltDtoR.rst diff --git a/Mac/BuildScript/README.rst b/Mac/BuildScript/README.rst index 4f74e7dc00520a..a9fae36ba28ae9 100644 --- a/Mac/BuildScript/README.rst +++ b/Mac/BuildScript/README.rst @@ -82,6 +82,7 @@ download them. * SQLite * XZ * libffi + * mpdecimal - uses system-supplied versions of third-party libraries @@ -111,6 +112,7 @@ download them. * SQLite * XZ * libffi + * mpdecimal - uses system-supplied versions of third-party libraries diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 286df4862793fb..8386e407f49aa3 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -378,6 +378,15 @@ def library_recipes(): '--disable-dependency-tracking', ] ), + dict( + name="libmpdec 4.0.0", + url="https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-4.0.0.tar.gz", + checksum="942445c3245b22730fd41a67a7c5c231d11cb1b9936b9c0f76334fb7d0b4468c", + configure_pre=[ + "--disable-cxx", + "MACHINE=universal", + ] + ), ]) if not PYTHON_3: @@ -1150,6 +1159,7 @@ def buildPython(): print(" NOTE: --with-mimalloc=no pending resolution of weak linking issues") runCommand("%s -C --enable-framework --enable-universalsdk=/ " "--with-mimalloc=no " + "--with-system-libmpdec " "--with-universal-archs=%s " "%s " "%s " diff --git a/Misc/NEWS.d/next/macOS/2024-05-03-12-13-27.gh-issue-115119.ltDtoR.rst b/Misc/NEWS.d/next/macOS/2024-05-03-12-13-27.gh-issue-115119.ltDtoR.rst new file mode 100644 index 00000000000000..693dcc72628282 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2024-05-03-12-13-27.gh-issue-115119.ltDtoR.rst @@ -0,0 +1 @@ +Update macOS installer to use libmpdecimal 4.0.0. From cb57a52a85a7845b1c017085f05a7f6d71855edc Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Fri, 3 May 2024 10:49:51 -0700 Subject: [PATCH 189/217] GH-118487: Add Black to `.pre-commit-config.yaml` for JIT files (GH-118537) --- .pre-commit-config.yaml | 8 ++++++++ Tools/jit/_targets.py | 11 +++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 663a11897d98e2..fde9d9149bf62b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,6 +11,14 @@ repos: args: [--exit-non-zero-on-fix, --config=Tools/clinic/.ruff.toml] files: ^Tools/clinic/|Lib/test/test_clinic.py + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.4.2 + hooks: + - id: black + name: Run Black on Tools/jit/ + files: ^Tools/jit/ + language_version: python3.12 + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 23bb18947f80ea..023ef498a21d7c 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -154,13 +154,20 @@ async def _compile( args_ll = args + [ # -fomit-frame-pointer is necessary because the GHC calling # convention uses RBP to pass arguments: - "-S", "-emit-llvm", "-fomit-frame-pointer", "-o", f"{ll}", f"{c}" + "-S", + "-emit-llvm", + "-fomit-frame-pointer", + "-o", + f"{ll}", + f"{c}", ] await _llvm.run("clang", args_ll, echo=self.verbose) ir = ll.read_text() # This handles declarations, definitions, and calls to named symbols # starting with "_JIT_": - ir = re.sub(r"(((noalias|nonnull|noundef) )*ptr @_JIT_\w+\()", r"ghccc \1", ir) + ir = re.sub( + r"(((noalias|nonnull|noundef) )*ptr @_JIT_\w+\()", r"ghccc \1", ir + ) # This handles calls to anonymous callees, since anything with # "musttail" needs to use the same calling convention: ir = ir.replace("musttail call", "musttail call ghccc") From 998c3856c1e922ece806c162858dc587a1e92e02 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Fri, 3 May 2024 11:45:46 -0700 Subject: [PATCH 190/217] gh-83856: Honor atexit for all multiprocessing start methods (GH-114279) Use atexit for all multiprocessing start methods to cleanup. See the GH-114279 PR discussion and related issue for details as to why. --- Lib/multiprocessing/forkserver.py | 4 ++++ Lib/multiprocessing/popen_fork.py | 4 ++++ Lib/multiprocessing/process.py | 7 ++---- Lib/test/_test_multiprocessing.py | 23 +++++++++++++++++++ ...4-01-19-05-40-46.gh-issue-83856.jN5M80.rst | 1 + 5 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-01-19-05-40-46.gh-issue-83856.jN5M80.rst diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index 4642707dae2f4e..53b8c492675878 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -1,3 +1,4 @@ +import atexit import errno import os import selectors @@ -271,6 +272,8 @@ def sigchld_handler(*_unused): selector.close() unused_fds = [alive_r, child_w, sig_r, sig_w] unused_fds.extend(pid_to_fd.values()) + atexit._clear() + atexit.register(util._exit_function) code = _serve_one(child_r, fds, unused_fds, old_handlers) @@ -278,6 +281,7 @@ def sigchld_handler(*_unused): sys.excepthook(*sys.exc_info()) sys.stderr.flush() finally: + atexit._run_exitfuncs() os._exit(code) else: # Send pid to client process diff --git a/Lib/multiprocessing/popen_fork.py b/Lib/multiprocessing/popen_fork.py index 625981cf47627c..a57ef6bdad5ccc 100644 --- a/Lib/multiprocessing/popen_fork.py +++ b/Lib/multiprocessing/popen_fork.py @@ -1,3 +1,4 @@ +import atexit import os import signal @@ -66,10 +67,13 @@ def _launch(self, process_obj): self.pid = os.fork() if self.pid == 0: try: + atexit._clear() + atexit.register(util._exit_function) os.close(parent_r) os.close(parent_w) code = process_obj._bootstrap(parent_sentinel=child_r) finally: + atexit._run_exitfuncs() os._exit(code) else: os.close(child_w) diff --git a/Lib/multiprocessing/process.py b/Lib/multiprocessing/process.py index 271ba3fd325138..b45f7df476f7d8 100644 --- a/Lib/multiprocessing/process.py +++ b/Lib/multiprocessing/process.py @@ -310,11 +310,8 @@ def _bootstrap(self, parent_sentinel=None): # _run_after_forkers() is executed del old_process util.info('child process calling self.run()') - try: - self.run() - exitcode = 0 - finally: - util._exit_function() + self.run() + exitcode = 0 except SystemExit as e: if e.code is None: exitcode = 0 diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 5fc4181a1eeadb..46afdfca331a23 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -6161,6 +6161,29 @@ def submain(): pass self.assertFalse(err, msg=err.decode('utf-8')) +class _TestAtExit(BaseTestCase): + + ALLOWED_TYPES = ('processes',) + + @classmethod + def _write_file_at_exit(self, output_path): + import atexit + def exit_handler(): + with open(output_path, 'w') as f: + f.write("deadbeef") + atexit.register(exit_handler) + + def test_atexit(self): + # gh-83856 + with os_helper.temp_dir() as temp_dir: + output_path = os.path.join(temp_dir, 'output.txt') + p = self.Process(target=self._write_file_at_exit, args=(output_path,)) + p.start() + p.join() + with open(output_path) as f: + self.assertEqual(f.read(), 'deadbeef') + + class MiscTestCase(unittest.TestCase): def test__all__(self): # Just make sure names in not_exported are excluded diff --git a/Misc/NEWS.d/next/Library/2024-01-19-05-40-46.gh-issue-83856.jN5M80.rst b/Misc/NEWS.d/next/Library/2024-01-19-05-40-46.gh-issue-83856.jN5M80.rst new file mode 100644 index 00000000000000..b2889f216a0beb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-01-19-05-40-46.gh-issue-83856.jN5M80.rst @@ -0,0 +1 @@ +Honor :mod:`atexit` for all :mod:`multiprocessing` start methods From 9c14ed06188aa4d462cd0fc4218c6023f9bf03cb Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Fri, 3 May 2024 11:49:24 -0700 Subject: [PATCH 191/217] gh-107674: Improve performance of `sys.settrace` (GH-117133) * Check tracing in RESUME_CHECK * Only change to RESUME_CHECK if not tracing --- ...-02-04-07-45-29.gh-issue-107674.q8mCmi.rst | 1 + Python/bytecodes.c | 33 ++++++++++--------- Python/ceval.c | 28 +++++++++------- Python/ceval_macros.h | 16 +++++---- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 31 ++++++++--------- Python/instrumentation.c | 4 +-- 7 files changed, 63 insertions(+), 52 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-02-04-07-45-29.gh-issue-107674.q8mCmi.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-02-04-07-45-29.gh-issue-107674.q8mCmi.rst b/Misc/NEWS.d/next/Core and Builtins/2024-02-04-07-45-29.gh-issue-107674.q8mCmi.rst new file mode 100644 index 00000000000000..f9b96788bfad94 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-02-04-07-45-29.gh-issue-107674.q8mCmi.rst @@ -0,0 +1 @@ +Improved the performance of :func:`sys.settrace` significantly diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 9769cfe68aaeba..e8383eda6a9d49 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -148,20 +148,18 @@ dummy_func( tier1 inst(RESUME, (--)) { assert(frame == tstate->current_frame); - uintptr_t global_version = - _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & - ~_PY_EVAL_EVENTS_MASK; - PyCodeObject *code = _PyFrame_GetCode(frame); - uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version); - assert((code_version & 255) == 0); - if (code_version != global_version) { - int err = _Py_Instrument(code, tstate->interp); - ERROR_IF(err, error); - next_instr = this_instr; - } - else { - if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - CHECK_EVAL_BREAKER(); + if (tstate->tracing == 0) { + uintptr_t global_version = + _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & + ~_PY_EVAL_EVENTS_MASK; + PyCodeObject* code = _PyFrame_GetCode(frame); + uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version); + assert((code_version & 255) == 0); + if (code_version != global_version) { + int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); + ERROR_IF(err, error); + next_instr = this_instr; + DISPATCH(); } assert(this_instr->op.code == RESUME || this_instr->op.code == RESUME_CHECK || @@ -173,6 +171,9 @@ dummy_func( #endif /* ENABLE_SPECIALIZATION */ } } + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + CHECK_EVAL_BREAKER(); + } } inst(RESUME_CHECK, (--)) { @@ -189,7 +190,7 @@ dummy_func( inst(INSTRUMENTED_RESUME, (--)) { uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK; uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); - if (code_version != global_version) { + if (code_version != global_version && tstate->tracing == 0) { if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { ERROR_NO_POP(); } @@ -4284,7 +4285,7 @@ dummy_func( #endif uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); DEOPT_IF(eval_breaker & _PY_EVAL_EVENTS_MASK); - assert(eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); + assert(tstate->tracing || eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); } // END BYTECODES // diff --git a/Python/ceval.c b/Python/ceval.c index 59498bc826e941..118746909902f3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -808,17 +808,23 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int { _Py_CODEUNIT *prev = frame->instr_ptr; _Py_CODEUNIT *here = frame->instr_ptr = next_instr; - _PyFrame_SetStackPointer(frame, stack_pointer); - int original_opcode = _Py_call_instrumentation_line( - tstate, frame, here, prev); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (original_opcode < 0) { - next_instr = here+1; - goto error; - } - next_instr = frame->instr_ptr; - if (next_instr != here) { - DISPATCH(); + int original_opcode = 0; + if (tstate->tracing) { + PyCodeObject *code = _PyFrame_GetCode(frame); + original_opcode = code->_co_monitoring->lines[(int)(here - _PyCode_CODE(code))].original_opcode; + } else { + _PyFrame_SetStackPointer(frame, stack_pointer); + original_opcode = _Py_call_instrumentation_line( + tstate, frame, here, prev); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (original_opcode < 0) { + next_instr = here+1; + goto error; + } + next_instr = frame->instr_ptr; + if (next_instr != here) { + DISPATCH(); + } } if (_PyOpcode_Caches[original_opcode]) { _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1); diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index c88a07c1f5e951..50941e4ec473e8 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -358,12 +358,16 @@ do { \ // for an exception handler, displaying the traceback, and so on #define INSTRUMENTED_JUMP(src, dest, event) \ do { \ - _PyFrame_SetStackPointer(frame, stack_pointer); \ - next_instr = _Py_call_instrumentation_jump(tstate, event, frame, src, dest); \ - stack_pointer = _PyFrame_GetStackPointer(frame); \ - if (next_instr == NULL) { \ - next_instr = (dest)+1; \ - goto error; \ + if (tstate->tracing) {\ + next_instr = dest; \ + } else { \ + _PyFrame_SetStackPointer(frame, stack_pointer); \ + next_instr = _Py_call_instrumentation_jump(tstate, event, frame, src, dest); \ + stack_pointer = _PyFrame_GetStackPointer(frame); \ + if (next_instr == NULL) { \ + next_instr = (dest)+1; \ + goto error; \ + } \ } \ } while (0); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 03db9b623cbd86..c3ee6e9039c900 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4346,7 +4346,7 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - assert(eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); + assert(tstate->tracing || eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 2a0f268ce6ed54..800d19229e3d6a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3292,7 +3292,7 @@ INSTRUCTION_STATS(INSTRUMENTED_RESUME); uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK; uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); - if (code_version != global_version) { + if (code_version != global_version && tstate->tracing == 0) { if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { goto error; } @@ -4948,20 +4948,18 @@ _Py_CODEUNIT *this_instr = next_instr - 1; (void)this_instr; assert(frame == tstate->current_frame); - uintptr_t global_version = - _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & - ~_PY_EVAL_EVENTS_MASK; - PyCodeObject *code = _PyFrame_GetCode(frame); - uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version); - assert((code_version & 255) == 0); - if (code_version != global_version) { - int err = _Py_Instrument(code, tstate->interp); - if (err) goto error; - next_instr = this_instr; - } - else { - if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - CHECK_EVAL_BREAKER(); + if (tstate->tracing == 0) { + uintptr_t global_version = + _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & + ~_PY_EVAL_EVENTS_MASK; + PyCodeObject* code = _PyFrame_GetCode(frame); + uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version); + assert((code_version & 255) == 0); + if (code_version != global_version) { + int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); + if (err) goto error; + next_instr = this_instr; + DISPATCH(); } assert(this_instr->op.code == RESUME || this_instr->op.code == RESUME_CHECK || @@ -4973,6 +4971,9 @@ #endif /* ENABLE_SPECIALIZATION */ } } + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + CHECK_EVAL_BREAKER(); + } DISPATCH(); } diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 5614f4867a390d..8085d7335fe21a 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1217,15 +1217,13 @@ int _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *prev) { PyCodeObject *code = _PyFrame_GetCode(frame); + assert(tstate->tracing == 0); assert(is_version_up_to_date(code, tstate->interp)); assert(instrumentation_cross_checks(tstate->interp, code)); int i = (int)(instr - _PyCode_CODE(code)); _PyCoMonitoringData *monitoring = code->_co_monitoring; _PyCoLineInstrumentationData *line_data = &monitoring->lines[i]; - if (tstate->tracing) { - goto done; - } PyInterpreterState *interp = tstate->interp; int8_t line_delta = line_data->line_delta; int line = 0; From 52485967813acdb35c274e1b2eaedd34e9ac01fc Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Fri, 3 May 2024 12:07:10 -0700 Subject: [PATCH 192/217] GH-118251: Skip fewer test in emulated JIT CI (GH-118536) --- .github/workflows/jit.yml | 13 ++-- Tools/jit/ignore-tests-emulated-linux.txt | 79 +++++++++++++++++++++++ 2 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 Tools/jit/ignore-tests-emulated-linux.txt diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index b969e6d13e61a3..014cced8e81b52 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -75,14 +75,10 @@ jobs: architecture: aarch64 runner: ubuntu-latest compiler: gcc - # These fail because of emulation, not because of the JIT: - exclude: test_pathlib test_posixpath test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv test_external_inspection - target: aarch64-unknown-linux-gnu/clang architecture: aarch64 runner: ubuntu-latest compiler: clang - # These fail because of emulation, not because of the JIT: - exclude: test_pathlib test_posixpath test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv test_external_inspection env: CC: ${{ matrix.compiler }} steps: @@ -97,7 +93,7 @@ jobs: choco upgrade llvm -y choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }} ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '--pgo' }} -p ${{ matrix.architecture }} - ./PCbuild/rt.bat ${{ matrix.debug && '-d' }} -p ${{ matrix.architecture }} -q --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + ./PCbuild/rt.bat ${{ matrix.debug && '-d' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3 # No PGO or tests (yet): - name: Emulated Windows @@ -115,7 +111,7 @@ jobs: SDKROOT="$(xcrun --show-sdk-path)" \ ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }} make all --jobs 4 - ./python.exe -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 # --with-lto has been removed temporarily as a result of an open issue in LLVM 18 (see https://github.com/llvm/llvm-project/issues/87553) - name: Native Linux @@ -125,11 +121,12 @@ jobs: export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH" ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations' }} make all --jobs 4 - ./python -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 # --with-lto has been removed temporarily as a result of an open issue in LLVM 18 (see https://github.com/llvm/llvm-project/issues/87553) - name: Emulated Linux if: runner.os == 'Linux' && matrix.architecture != 'x86_64' + # The --ignorefile on ./python -m test is used to exclude tests known to fail when running on an emulated Linux. run: | sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }} export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH" @@ -146,4 +143,4 @@ jobs: HOSTRUNNER=qemu-${{ matrix.architecture }} \ ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations ' }} --build=x86_64-linux-gnu --host="$HOST" --with-build-python=../build/bin/python3 --with-pkg-config=no ac_cv_buggy_getaddrinfo=no ac_cv_file__dev_ptc=no ac_cv_file__dev_ptmx=yes make all --jobs 4 - ./python -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + ./python -m test --ignorefile=Tools/jit/ignore-tests-emulated-linux.txt --multiprocess 0 --timeout 4500 --verbose2 --verbose3 diff --git a/Tools/jit/ignore-tests-emulated-linux.txt b/Tools/jit/ignore-tests-emulated-linux.txt new file mode 100644 index 00000000000000..84e8c0ee8afedb --- /dev/null +++ b/Tools/jit/ignore-tests-emulated-linux.txt @@ -0,0 +1,79 @@ +test_multiprocessing_fork +test.test_asyncio.test_unix_events.TestFork.test_fork_asyncio_run +test.test_asyncio.test_unix_events.TestFork.test_fork_asyncio_subprocess +test.test_asyncio.test_unix_events.TestFork.test_fork_signal_handling +test.test_cmd_line.CmdLineTest.test_no_std_streams +test.test_cmd_line.CmdLineTest.test_no_stdin +test.test_concurrent_futures.test_init.ProcessPoolForkFailingInitializerTest.test_initializer +test.test_concurrent_futures.test_process_pool.ProcessPoolForkProcessPoolExecutorTest.test_ressources_gced_in_workers +test.test_external_inspection.TestGetStackTrace.test_remote_stack_trace +test.test_external_inspection.TestGetStackTrace.test_self_trace +test.test_faulthandler.FaultHandlerTests.test_enable_fd +test.test_faulthandler.FaultHandlerTests.test_enable_file +test.test_init.ProcessPoolForkFailingInitializerTest.test_initializer +test.test_os.ForkTests.test_fork_warns_when_non_python_thread_exists +test.test_os.TimerfdTests.test_timerfd_initval +test.test_os.TimerfdTests.test_timerfd_interval +test.test_os.TimerfdTests.test_timerfd_TFD_TIMER_ABSTIME +test.test_pathlib.PathSubclassTest.test_is_mount_root +test.test_pathlib.PathTest.test_is_mount_root +test.test_pathlib.PosixPathTest.test_is_mount_root +test.test_pathlib.test_pathlib.PathSubclassTest.test_is_mount_root +test.test_pathlib.test_pathlib.PathTest.test_is_mount_root +test.test_pathlib.test_pathlib.PosixPathTest.test_is_mount_root +test.test_posix.TestPosixSpawn.test_close_file +test.test_posix.TestPosixSpawnP.test_close_file +test.test_posixpath.PosixPathTest.test_ismount +test.test_signal.StressTest.test_stress_modifying_handlers +test.test_socket.BasicCANTest.testFilter +test.test_socket.BasicCANTest.testLoopback +test.test_socket.LinuxKernelCryptoAPI.test_aead_aes_gcm +test.test_socket.LinuxKernelCryptoAPI.test_aes_cbc +test.test_socket.RecvmsgIntoRFC3542AncillaryUDP6Test.testSecondCmsgTrunc1 +test.test_socket.RecvmsgIntoRFC3542AncillaryUDP6Test.testSecondCmsgTrunc2Int +test.test_socket.RecvmsgIntoRFC3542AncillaryUDP6Test.testSecondCmsgTruncInData +test.test_socket.RecvmsgIntoRFC3542AncillaryUDP6Test.testSecondCmsgTruncLen0Minus1 +test.test_socket.RecvmsgIntoRFC3542AncillaryUDP6Test.testSingleCmsgTruncInData +test.test_socket.RecvmsgIntoRFC3542AncillaryUDP6Test.testSingleCmsgTruncLen0Minus1 +test.test_socket.RecvmsgIntoRFC3542AncillaryUDPLITE6Test.testSecondCmsgTrunc1 +test.test_socket.RecvmsgIntoRFC3542AncillaryUDPLITE6Test.testSecondCmsgTrunc2Int +test.test_socket.RecvmsgIntoRFC3542AncillaryUDPLITE6Test.testSecondCmsgTruncInData +test.test_socket.RecvmsgIntoRFC3542AncillaryUDPLITE6Test.testSecondCmsgTruncLen0Minus1 +test.test_socket.RecvmsgIntoRFC3542AncillaryUDPLITE6Test.testSingleCmsgTruncInData +test.test_socket.RecvmsgIntoRFC3542AncillaryUDPLITE6Test.testSingleCmsgTruncLen0Minus1 +test.test_socket.RecvmsgIntoSCMRightsStreamTest.testCmsgTruncLen0 +test.test_socket.RecvmsgIntoSCMRightsStreamTest.testCmsgTruncLen0Minus1 +test.test_socket.RecvmsgIntoSCMRightsStreamTest.testCmsgTruncLen0Plus1 +test.test_socket.RecvmsgIntoSCMRightsStreamTest.testCmsgTruncLen1 +test.test_socket.RecvmsgIntoSCMRightsStreamTest.testCmsgTruncLen2Minus1 +test.test_socket.RecvmsgRFC3542AncillaryUDP6Test.testSecondCmsgTrunc1 +test.test_socket.RecvmsgRFC3542AncillaryUDP6Test.testSecondCmsgTrunc2Int +test.test_socket.RecvmsgRFC3542AncillaryUDP6Test.testSecondCmsgTruncInData +test.test_socket.RecvmsgRFC3542AncillaryUDP6Test.testSecondCmsgTruncLen0Minus1 +test.test_socket.RecvmsgRFC3542AncillaryUDP6Test.testSingleCmsgTruncInData +test.test_socket.RecvmsgRFC3542AncillaryUDP6Test.testSingleCmsgTruncLen0Minus1 +test.test_socket.RecvmsgRFC3542AncillaryUDPLITE6Test.testSecondCmsgTrunc1 +test.test_socket.RecvmsgRFC3542AncillaryUDPLITE6Test.testSecondCmsgTrunc2Int +test.test_socket.RecvmsgRFC3542AncillaryUDPLITE6Test.testSecondCmsgTruncInData +test.test_socket.RecvmsgRFC3542AncillaryUDPLITE6Test.testSecondCmsgTruncLen0Minus1 +test.test_socket.RecvmsgRFC3542AncillaryUDPLITE6Test.testSingleCmsgTruncInData +test.test_socket.RecvmsgRFC3542AncillaryUDPLITE6Test.testSingleCmsgTruncLen0Minus1 +test.test_socket.RecvmsgRFC3542AncillaryUDPLITE6Test.testSingleCmsgTruncLen0Minus1 +test.test_socket.RecvmsgSCMRightsStreamTest.testCmsgTruncLen0 +test.test_socket.RecvmsgSCMRightsStreamTest.testCmsgTruncLen0Minus1 +test.test_socket.RecvmsgSCMRightsStreamTest.testCmsgTruncLen0Plus1 +test.test_socket.RecvmsgSCMRightsStreamTest.testCmsgTruncLen1 +test.test_socket.RecvmsgSCMRightsStreamTest.testCmsgTruncLen2Minus1 +test.test_subprocess.POSIXProcessTestCase.test_exception_bad_args_0 +test.test_subprocess.POSIXProcessTestCase.test_exception_bad_executable +test.test_subprocess.ProcessTestCase.test_cwd_with_relative_arg +test.test_subprocess.ProcessTestCase.test_cwd_with_relative_executable +test.test_subprocess.ProcessTestCase.test_empty_env +test.test_subprocess.ProcessTestCase.test_file_not_found_includes_filename +test.test_subprocess.ProcessTestCase.test_one_environment_variable +test.test_subprocess.ProcessTestCaseNoPoll.test_cwd_with_relative_arg +test.test_subprocess.ProcessTestCaseNoPoll.test_cwd_with_relative_executable +test.test_subprocess.ProcessTestCaseNoPoll.test_empty_env +test.test_subprocess.ProcessTestCaseNoPoll.test_file_not_found_includes_filename +test.test_subprocess.ProcessTestCaseNoPoll.test_one_environment_variable +test.test_venv.BasicTest.test_zippath_from_non_installed_posix \ No newline at end of file From 37d095002216a4e45b4e82539ca0421ded8aaae3 Mon Sep 17 00:00:00 2001 From: mpage Date: Fri, 3 May 2024 13:21:04 -0700 Subject: [PATCH 193/217] gh-117657: Disable the function/code cache in free-threaded builds (#118301) This is only used by the specializing interpreter and the tier 2 optimizer, both of which are disabled in free-threaded builds. --- Include/internal/pycore_function.h | 7 +++++++ Objects/codeobject.c | 6 ++++++ Objects/funcobject.c | 10 ++++++++++ Tools/tsan/suppressions_free_threading.txt | 1 - 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_function.h b/Include/internal/pycore_function.h index 24fbb3ddbee602..6d44e933e8a8cb 100644 --- a/Include/internal/pycore_function.h +++ b/Include/internal/pycore_function.h @@ -4,6 +4,8 @@ extern "C" { #endif +#include "pycore_lock.h" + #ifndef Py_BUILD_CORE # error "this header requires Py_BUILD_CORE define" #endif @@ -24,6 +26,11 @@ struct _func_version_cache_item { }; struct _py_func_state { +#ifdef Py_GIL_DISABLED + // Protects next_version + PyMutex mutex; +#endif + uint32_t next_version; // Borrowed references to function and code objects whose // func_version % FUNC_VERSION_CACHE_SIZE diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 810f847485acda..7d02b038cf52ca 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -416,10 +416,16 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_ncellvars = ncellvars; co->co_nfreevars = nfreevars; PyInterpreterState *interp = _PyInterpreterState_GET(); +#ifdef Py_GIL_DISABLED + PyMutex_Lock(&interp->func_state.mutex); +#endif co->co_version = interp->func_state.next_version; if (interp->func_state.next_version != 0) { interp->func_state.next_version++; } +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&interp->func_state.mutex); +#endif co->_co_monitoring = NULL; co->_co_instrumentation_version = 0; /* not set */ diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 276b3db2970371..8a30213888ef87 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -287,6 +287,7 @@ functions is running. void _PyFunction_SetVersion(PyFunctionObject *func, uint32_t version) { +#ifndef Py_GIL_DISABLED PyInterpreterState *interp = _PyInterpreterState_GET(); if (func->func_version != 0) { struct _func_version_cache_item *slot = @@ -297,7 +298,9 @@ _PyFunction_SetVersion(PyFunctionObject *func, uint32_t version) // Leave slot->code alone, there may be use for it. } } +#endif func->func_version = version; +#ifndef Py_GIL_DISABLED if (version != 0) { struct _func_version_cache_item *slot = interp->func_state.func_version_cache @@ -305,11 +308,13 @@ _PyFunction_SetVersion(PyFunctionObject *func, uint32_t version) slot->func = func; slot->code = func->func_code; } +#endif } void _PyFunction_ClearCodeByVersion(uint32_t version) { +#ifndef Py_GIL_DISABLED PyInterpreterState *interp = _PyInterpreterState_GET(); struct _func_version_cache_item *slot = interp->func_state.func_version_cache @@ -322,11 +327,15 @@ _PyFunction_ClearCodeByVersion(uint32_t version) slot->func = NULL; } } +#endif } PyFunctionObject * _PyFunction_LookupByVersion(uint32_t version, PyObject **p_code) { +#ifdef Py_GIL_DISABLED + return NULL; +#else PyInterpreterState *interp = _PyInterpreterState_GET(); struct _func_version_cache_item *slot = interp->func_state.func_version_cache @@ -346,6 +355,7 @@ _PyFunction_LookupByVersion(uint32_t version, PyObject **p_code) return slot->func; } return NULL; +#endif } uint32_t diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 4f6648a7573184..74dbf4bb1cb688 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -15,7 +15,6 @@ race:_add_to_weak_set race:_in_weak_set race:_mi_heap_delayed_free_partial race:_PyEval_EvalFrameDefault -race:_PyFunction_SetVersion race:_PyImport_AcquireLock race:_PyImport_ReleaseLock race:_PyInterpreterState_SetNotRunningMain From a40f557d7b7a355a55bb90c068e3e9202fd9c8f2 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 3 May 2024 21:29:25 +0100 Subject: [PATCH 194/217] GH-116380: Move pathlib globbing implementation into `pathlib._glob` (#118562) Moving this code under the `pathlib` package makes it quite a lot easier to backport in the `pathlib-abc` PyPI package. It was a bit foolish of me to add it to `glob` in the first place. Also add `translate()` to `__all__` in `glob`. This function is new in 3.13, so there's no NEWS needed. --- Lib/glob.py | 306 +--------------------------------------- Lib/pathlib/__init__.py | 5 +- Lib/pathlib/_abc.py | 7 +- Lib/pathlib/_glob.py | 305 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+), 309 deletions(-) create mode 100644 Lib/pathlib/_glob.py diff --git a/Lib/glob.py b/Lib/glob.py index 72cf22299763f0..9fe0e9f98ac5a7 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -2,15 +2,14 @@ import contextlib import os -import re import fnmatch -import functools import itertools -import operator import stat import sys -__all__ = ["glob", "iglob", "escape"] +from pathlib._glob import translate, magic_check, magic_check_bytes + +__all__ = ["glob", "iglob", "escape", "translate"] def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, include_hidden=False): @@ -226,9 +225,6 @@ def _join(dirname, basename): return dirname or basename return os.path.join(dirname, basename) -magic_check = re.compile('([*?[])') -magic_check_bytes = re.compile(b'([*?[])') - def has_magic(s): if isinstance(s, bytes): match = magic_check_bytes.search(s) @@ -258,300 +254,4 @@ def escape(pathname): return drive + pathname -_special_parts = ('', '.', '..') _dir_open_flags = os.O_RDONLY | getattr(os, 'O_DIRECTORY', 0) -_no_recurse_symlinks = object() - - -def translate(pat, *, recursive=False, include_hidden=False, seps=None): - """Translate a pathname with shell wildcards to a regular expression. - - If `recursive` is true, the pattern segment '**' will match any number of - path segments. - - If `include_hidden` is true, wildcards can match path segments beginning - with a dot ('.'). - - If a sequence of separator characters is given to `seps`, they will be - used to split the pattern into segments and match path separators. If not - given, os.path.sep and os.path.altsep (where available) are used. - """ - if not seps: - if os.path.altsep: - seps = (os.path.sep, os.path.altsep) - else: - seps = os.path.sep - escaped_seps = ''.join(map(re.escape, seps)) - any_sep = f'[{escaped_seps}]' if len(seps) > 1 else escaped_seps - not_sep = f'[^{escaped_seps}]' - if include_hidden: - one_last_segment = f'{not_sep}+' - one_segment = f'{one_last_segment}{any_sep}' - any_segments = f'(?:.+{any_sep})?' - any_last_segments = '.*' - else: - one_last_segment = f'[^{escaped_seps}.]{not_sep}*' - one_segment = f'{one_last_segment}{any_sep}' - any_segments = f'(?:{one_segment})*' - any_last_segments = f'{any_segments}(?:{one_last_segment})?' - - results = [] - parts = re.split(any_sep, pat) - last_part_idx = len(parts) - 1 - for idx, part in enumerate(parts): - if part == '*': - results.append(one_segment if idx < last_part_idx else one_last_segment) - elif recursive and part == '**': - if idx < last_part_idx: - if parts[idx + 1] != '**': - results.append(any_segments) - else: - results.append(any_last_segments) - else: - if part: - if not include_hidden and part[0] in '*?': - results.append(r'(?!\.)') - results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep)) - if idx < last_part_idx: - results.append(any_sep) - res = ''.join(results) - return fr'(?s:{res})\Z' - - -@functools.lru_cache(maxsize=512) -def _compile_pattern(pat, sep, case_sensitive, recursive=True): - """Compile given glob pattern to a re.Pattern object (observing case - sensitivity).""" - flags = re.NOFLAG if case_sensitive else re.IGNORECASE - regex = translate(pat, recursive=recursive, include_hidden=True, seps=sep) - return re.compile(regex, flags=flags).match - - -class _Globber: - """Class providing shell-style pattern matching and globbing. - """ - - def __init__(self, sep, case_sensitive, case_pedantic=False, recursive=False): - self.sep = sep - self.case_sensitive = case_sensitive - self.case_pedantic = case_pedantic - self.recursive = recursive - - # Low-level methods - - lstat = staticmethod(os.lstat) - scandir = staticmethod(os.scandir) - parse_entry = operator.attrgetter('path') - concat_path = operator.add - - if os.name == 'nt': - @staticmethod - def add_slash(pathname): - tail = os.path.splitroot(pathname)[2] - if not tail or tail[-1] in '\\/': - return pathname - return f'{pathname}\\' - else: - @staticmethod - def add_slash(pathname): - if not pathname or pathname[-1] == '/': - return pathname - return f'{pathname}/' - - # High-level methods - - def compile(self, pat): - return _compile_pattern(pat, self.sep, self.case_sensitive, self.recursive) - - def selector(self, parts): - """Returns a function that selects from a given path, walking and - filtering according to the glob-style pattern parts in *parts*. - """ - if not parts: - return self.select_exists - part = parts.pop() - if self.recursive and part == '**': - selector = self.recursive_selector - elif part in _special_parts: - selector = self.special_selector - elif not self.case_pedantic and magic_check.search(part) is None: - selector = self.literal_selector - else: - selector = self.wildcard_selector - return selector(part, parts) - - def special_selector(self, part, parts): - """Returns a function that selects special children of the given path. - """ - select_next = self.selector(parts) - - def select_special(path, exists=False): - path = self.concat_path(self.add_slash(path), part) - return select_next(path, exists) - return select_special - - def literal_selector(self, part, parts): - """Returns a function that selects a literal descendant of a path. - """ - - # Optimization: consume and join any subsequent literal parts here, - # rather than leaving them for the next selector. This reduces the - # number of string concatenation operations and calls to add_slash(). - while parts and magic_check.search(parts[-1]) is None: - part += self.sep + parts.pop() - - select_next = self.selector(parts) - - def select_literal(path, exists=False): - path = self.concat_path(self.add_slash(path), part) - return select_next(path, exists=False) - return select_literal - - def wildcard_selector(self, part, parts): - """Returns a function that selects direct children of a given path, - filtering by pattern. - """ - - match = None if part == '*' else self.compile(part) - dir_only = bool(parts) - if dir_only: - select_next = self.selector(parts) - - def select_wildcard(path, exists=False): - try: - # We must close the scandir() object before proceeding to - # avoid exhausting file descriptors when globbing deep trees. - with self.scandir(path) as scandir_it: - entries = list(scandir_it) - except OSError: - pass - else: - for entry in entries: - if match is None or match(entry.name): - if dir_only: - try: - if not entry.is_dir(): - continue - except OSError: - continue - entry_path = self.parse_entry(entry) - if dir_only: - yield from select_next(entry_path, exists=True) - else: - yield entry_path - return select_wildcard - - def recursive_selector(self, part, parts): - """Returns a function that selects a given path and all its children, - recursively, filtering by pattern. - """ - # Optimization: consume following '**' parts, which have no effect. - while parts and parts[-1] == '**': - parts.pop() - - # Optimization: consume and join any following non-special parts here, - # rather than leaving them for the next selector. They're used to - # build a regular expression, which we use to filter the results of - # the recursive walk. As a result, non-special pattern segments - # following a '**' wildcard don't require additional filesystem access - # to expand. - follow_symlinks = self.recursive is not _no_recurse_symlinks - if follow_symlinks: - while parts and parts[-1] not in _special_parts: - part += self.sep + parts.pop() - - match = None if part == '**' else self.compile(part) - dir_only = bool(parts) - select_next = self.selector(parts) - - def select_recursive(path, exists=False): - path = self.add_slash(path) - match_pos = len(str(path)) - if match is None or match(str(path), match_pos): - yield from select_next(path, exists) - stack = [path] - while stack: - yield from select_recursive_step(stack, match_pos) - - def select_recursive_step(stack, match_pos): - path = stack.pop() - try: - # We must close the scandir() object before proceeding to - # avoid exhausting file descriptors when globbing deep trees. - with self.scandir(path) as scandir_it: - entries = list(scandir_it) - except OSError: - pass - else: - for entry in entries: - is_dir = False - try: - if entry.is_dir(follow_symlinks=follow_symlinks): - is_dir = True - except OSError: - pass - - if is_dir or not dir_only: - entry_path = self.parse_entry(entry) - if match is None or match(str(entry_path), match_pos): - if dir_only: - yield from select_next(entry_path, exists=True) - else: - # Optimization: directly yield the path if this is - # last pattern part. - yield entry_path - if is_dir: - stack.append(entry_path) - - return select_recursive - - def select_exists(self, path, exists=False): - """Yields the given path, if it exists. - """ - if exists: - # Optimization: this path is already known to exist, e.g. because - # it was returned from os.scandir(), so we skip calling lstat(). - yield path - else: - try: - self.lstat(path) - yield path - except OSError: - pass - - @classmethod - def walk(cls, root, top_down, on_error, follow_symlinks): - """Walk the directory tree from the given root, similar to os.walk(). - """ - paths = [root] - while paths: - path = paths.pop() - if isinstance(path, tuple): - yield path - continue - try: - with cls.scandir(path) as scandir_it: - dirnames = [] - filenames = [] - if not top_down: - paths.append((path, dirnames, filenames)) - for entry in scandir_it: - name = entry.name - try: - if entry.is_dir(follow_symlinks=follow_symlinks): - if not top_down: - paths.append(cls.parse_entry(entry)) - dirnames.append(name) - else: - filenames.append(name) - except OSError: - filenames.append(name) - except OSError as error: - if on_error is not None: - on_error(error) - else: - if top_down: - yield path, dirnames, filenames - if dirnames: - prefix = cls.add_slash(path) - paths += [cls.concat_path(prefix, d) for d in reversed(dirnames)] diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index 8eecf2cefb790f..381b08cbe8e01f 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -5,7 +5,6 @@ operating systems. """ -import glob import io import ntpath import operator @@ -25,7 +24,7 @@ except ImportError: grp = None -from . import _abc +from . import _abc, _glob __all__ = [ @@ -113,7 +112,7 @@ class PurePath(_abc.PurePathBase): '_hash', ) parser = os.path - _globber = glob._Globber + _globber = _glob.Globber def __new__(cls, *args, **kwargs): """Construct a PurePath from one or several strings and or existing diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index c7e8e2f68ed058..591df443be093a 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -12,11 +12,12 @@ """ import functools -import glob import operator from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO +from . import _glob + # # Internals # @@ -43,7 +44,7 @@ def _is_case_sensitive(parser): return parser.normcase('Aa') == 'Aa' -class Globber(glob._Globber): +class Globber(_glob.Globber): lstat = operator.methodcaller('lstat') add_slash = operator.methodcaller('joinpath', '') @@ -692,7 +693,7 @@ def _glob_selector(self, parts, case_sensitive, recurse_symlinks): # know the case sensitivity of the underlying filesystem, so we # must use scandir() for everything, including non-wildcard parts. case_pedantic = True - recursive = True if recurse_symlinks else glob._no_recurse_symlinks + recursive = True if recurse_symlinks else _glob.no_recurse_symlinks globber = self._globber(self.parser.sep, case_sensitive, case_pedantic, recursive) return globber.selector(parts) diff --git a/Lib/pathlib/_glob.py b/Lib/pathlib/_glob.py new file mode 100644 index 00000000000000..73ccc0677920ce --- /dev/null +++ b/Lib/pathlib/_glob.py @@ -0,0 +1,305 @@ +import os +import re +import fnmatch +import functools +import operator + + +special_parts = ('', '.', '..') +magic_check = re.compile('([*?[])') +magic_check_bytes = re.compile(b'([*?[])') +no_recurse_symlinks = object() + + +def translate(pat, *, recursive=False, include_hidden=False, seps=None): + """Translate a pathname with shell wildcards to a regular expression. + + If `recursive` is true, the pattern segment '**' will match any number of + path segments. + + If `include_hidden` is true, wildcards can match path segments beginning + with a dot ('.'). + + If a sequence of separator characters is given to `seps`, they will be + used to split the pattern into segments and match path separators. If not + given, os.path.sep and os.path.altsep (where available) are used. + """ + if not seps: + if os.path.altsep: + seps = (os.path.sep, os.path.altsep) + else: + seps = os.path.sep + escaped_seps = ''.join(map(re.escape, seps)) + any_sep = f'[{escaped_seps}]' if len(seps) > 1 else escaped_seps + not_sep = f'[^{escaped_seps}]' + if include_hidden: + one_last_segment = f'{not_sep}+' + one_segment = f'{one_last_segment}{any_sep}' + any_segments = f'(?:.+{any_sep})?' + any_last_segments = '.*' + else: + one_last_segment = f'[^{escaped_seps}.]{not_sep}*' + one_segment = f'{one_last_segment}{any_sep}' + any_segments = f'(?:{one_segment})*' + any_last_segments = f'{any_segments}(?:{one_last_segment})?' + + results = [] + parts = re.split(any_sep, pat) + last_part_idx = len(parts) - 1 + for idx, part in enumerate(parts): + if part == '*': + results.append(one_segment if idx < last_part_idx else one_last_segment) + elif recursive and part == '**': + if idx < last_part_idx: + if parts[idx + 1] != '**': + results.append(any_segments) + else: + results.append(any_last_segments) + else: + if part: + if not include_hidden and part[0] in '*?': + results.append(r'(?!\.)') + results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep)) + if idx < last_part_idx: + results.append(any_sep) + res = ''.join(results) + return fr'(?s:{res})\Z' + + +@functools.lru_cache(maxsize=512) +def compile_pattern(pat, sep, case_sensitive, recursive=True): + """Compile given glob pattern to a re.Pattern object (observing case + sensitivity).""" + flags = re.NOFLAG if case_sensitive else re.IGNORECASE + regex = translate(pat, recursive=recursive, include_hidden=True, seps=sep) + return re.compile(regex, flags=flags).match + + +class Globber: + """Class providing shell-style pattern matching and globbing. + """ + + def __init__(self, sep, case_sensitive, case_pedantic=False, recursive=False): + self.sep = sep + self.case_sensitive = case_sensitive + self.case_pedantic = case_pedantic + self.recursive = recursive + + # Low-level methods + + lstat = staticmethod(os.lstat) + scandir = staticmethod(os.scandir) + parse_entry = operator.attrgetter('path') + concat_path = operator.add + + if os.name == 'nt': + @staticmethod + def add_slash(pathname): + tail = os.path.splitroot(pathname)[2] + if not tail or tail[-1] in '\\/': + return pathname + return f'{pathname}\\' + else: + @staticmethod + def add_slash(pathname): + if not pathname or pathname[-1] == '/': + return pathname + return f'{pathname}/' + + # High-level methods + + def compile(self, pat): + return compile_pattern(pat, self.sep, self.case_sensitive, self.recursive) + + def selector(self, parts): + """Returns a function that selects from a given path, walking and + filtering according to the glob-style pattern parts in *parts*. + """ + if not parts: + return self.select_exists + part = parts.pop() + if self.recursive and part == '**': + selector = self.recursive_selector + elif part in special_parts: + selector = self.special_selector + elif not self.case_pedantic and magic_check.search(part) is None: + selector = self.literal_selector + else: + selector = self.wildcard_selector + return selector(part, parts) + + def special_selector(self, part, parts): + """Returns a function that selects special children of the given path. + """ + select_next = self.selector(parts) + + def select_special(path, exists=False): + path = self.concat_path(self.add_slash(path), part) + return select_next(path, exists) + return select_special + + def literal_selector(self, part, parts): + """Returns a function that selects a literal descendant of a path. + """ + + # Optimization: consume and join any subsequent literal parts here, + # rather than leaving them for the next selector. This reduces the + # number of string concatenation operations and calls to add_slash(). + while parts and magic_check.search(parts[-1]) is None: + part += self.sep + parts.pop() + + select_next = self.selector(parts) + + def select_literal(path, exists=False): + path = self.concat_path(self.add_slash(path), part) + return select_next(path, exists=False) + return select_literal + + def wildcard_selector(self, part, parts): + """Returns a function that selects direct children of a given path, + filtering by pattern. + """ + + match = None if part == '*' else self.compile(part) + dir_only = bool(parts) + if dir_only: + select_next = self.selector(parts) + + def select_wildcard(path, exists=False): + try: + # We must close the scandir() object before proceeding to + # avoid exhausting file descriptors when globbing deep trees. + with self.scandir(path) as scandir_it: + entries = list(scandir_it) + except OSError: + pass + else: + for entry in entries: + if match is None or match(entry.name): + if dir_only: + try: + if not entry.is_dir(): + continue + except OSError: + continue + entry_path = self.parse_entry(entry) + if dir_only: + yield from select_next(entry_path, exists=True) + else: + yield entry_path + return select_wildcard + + def recursive_selector(self, part, parts): + """Returns a function that selects a given path and all its children, + recursively, filtering by pattern. + """ + # Optimization: consume following '**' parts, which have no effect. + while parts and parts[-1] == '**': + parts.pop() + + # Optimization: consume and join any following non-special parts here, + # rather than leaving them for the next selector. They're used to + # build a regular expression, which we use to filter the results of + # the recursive walk. As a result, non-special pattern segments + # following a '**' wildcard don't require additional filesystem access + # to expand. + follow_symlinks = self.recursive is not no_recurse_symlinks + if follow_symlinks: + while parts and parts[-1] not in special_parts: + part += self.sep + parts.pop() + + match = None if part == '**' else self.compile(part) + dir_only = bool(parts) + select_next = self.selector(parts) + + def select_recursive(path, exists=False): + path = self.add_slash(path) + match_pos = len(str(path)) + if match is None or match(str(path), match_pos): + yield from select_next(path, exists) + stack = [path] + while stack: + yield from select_recursive_step(stack, match_pos) + + def select_recursive_step(stack, match_pos): + path = stack.pop() + try: + # We must close the scandir() object before proceeding to + # avoid exhausting file descriptors when globbing deep trees. + with self.scandir(path) as scandir_it: + entries = list(scandir_it) + except OSError: + pass + else: + for entry in entries: + is_dir = False + try: + if entry.is_dir(follow_symlinks=follow_symlinks): + is_dir = True + except OSError: + pass + + if is_dir or not dir_only: + entry_path = self.parse_entry(entry) + if match is None or match(str(entry_path), match_pos): + if dir_only: + yield from select_next(entry_path, exists=True) + else: + # Optimization: directly yield the path if this is + # last pattern part. + yield entry_path + if is_dir: + stack.append(entry_path) + + return select_recursive + + def select_exists(self, path, exists=False): + """Yields the given path, if it exists. + """ + if exists: + # Optimization: this path is already known to exist, e.g. because + # it was returned from os.scandir(), so we skip calling lstat(). + yield path + else: + try: + self.lstat(path) + yield path + except OSError: + pass + + @classmethod + def walk(cls, root, top_down, on_error, follow_symlinks): + """Walk the directory tree from the given root, similar to os.walk(). + """ + paths = [root] + while paths: + path = paths.pop() + if isinstance(path, tuple): + yield path + continue + try: + with cls.scandir(path) as scandir_it: + dirnames = [] + filenames = [] + if not top_down: + paths.append((path, dirnames, filenames)) + for entry in scandir_it: + name = entry.name + try: + if entry.is_dir(follow_symlinks=follow_symlinks): + if not top_down: + paths.append(cls.parse_entry(entry)) + dirnames.append(name) + else: + filenames.append(name) + except OSError: + filenames.append(name) + except OSError as error: + if on_error is not None: + on_error(error) + else: + if top_down: + yield path, dirnames, filenames + if dirnames: + prefix = cls.add_slash(path) + paths += [cls.concat_path(prefix, d) for d in reversed(dirnames)] From 6d9e8e989e9e0931f84155451f921621b906e213 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 3 May 2024 17:07:47 -0500 Subject: [PATCH 195/217] Minor improvements to the itertools recipes (#118563) --- Doc/library/itertools.rst | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 9a5cb8be37d349..820906085151a3 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -826,10 +826,7 @@ and :term:`generators ` which incur interpreter overhead. return map(function, count(start)) def repeatfunc(func, times=None, *args): - """Repeat calls to func with specified arguments. - - Example: repeatfunc(random.random) - """ + "Repeat calls to func with specified arguments." if times is None: return starmap(func, repeat(args)) return starmap(func, repeat(args, times)) @@ -851,10 +848,8 @@ and :term:`generators ` which incur interpreter overhead. "Advance the iterator n-steps ahead. If n is None, consume entirely." # Use functions that consume iterators at C speed. if n is None: - # feed the entire iterator into a zero-length deque collections.deque(iterator, maxlen=0) else: - # advance to the empty slice starting at position n next(islice(iterator, n, n), None) def nth(iterable, n, default=None): @@ -873,7 +868,7 @@ and :term:`generators ` which incur interpreter overhead. def all_equal(iterable, key=None): "Returns True if all the elements are equal to each other." - # all_equal('4٤໔4৪', key=int) → True + # all_equal('4٤௪౪໔', key=int) → True return len(take(2, groupby(iterable, key))) <= 1 def unique_justseen(iterable, key=None): @@ -903,9 +898,9 @@ and :term:`generators ` which incur interpreter overhead. def sliding_window(iterable, n): "Collect data into overlapping fixed-length chunks or blocks." # sliding_window('ABCDEFG', 4) → ABCD BCDE CDEF DEFG - it = iter(iterable) - window = collections.deque(islice(it, n-1), maxlen=n) - for x in it: + iterator = iter(iterable) + window = collections.deque(islice(iterator, n - 1), maxlen=n) + for x in iterator: window.append(x) yield tuple(window) @@ -955,8 +950,8 @@ and :term:`generators ` which incur interpreter overhead. seq_index = getattr(iterable, 'index', None) if seq_index is None: # Path for general iterables - it = islice(iterable, start, stop) - for i, element in enumerate(it, start): + iterator = islice(iterable, start, stop) + for i, element in enumerate(iterator, start): if element is value or element == value: yield i else: @@ -971,10 +966,7 @@ and :term:`generators ` which incur interpreter overhead. pass def iter_except(func, exception, first=None): - """ Call a function repeatedly until an exception is raised. - - Converts a call-until-exception interface to an iterator interface. - """ + "Convert a call-until-exception interface to an iterator interface." # iter_except(d.popitem, KeyError) → non-blocking dictionary iterator try: if first is not None: @@ -1074,14 +1066,10 @@ The following recipes have a more mathematical flavor: # sieve(30) → 2 3 5 7 11 13 17 19 23 29 if n > 2: yield 2 - start = 3 data = bytearray((0, 1)) * (n // 2) - limit = math.isqrt(n) + 1 - for p in iter_index(data, 1, start, limit): - yield from iter_index(data, 1, start, p*p) + for p in iter_index(data, 1, start=3, stop=math.isqrt(n) + 1): data[p*p : n : p+p] = bytes(len(range(p*p, n, p+p))) - start = p*p - yield from iter_index(data, 1, start) + yield from iter_index(data, 1, start=3) def factor(n): "Prime factors of n." @@ -1101,8 +1089,8 @@ The following recipes have a more mathematical flavor: "Count of natural numbers up to n that are coprime to n." # https://mathworld.wolfram.com/TotientFunction.html # totient(12) → 4 because len([1, 5, 7, 11]) == 4 - for p in unique_justseen(factor(n)): - n -= n // p + for prime in set(factor(n)): + n -= n // prime return n From 0e78a545e6e78b16eb7709ec3db2243364603134 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 3 May 2024 18:13:40 -0400 Subject: [PATCH 196/217] gh-118534: Fix load of `gil->locked` (#118553) --- Python/ceval_gil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index e4f7217255efd8..b456a9d80455e4 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -224,7 +224,7 @@ drop_gil(PyInterpreterState *interp, PyThreadState *tstate) return; } #endif - if (!_Py_atomic_load_ptr_relaxed(&gil->locked)) { + if (!_Py_atomic_load_int_relaxed(&gil->locked)) { Py_FatalError("drop_gil: GIL is not locked"); } From 37c31bea72fb6971f1008faa2e253d12167c221f Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 3 May 2024 18:16:45 -0400 Subject: [PATCH 197/217] gh-118527: Intern filename, name, and qualname in code objects. (#118558) This interns the strings for `co_filename`, `co_name`, and `co_qualname` on codeobjects in the free-threaded build. This partially addresses a reference counting bottleneck when creating closures concurrently. The closures take the name and qualified name from the code object. --- Objects/codeobject.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 7d02b038cf52ca..15d3960fc2df51 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -390,6 +390,11 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_filename = Py_NewRef(con->filename); co->co_name = Py_NewRef(con->name); co->co_qualname = Py_NewRef(con->qualname); +#ifdef Py_GIL_DISABLED + PyUnicode_InternInPlace(&co->co_filename); + PyUnicode_InternInPlace(&co->co_name); + PyUnicode_InternInPlace(&co->co_qualname); +#endif co->co_flags = con->flags; co->co_firstlineno = con->firstlineno; From 139dc487b5ac37b0c2c4b93f2bfba194507d8310 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 3 May 2024 16:10:02 -0700 Subject: [PATCH 198/217] GH-118251: Fix incomplete ternary expression in JIT workflow (GH-118564) --- .github/workflows/jit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 014cced8e81b52..f68567c8a3153e 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -93,7 +93,7 @@ jobs: choco upgrade llvm -y choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }} ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '--pgo' }} -p ${{ matrix.architecture }} - ./PCbuild/rt.bat ${{ matrix.debug && '-d' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + ./PCbuild/rt.bat ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3 # No PGO or tests (yet): - name: Emulated Windows From 1b7e5e6e60e0d22b2a928cbbb36ebb989183450f Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 3 May 2024 16:41:07 -0700 Subject: [PATCH 199/217] GH-113464: Generate a more efficient JIT (GH-118512) --- Python/jit.c | 587 +++++++++++++++++++---------------------- Tools/jit/_stencils.py | 116 +++++++- Tools/jit/_writer.py | 107 +++----- 3 files changed, 418 insertions(+), 392 deletions(-) diff --git a/Python/jit.c b/Python/jit.c index df14e48c564447..7c316a410dda6a 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -16,8 +16,6 @@ #include "pycore_sliceobject.h" #include "pycore_jit.h" -#include "jit_stencils.h" - // Memory management stuff: //////////////////////////////////////////////////// #ifndef MS_WINDOWS @@ -146,256 +144,275 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, #define IS_AARCH64_LDR_OR_STR(I) (((I) & 0x3B000000) == 0x39000000) #define IS_AARCH64_MOV(I) (((I) & 0x9F800000) == 0x92800000) -// Fill all of stencil's holes in the memory pointed to by base, using the -// values in patches. -static void -patch(unsigned char *base, const Stencil *stencil, uintptr_t patches[]) +// LLD is a great reference for performing relocations... just keep in +// mind that Tools/jit/build.py does filtering and preprocessing for us! +// Here's a good place to start for each platform: +// - aarch64-apple-darwin: +// - https://github.com/llvm/llvm-project/blob/main/lld/MachO/Arch/ARM64.cpp +// - https://github.com/llvm/llvm-project/blob/main/lld/MachO/Arch/ARM64Common.cpp +// - https://github.com/llvm/llvm-project/blob/main/lld/MachO/Arch/ARM64Common.h +// - aarch64-pc-windows-msvc: +// - https://github.com/llvm/llvm-project/blob/main/lld/COFF/Chunks.cpp +// - aarch64-unknown-linux-gnu: +// - https://github.com/llvm/llvm-project/blob/main/lld/ELF/Arch/AArch64.cpp +// - i686-pc-windows-msvc: +// - https://github.com/llvm/llvm-project/blob/main/lld/COFF/Chunks.cpp +// - x86_64-apple-darwin: +// - https://github.com/llvm/llvm-project/blob/main/lld/MachO/Arch/X86_64.cpp +// - x86_64-pc-windows-msvc: +// - https://github.com/llvm/llvm-project/blob/main/lld/COFF/Chunks.cpp +// - x86_64-unknown-linux-gnu: +// - https://github.com/llvm/llvm-project/blob/main/lld/ELF/Arch/X86_64.cpp + +// Many of these patches are "relaxing", meaning that they can rewrite the +// code they're patching to be more efficient (like turning a 64-bit memory +// load into a 32-bit immediate load). These patches have an "x" in their name. +// Relative patches have an "r" in their name. + +// 32-bit absolute address. +void +patch_32(unsigned char *location, uint64_t value) { - for (size_t i = 0; i < stencil->holes_size; i++) { - const Hole *hole = &stencil->holes[i]; - unsigned char *location = base + hole->offset; - uint64_t value = patches[hole->value] + (uintptr_t)hole->symbol + hole->addend; - uint8_t *loc8 = (uint8_t *)location; - uint32_t *loc32 = (uint32_t *)location; - uint64_t *loc64 = (uint64_t *)location; - // LLD is a great reference for performing relocations... just keep in - // mind that Tools/jit/build.py does filtering and preprocessing for us! - // Here's a good place to start for each platform: - // - aarch64-apple-darwin: - // - https://github.com/llvm/llvm-project/blob/main/lld/MachO/Arch/ARM64.cpp - // - https://github.com/llvm/llvm-project/blob/main/lld/MachO/Arch/ARM64Common.cpp - // - https://github.com/llvm/llvm-project/blob/main/lld/MachO/Arch/ARM64Common.h - // - aarch64-pc-windows-msvc: - // - https://github.com/llvm/llvm-project/blob/main/lld/COFF/Chunks.cpp - // - aarch64-unknown-linux-gnu: - // - https://github.com/llvm/llvm-project/blob/main/lld/ELF/Arch/AArch64.cpp - // - i686-pc-windows-msvc: - // - https://github.com/llvm/llvm-project/blob/main/lld/COFF/Chunks.cpp - // - x86_64-apple-darwin: - // - https://github.com/llvm/llvm-project/blob/main/lld/MachO/Arch/X86_64.cpp - // - x86_64-pc-windows-msvc: - // - https://github.com/llvm/llvm-project/blob/main/lld/COFF/Chunks.cpp - // - x86_64-unknown-linux-gnu: - // - https://github.com/llvm/llvm-project/blob/main/lld/ELF/Arch/X86_64.cpp - switch (hole->kind) { - case HoleKind_IMAGE_REL_I386_DIR32: - // 32-bit absolute address. - // Check that we're not out of range of 32 unsigned bits: - assert(value < (1ULL << 32)); - *loc32 = (uint32_t)value; - continue; - case HoleKind_ARM64_RELOC_UNSIGNED: - case HoleKind_R_AARCH64_ABS64: - case HoleKind_X86_64_RELOC_UNSIGNED: - case HoleKind_R_X86_64_64: - // 64-bit absolute address. - *loc64 = value; - continue; - case HoleKind_IMAGE_REL_AMD64_REL32: - case HoleKind_IMAGE_REL_I386_REL32: - case HoleKind_R_X86_64_GOTPCRELX: - case HoleKind_R_X86_64_REX_GOTPCRELX: - case HoleKind_X86_64_RELOC_GOT: - case HoleKind_X86_64_RELOC_GOT_LOAD: { - // 32-bit relative address. - // Try to relax the GOT load into an immediate value: - uint64_t relaxed = *(uint64_t *)(value + 4) - 4; - if ((int64_t)relaxed - (int64_t)location >= -(1LL << 31) && - (int64_t)relaxed - (int64_t)location + 1 < (1LL << 31)) - { - if (loc8[-2] == 0x8B) { - // mov reg, dword ptr [rip + AAA] -> lea reg, [rip + XXX] - loc8[-2] = 0x8D; - value = relaxed; - } - else if (loc8[-2] == 0xFF && loc8[-1] == 0x15) { - // call qword ptr [rip + AAA] -> nop; call XXX - loc8[-2] = 0x90; - loc8[-1] = 0xE8; - value = relaxed; - } - else if (loc8[-2] == 0xFF && loc8[-1] == 0x25) { - // jmp qword ptr [rip + AAA] -> nop; jmp XXX - loc8[-2] = 0x90; - loc8[-1] = 0xE9; - value = relaxed; - } - } - } - // Fall through... - case HoleKind_R_X86_64_GOTPCREL: - case HoleKind_R_X86_64_PC32: - case HoleKind_X86_64_RELOC_SIGNED: - case HoleKind_X86_64_RELOC_BRANCH: - // 32-bit relative address. - value -= (uintptr_t)location; - // Check that we're not out of range of 32 signed bits: - assert((int64_t)value >= -(1LL << 31)); - assert((int64_t)value < (1LL << 31)); - *loc32 = (uint32_t)value; - continue; - case HoleKind_ARM64_RELOC_BRANCH26: - case HoleKind_IMAGE_REL_ARM64_BRANCH26: - case HoleKind_R_AARCH64_CALL26: - case HoleKind_R_AARCH64_JUMP26: - // 28-bit relative branch. - assert(IS_AARCH64_BRANCH(*loc32)); - value -= (uintptr_t)location; - // Check that we're not out of range of 28 signed bits: - assert((int64_t)value >= -(1 << 27)); - assert((int64_t)value < (1 << 27)); - // Since instructions are 4-byte aligned, only use 26 bits: - assert(get_bits(value, 0, 2) == 0); - set_bits(loc32, 0, value, 2, 26); - continue; - case HoleKind_R_AARCH64_MOVW_UABS_G0_NC: - // 16-bit low part of an absolute address. - assert(IS_AARCH64_MOV(*loc32)); - // Check the implicit shift (this is "part 0 of 3"): - assert(get_bits(*loc32, 21, 2) == 0); - set_bits(loc32, 5, value, 0, 16); - continue; - case HoleKind_R_AARCH64_MOVW_UABS_G1_NC: - // 16-bit middle-low part of an absolute address. - assert(IS_AARCH64_MOV(*loc32)); - // Check the implicit shift (this is "part 1 of 3"): - assert(get_bits(*loc32, 21, 2) == 1); - set_bits(loc32, 5, value, 16, 16); - continue; - case HoleKind_R_AARCH64_MOVW_UABS_G2_NC: - // 16-bit middle-high part of an absolute address. - assert(IS_AARCH64_MOV(*loc32)); - // Check the implicit shift (this is "part 2 of 3"): - assert(get_bits(*loc32, 21, 2) == 2); - set_bits(loc32, 5, value, 32, 16); - continue; - case HoleKind_R_AARCH64_MOVW_UABS_G3: - // 16-bit high part of an absolute address. - assert(IS_AARCH64_MOV(*loc32)); - // Check the implicit shift (this is "part 3 of 3"): - assert(get_bits(*loc32, 21, 2) == 3); - set_bits(loc32, 5, value, 48, 16); - continue; - case HoleKind_ARM64_RELOC_GOT_LOAD_PAGE21: - case HoleKind_IMAGE_REL_ARM64_PAGEBASE_REL21: - case HoleKind_R_AARCH64_ADR_GOT_PAGE: - case HoleKind_R_AARCH64_ADR_PREL_PG_HI21: - // 21-bit count of pages between this page and an absolute address's - // page... I know, I know, it's weird. Pairs nicely with - // ARM64_RELOC_GOT_LOAD_PAGEOFF12 (below). - assert(IS_AARCH64_ADRP(*loc32)); - // Try to relax the pair of GOT loads into an immediate value: - const Hole *next_hole = &stencil->holes[i + 1]; - if (i + 1 < stencil->holes_size && - (next_hole->kind == HoleKind_ARM64_RELOC_GOT_LOAD_PAGEOFF12 || - next_hole->kind == HoleKind_IMAGE_REL_ARM64_PAGEOFFSET_12L || - next_hole->kind == HoleKind_R_AARCH64_LD64_GOT_LO12_NC) && - next_hole->offset == hole->offset + 4 && - next_hole->symbol == hole->symbol && - next_hole->addend == hole->addend && - next_hole->value == hole->value) - { - unsigned char reg = get_bits(loc32[0], 0, 5); - assert(IS_AARCH64_LDR_OR_STR(loc32[1])); - // There should be only one register involved: - assert(reg == get_bits(loc32[1], 0, 5)); // ldr's output register. - assert(reg == get_bits(loc32[1], 5, 5)); // ldr's input register. - uint64_t relaxed = *(uint64_t *)value; - if (relaxed < (1UL << 16)) { - // adrp reg, AAA; ldr reg, [reg + BBB] -> movz reg, XXX; nop - loc32[0] = 0xD2800000 | (get_bits(relaxed, 0, 16) << 5) | reg; - loc32[1] = 0xD503201F; - i++; - continue; - } - if (relaxed < (1ULL << 32)) { - // adrp reg, AAA; ldr reg, [reg + BBB] -> movz reg, XXX; movk reg, YYY - loc32[0] = 0xD2800000 | (get_bits(relaxed, 0, 16) << 5) | reg; - loc32[1] = 0xF2A00000 | (get_bits(relaxed, 16, 16) << 5) | reg; - i++; - continue; - } - relaxed = value - (uintptr_t)location; - if ((relaxed & 0x3) == 0 && - (int64_t)relaxed >= -(1L << 19) && - (int64_t)relaxed < (1L << 19)) - { - // adrp reg, AAA; ldr reg, [reg + BBB] -> ldr reg, XXX; nop - loc32[0] = 0x58000000 | (get_bits(relaxed, 2, 19) << 5) | reg; - loc32[1] = 0xD503201F; - i++; - continue; - } - } - // Fall through... - case HoleKind_ARM64_RELOC_PAGE21: - // Number of pages between this page and the value's page: - value = (value >> 12) - ((uintptr_t)location >> 12); - // Check that we're not out of range of 21 signed bits: - assert((int64_t)value >= -(1 << 20)); - assert((int64_t)value < (1 << 20)); - // value[0:2] goes in loc[29:31]: - set_bits(loc32, 29, value, 0, 2); - // value[2:21] goes in loc[5:26]: - set_bits(loc32, 5, value, 2, 19); - continue; - case HoleKind_ARM64_RELOC_GOT_LOAD_PAGEOFF12: - case HoleKind_ARM64_RELOC_PAGEOFF12: - case HoleKind_IMAGE_REL_ARM64_PAGEOFFSET_12A: - case HoleKind_IMAGE_REL_ARM64_PAGEOFFSET_12L: - case HoleKind_R_AARCH64_ADD_ABS_LO12_NC: - case HoleKind_R_AARCH64_LD64_GOT_LO12_NC: - // 12-bit low part of an absolute address. Pairs nicely with - // ARM64_RELOC_GOT_LOAD_PAGE21 (above). - assert(IS_AARCH64_LDR_OR_STR(*loc32) || IS_AARCH64_ADD_OR_SUB(*loc32)); - // There might be an implicit shift encoded in the instruction: - uint8_t shift = 0; - if (IS_AARCH64_LDR_OR_STR(*loc32)) { - shift = (uint8_t)get_bits(*loc32, 30, 2); - // If both of these are set, the shift is supposed to be 4. - // That's pretty weird, and it's never actually been observed... - assert(get_bits(*loc32, 23, 1) == 0 || get_bits(*loc32, 26, 1) == 0); - } - value = get_bits(value, 0, 12); - assert(get_bits(value, 0, shift) == 0); - set_bits(loc32, 10, value, shift, 12); - continue; - } - Py_UNREACHABLE(); + uint32_t *loc32 = (uint32_t *)location; + // Check that we're not out of range of 32 unsigned bits: + assert(value < (1ULL << 32)); + *loc32 = (uint32_t)value; +} + +// 32-bit relative address. +void +patch_32r(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + value -= (uintptr_t)location; + // Check that we're not out of range of 32 signed bits: + assert((int64_t)value >= -(1LL << 31)); + assert((int64_t)value < (1LL << 31)); + *loc32 = (uint32_t)value; +} + +// 64-bit absolute address. +void +patch_64(unsigned char *location, uint64_t value) +{ + uint64_t *loc64 = (uint64_t *)location; + *loc64 = value; +} + +// 12-bit low part of an absolute address. Pairs nicely with patch_aarch64_21r +// (below). +void +patch_aarch64_12(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + assert(IS_AARCH64_LDR_OR_STR(*loc32) || IS_AARCH64_ADD_OR_SUB(*loc32)); + // There might be an implicit shift encoded in the instruction: + uint8_t shift = 0; + if (IS_AARCH64_LDR_OR_STR(*loc32)) { + shift = (uint8_t)get_bits(*loc32, 30, 2); + // If both of these are set, the shift is supposed to be 4. + // That's pretty weird, and it's never actually been observed... + assert(get_bits(*loc32, 23, 1) == 0 || get_bits(*loc32, 26, 1) == 0); } + value = get_bits(value, 0, 12); + assert(get_bits(value, 0, shift) == 0); + set_bits(loc32, 10, value, shift, 12); } -static void -copy_and_patch(unsigned char *base, const Stencil *stencil, uintptr_t patches[]) +// Relaxable 12-bit low part of an absolute address. Pairs nicely with +// patch_aarch64_21rx (below). +void +patch_aarch64_12x(unsigned char *location, uint64_t value) { - memcpy(base, stencil->body, stencil->body_size); - patch(base, stencil, patches); + // This can *only* be relaxed if it occurs immediately before a matching + // patch_aarch64_21rx. If that happens, the JIT build step will replace both + // calls with a single call to patch_aarch64_33rx. Otherwise, we end up + // here, and the instruction is patched normally: + patch_aarch64_12(location, value); } -static void -emit(const StencilGroup *group, uintptr_t patches[]) +// 16-bit low part of an absolute address. +void +patch_aarch64_16a(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + assert(IS_AARCH64_MOV(*loc32)); + // Check the implicit shift (this is "part 0 of 3"): + assert(get_bits(*loc32, 21, 2) == 0); + set_bits(loc32, 5, value, 0, 16); +} + +// 16-bit middle-low part of an absolute address. +void +patch_aarch64_16b(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + assert(IS_AARCH64_MOV(*loc32)); + // Check the implicit shift (this is "part 1 of 3"): + assert(get_bits(*loc32, 21, 2) == 1); + set_bits(loc32, 5, value, 16, 16); +} + +// 16-bit middle-high part of an absolute address. +void +patch_aarch64_16c(unsigned char *location, uint64_t value) { - copy_and_patch((unsigned char *)patches[HoleValue_DATA], &group->data, patches); - copy_and_patch((unsigned char *)patches[HoleValue_CODE], &group->code, patches); + uint32_t *loc32 = (uint32_t *)location; + assert(IS_AARCH64_MOV(*loc32)); + // Check the implicit shift (this is "part 2 of 3"): + assert(get_bits(*loc32, 21, 2) == 2); + set_bits(loc32, 5, value, 32, 16); } +// 16-bit high part of an absolute address. +void +patch_aarch64_16d(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + assert(IS_AARCH64_MOV(*loc32)); + // Check the implicit shift (this is "part 3 of 3"): + assert(get_bits(*loc32, 21, 2) == 3); + set_bits(loc32, 5, value, 48, 16); +} + +// 21-bit count of pages between this page and an absolute address's page... I +// know, I know, it's weird. Pairs nicely with patch_aarch64_12 (above). +void +patch_aarch64_21r(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + value = (value >> 12) - ((uintptr_t)location >> 12); + // Check that we're not out of range of 21 signed bits: + assert((int64_t)value >= -(1 << 20)); + assert((int64_t)value < (1 << 20)); + // value[0:2] goes in loc[29:31]: + set_bits(loc32, 29, value, 0, 2); + // value[2:21] goes in loc[5:26]: + set_bits(loc32, 5, value, 2, 19); +} + +// Relaxable 21-bit count of pages between this page and an absolute address's +// page. Pairs nicely with patch_aarch64_12x (above). +void +patch_aarch64_21rx(unsigned char *location, uint64_t value) +{ + // This can *only* be relaxed if it occurs immediately before a matching + // patch_aarch64_12x. If that happens, the JIT build step will replace both + // calls with a single call to patch_aarch64_33rx. Otherwise, we end up + // here, and the instruction is patched normally: + patch_aarch64_21r(location, value); +} + +// 28-bit relative branch. +void +patch_aarch64_26r(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + assert(IS_AARCH64_BRANCH(*loc32)); + value -= (uintptr_t)location; + // Check that we're not out of range of 28 signed bits: + assert((int64_t)value >= -(1 << 27)); + assert((int64_t)value < (1 << 27)); + // Since instructions are 4-byte aligned, only use 26 bits: + assert(get_bits(value, 0, 2) == 0); + set_bits(loc32, 0, value, 2, 26); +} + +// A pair of patch_aarch64_21rx and patch_aarch64_12x. +void +patch_aarch64_33rx(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + // Try to relax the pair of GOT loads into an immediate value: + assert(IS_AARCH64_ADRP(*loc32)); + unsigned char reg = get_bits(loc32[0], 0, 5); + assert(IS_AARCH64_LDR_OR_STR(loc32[1])); + // There should be only one register involved: + assert(reg == get_bits(loc32[1], 0, 5)); // ldr's output register. + assert(reg == get_bits(loc32[1], 5, 5)); // ldr's input register. + uint64_t relaxed = *(uint64_t *)value; + if (relaxed < (1UL << 16)) { + // adrp reg, AAA; ldr reg, [reg + BBB] -> movz reg, XXX; nop + loc32[0] = 0xD2800000 | (get_bits(relaxed, 0, 16) << 5) | reg; + loc32[1] = 0xD503201F; + return; + } + if (relaxed < (1ULL << 32)) { + // adrp reg, AAA; ldr reg, [reg + BBB] -> movz reg, XXX; movk reg, YYY + loc32[0] = 0xD2800000 | (get_bits(relaxed, 0, 16) << 5) | reg; + loc32[1] = 0xF2A00000 | (get_bits(relaxed, 16, 16) << 5) | reg; + return; + } + relaxed = value - (uintptr_t)location; + if ((relaxed & 0x3) == 0 && + (int64_t)relaxed >= -(1L << 19) && + (int64_t)relaxed < (1L << 19)) + { + // adrp reg, AAA; ldr reg, [reg + BBB] -> ldr reg, XXX; nop + loc32[0] = 0x58000000 | (get_bits(relaxed, 2, 19) << 5) | reg; + loc32[1] = 0xD503201F; + return; + } + // Couldn't do it. Just patch the two instructions normally: + patch_aarch64_21rx(location, value); + patch_aarch64_12x(location + 4, value); +} + +// Relaxable 32-bit relative address. +void +patch_x86_64_32rx(unsigned char *location, uint64_t value) +{ + uint8_t *loc8 = (uint8_t *)location; + // Try to relax the GOT load into an immediate value: + uint64_t relaxed = *(uint64_t *)(value + 4) - 4; + if ((int64_t)relaxed - (int64_t)location >= -(1LL << 31) && + (int64_t)relaxed - (int64_t)location + 1 < (1LL << 31)) + { + if (loc8[-2] == 0x8B) { + // mov reg, dword ptr [rip + AAA] -> lea reg, [rip + XXX] + loc8[-2] = 0x8D; + value = relaxed; + } + else if (loc8[-2] == 0xFF && loc8[-1] == 0x15) { + // call qword ptr [rip + AAA] -> nop; call XXX + loc8[-2] = 0x90; + loc8[-1] = 0xE8; + value = relaxed; + } + else if (loc8[-2] == 0xFF && loc8[-1] == 0x25) { + // jmp qword ptr [rip + AAA] -> nop; jmp XXX + loc8[-2] = 0x90; + loc8[-1] = 0xE9; + value = relaxed; + } + } + patch_32r(location, value); +} + +#include "jit_stencils.h" + // Compiles executor in-place. Don't forget to call _PyJIT_Free later! int -_PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size_t length) +_PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], size_t length) { + const StencilGroup *group; // Loop once to find the total compiled size: - size_t instruction_starts[UOP_MAX_TRACE_LENGTH]; - size_t code_size = trampoline.code.body_size; - size_t data_size = trampoline.data.body_size; + uintptr_t instruction_starts[UOP_MAX_TRACE_LENGTH]; + size_t code_size = 0; + size_t data_size = 0; + group = &trampoline; + code_size += group->code_size; + data_size += group->data_size; for (size_t i = 0; i < length; i++) { - _PyUOpInstruction *instruction = (_PyUOpInstruction *)&trace[i]; - const StencilGroup *group = &stencil_groups[instruction->opcode]; + const _PyUOpInstruction *instruction = &trace[i]; + group = &stencil_groups[instruction->opcode]; instruction_starts[i] = code_size; - code_size += group->code.body_size; - data_size += group->data.body_size; + code_size += group->code_size; + data_size += group->data_size; } - code_size += stencil_groups[_FATAL_ERROR].code.body_size; - data_size += stencil_groups[_FATAL_ERROR].data.body_size; + group = &stencil_groups[_FATAL_ERROR]; + code_size += group->code_size; + data_size += group->data_size; // Round up to the nearest page: size_t page_size = get_page_size(); assert((page_size & (page_size - 1)) == 0); @@ -405,87 +422,35 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size if (memory == NULL) { return -1; } + // Update the offsets of each instruction: + for (size_t i = 0; i < length; i++) { + instruction_starts[i] += (uintptr_t)memory; + } // Loop again to emit the code: unsigned char *code = memory; unsigned char *data = memory + code_size; - { - // Compile the trampoline, which handles converting between the native - // calling convention and the calling convention used by jitted code - // (which may be different for efficiency reasons). On platforms where - // we don't change calling conventions, the trampoline is empty and - // nothing is emitted here: - const StencilGroup *group = &trampoline; - // Think of patches as a dictionary mapping HoleValue to uintptr_t: - uintptr_t patches[] = GET_PATCHES(); - patches[HoleValue_CODE] = (uintptr_t)code; - patches[HoleValue_CONTINUE] = (uintptr_t)code + group->code.body_size; - patches[HoleValue_DATA] = (uintptr_t)data; - patches[HoleValue_EXECUTOR] = (uintptr_t)executor; - patches[HoleValue_TOP] = (uintptr_t)memory + trampoline.code.body_size; - patches[HoleValue_ZERO] = 0; - emit(group, patches); - code += group->code.body_size; - data += group->data.body_size; - } + // Compile the trampoline, which handles converting between the native + // calling convention and the calling convention used by jitted code + // (which may be different for efficiency reasons). On platforms where + // we don't change calling conventions, the trampoline is empty and + // nothing is emitted here: + group = &trampoline; + group->emit(code, data, executor, NULL, instruction_starts); + code += group->code_size; + data += group->data_size; assert(trace[0].opcode == _START_EXECUTOR || trace[0].opcode == _COLD_EXIT); for (size_t i = 0; i < length; i++) { - _PyUOpInstruction *instruction = (_PyUOpInstruction *)&trace[i]; - const StencilGroup *group = &stencil_groups[instruction->opcode]; - uintptr_t patches[] = GET_PATCHES(); - patches[HoleValue_CODE] = (uintptr_t)code; - patches[HoleValue_CONTINUE] = (uintptr_t)code + group->code.body_size; - patches[HoleValue_DATA] = (uintptr_t)data; - patches[HoleValue_EXECUTOR] = (uintptr_t)executor; - patches[HoleValue_OPARG] = instruction->oparg; - #if SIZEOF_VOID_P == 8 - patches[HoleValue_OPERAND] = instruction->operand; - #else - assert(SIZEOF_VOID_P == 4); - patches[HoleValue_OPERAND_HI] = instruction->operand >> 32; - patches[HoleValue_OPERAND_LO] = instruction->operand & UINT32_MAX; - #endif - switch (instruction->format) { - case UOP_FORMAT_TARGET: - patches[HoleValue_TARGET] = instruction->target; - break; - case UOP_FORMAT_EXIT: - assert(instruction->exit_index < executor->exit_count); - patches[HoleValue_EXIT_INDEX] = instruction->exit_index; - if (instruction->error_target < length) { - patches[HoleValue_ERROR_TARGET] = (uintptr_t)memory + instruction_starts[instruction->error_target]; - } - break; - case UOP_FORMAT_JUMP: - assert(instruction->jump_target < length); - patches[HoleValue_JUMP_TARGET] = (uintptr_t)memory + instruction_starts[instruction->jump_target]; - if (instruction->error_target < length) { - patches[HoleValue_ERROR_TARGET] = (uintptr_t)memory + instruction_starts[instruction->error_target]; - } - break; - default: - assert(0); - Py_FatalError("Illegal instruction format"); - } - patches[HoleValue_TOP] = (uintptr_t)memory + instruction_starts[1]; - patches[HoleValue_ZERO] = 0; - emit(group, patches); - code += group->code.body_size; - data += group->data.body_size; - } - { - // Protect against accidental buffer overrun into data: - const StencilGroup *group = &stencil_groups[_FATAL_ERROR]; - uintptr_t patches[] = GET_PATCHES(); - patches[HoleValue_CODE] = (uintptr_t)code; - patches[HoleValue_CONTINUE] = (uintptr_t)code; - patches[HoleValue_DATA] = (uintptr_t)data; - patches[HoleValue_EXECUTOR] = (uintptr_t)executor; - patches[HoleValue_TOP] = (uintptr_t)code; - patches[HoleValue_ZERO] = 0; - emit(group, patches); - code += group->code.body_size; - data += group->data.body_size; + const _PyUOpInstruction *instruction = &trace[i]; + group = &stencil_groups[instruction->opcode]; + group->emit(code, data, executor, instruction, instruction_starts); + code += group->code_size; + data += group->data_size; } + // Protect against accidental buffer overrun into data: + group = &stencil_groups[_FATAL_ERROR]; + group->emit(code, data, executor, NULL, instruction_starts); + code += group->code_size; + data += group->data_size; assert(code == memory + code_size); assert(data == memory + code_size + data_size); if (mark_executable(memory, total_size)) { @@ -493,7 +458,7 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size return -1; } executor->jit_code = memory; - executor->jit_side_entry = memory + trampoline.code.body_size; + executor->jit_side_entry = memory + trampoline.code_size; executor->jit_size = total_size; return 0; } diff --git a/Tools/jit/_stencils.py b/Tools/jit/_stencils.py index 9feceb45388d05..6e046df3026ae9 100644 --- a/Tools/jit/_stencils.py +++ b/Tools/jit/_stencils.py @@ -3,6 +3,7 @@ import dataclasses import enum import sys +import typing import _schema @@ -47,6 +48,73 @@ class HoleValue(enum.Enum): ZERO = enum.auto() +# Map relocation types to our JIT's patch functions. "r" suffixes indicate that +# the patch function is relative. "x" suffixes indicate that they are "relaxing" +# (see comments in jit.c for more info): +_PATCH_FUNCS = { + # aarch64-apple-darwin: + "ARM64_RELOC_BRANCH26": "patch_aarch64_26r", + "ARM64_RELOC_GOT_LOAD_PAGE21": "patch_aarch64_21rx", + "ARM64_RELOC_GOT_LOAD_PAGEOFF12": "patch_aarch64_12x", + "ARM64_RELOC_PAGE21": "patch_aarch64_21r", + "ARM64_RELOC_PAGEOFF12": "patch_aarch64_12", + "ARM64_RELOC_UNSIGNED": "patch_64", + # x86_64-pc-windows-msvc: + "IMAGE_REL_AMD64_REL32": "patch_x86_64_32rx", + # aarch64-pc-windows-msvc: + "IMAGE_REL_ARM64_BRANCH26": "patch_aarch64_26r", + "IMAGE_REL_ARM64_PAGEBASE_REL21": "patch_aarch64_21rx", + "IMAGE_REL_ARM64_PAGEOFFSET_12A": "patch_aarch64_12", + "IMAGE_REL_ARM64_PAGEOFFSET_12L": "patch_aarch64_12x", + # i686-pc-windows-msvc: + "IMAGE_REL_I386_DIR32": "patch_32", + "IMAGE_REL_I386_REL32": "patch_x86_64_32rx", + # aarch64-unknown-linux-gnu: + "R_AARCH64_ABS64": "patch_64", + "R_AARCH64_ADD_ABS_LO12_NC": "patch_aarch64_12", + "R_AARCH64_ADR_GOT_PAGE": "patch_aarch64_21rx", + "R_AARCH64_ADR_PREL_PG_HI21": "patch_aarch64_21r", + "R_AARCH64_CALL26": "patch_aarch64_26r", + "R_AARCH64_JUMP26": "patch_aarch64_26r", + "R_AARCH64_LD64_GOT_LO12_NC": "patch_aarch64_12x", + "R_AARCH64_MOVW_UABS_G0_NC": "patch_aarch64_16a", + "R_AARCH64_MOVW_UABS_G1_NC": "patch_aarch64_16b", + "R_AARCH64_MOVW_UABS_G2_NC": "patch_aarch64_16c", + "R_AARCH64_MOVW_UABS_G3": "patch_aarch64_16d", + # x86_64-unknown-linux-gnu: + "R_X86_64_64": "patch_64", + "R_X86_64_GOTPCREL": "patch_32r", + "R_X86_64_GOTPCRELX": "patch_x86_64_32rx", + "R_X86_64_PC32": "patch_32r", + "R_X86_64_REX_GOTPCRELX": "patch_x86_64_32rx", + # x86_64-apple-darwin: + "X86_64_RELOC_BRANCH": "patch_32r", + "X86_64_RELOC_GOT": "patch_x86_64_32rx", + "X86_64_RELOC_GOT_LOAD": "patch_x86_64_32rx", + "X86_64_RELOC_SIGNED": "patch_32r", + "X86_64_RELOC_UNSIGNED": "patch_64", +} +# Translate HoleValues to C expressions: +_HOLE_EXPRS = { + HoleValue.CODE: "(uintptr_t)code", + HoleValue.CONTINUE: "(uintptr_t)code + sizeof(code_body)", + HoleValue.DATA: "(uintptr_t)data", + HoleValue.EXECUTOR: "(uintptr_t)executor", + # These should all have been turned into DATA values by process_relocations: + # HoleValue.GOT: "", + HoleValue.OPARG: "instruction->oparg", + HoleValue.OPERAND: "instruction->operand", + HoleValue.OPERAND_HI: "(instruction->operand >> 32)", + HoleValue.OPERAND_LO: "(instruction->operand & UINT32_MAX)", + HoleValue.TARGET: "instruction->target", + HoleValue.JUMP_TARGET: "instruction_starts[instruction->jump_target]", + HoleValue.ERROR_TARGET: "instruction_starts[instruction->error_target]", + HoleValue.EXIT_INDEX: "instruction->exit_index", + HoleValue.TOP: "instruction_starts[1]", + HoleValue.ZERO: "", +} + + @dataclasses.dataclass class Hole: """ @@ -63,19 +131,43 @@ class Hole: symbol: str | None # ...plus this addend: addend: int + func: str = dataclasses.field(init=False) # Convenience method: replace = dataclasses.replace - def as_c(self) -> str: - """Dump this hole as an initialization of a C Hole struct.""" - parts = [ - f"{self.offset:#x}", - f"HoleKind_{self.kind}", - f"HoleValue_{self.value.name}", - f"&{self.symbol}" if self.symbol else "NULL", - f"{_signed(self.addend):#x}", - ] - return f"{{{', '.join(parts)}}}" + def __post_init__(self) -> None: + self.func = _PATCH_FUNCS[self.kind] + + def fold(self, other: typing.Self) -> typing.Self | None: + """Combine two holes into a single hole, if possible.""" + if ( + self.offset + 4 == other.offset + and self.value == other.value + and self.symbol == other.symbol + and self.addend == other.addend + and self.func == "patch_aarch64_21rx" + and other.func == "patch_aarch64_12x" + ): + # These can *only* be properly relaxed when they appear together and + # patch the same value: + folded = self.replace() + folded.func = "patch_aarch64_33rx" + return folded + return None + + def as_c(self, where: str) -> str: + """Dump this hole as a call to a patch_* function.""" + location = f"{where} + {self.offset:#x}" + value = _HOLE_EXPRS[self.value] + if self.symbol: + if value: + value += " + " + value += f"(uintptr_t)&{self.symbol}" + if _signed(self.addend): + if value: + value += " + " + value += f"{_signed(self.addend):#x}" + return f"{self.func}({location}, {value});" @dataclasses.dataclass @@ -265,6 +357,10 @@ def _emit_global_offset_table(self) -> None: ) self.data.body.extend([0] * 8) + def as_c(self, opname: str) -> str: + """Dump this hole as a StencilGroup initializer.""" + return f"{{emit_{opname}, {len(self.code.body)}, {len(self.data.body)}}}" + def symbol_to_value(symbol: str) -> tuple[HoleValue, str | None]: """ diff --git a/Tools/jit/_writer.py b/Tools/jit/_writer.py index ccd67850c37787..9d11094f85c7ff 100644 --- a/Tools/jit/_writer.py +++ b/Tools/jit/_writer.py @@ -1,100 +1,65 @@ """Utilities for writing StencilGroups out to a C header file.""" +import itertools import typing -import _schema import _stencils -def _dump_header() -> typing.Iterator[str]: - yield "typedef enum {" - for kind in typing.get_args(_schema.HoleKind): - yield f" HoleKind_{kind}," - yield "} HoleKind;" - yield "" - yield "typedef enum {" - for value in _stencils.HoleValue: - yield f" HoleValue_{value.name}," - yield "} HoleValue;" - yield "" - yield "typedef struct {" - yield " const size_t offset;" - yield " const HoleKind kind;" - yield " const HoleValue value;" - yield " const void *symbol;" - yield " const uint64_t addend;" - yield "} Hole;" - yield "" +def _dump_footer(groups: dict[str, _stencils.StencilGroup]) -> typing.Iterator[str]: yield "typedef struct {" - yield " const size_t body_size;" - yield " const unsigned char * const body;" - yield " const size_t holes_size;" - yield " const Hole * const holes;" - yield "} Stencil;" - yield "" - yield "typedef struct {" - yield " const Stencil code;" - yield " const Stencil data;" + yield " void (*emit)(" + yield " unsigned char *code, unsigned char *data, _PyExecutorObject *executor," + yield " const _PyUOpInstruction *instruction, uintptr_t instruction_starts[]);" + yield " size_t code_size;" + yield " size_t data_size;" yield "} StencilGroup;" yield "" - - -def _dump_footer(opnames: typing.Iterable[str]) -> typing.Iterator[str]: - yield "#define INIT_STENCIL(STENCIL) { \\" - yield " .body_size = Py_ARRAY_LENGTH(STENCIL##_body) - 1, \\" - yield " .body = STENCIL##_body, \\" - yield " .holes_size = Py_ARRAY_LENGTH(STENCIL##_holes) - 1, \\" - yield " .holes = STENCIL##_holes, \\" - yield "}" - yield "" - yield "#define INIT_STENCIL_GROUP(OP) { \\" - yield " .code = INIT_STENCIL(OP##_code), \\" - yield " .data = INIT_STENCIL(OP##_data), \\" - yield "}" + yield f"static const StencilGroup trampoline = {groups['trampoline'].as_c('trampoline')};" yield "" - yield "static const StencilGroup stencil_groups[512] = {" - for opname in opnames: + yield "static const StencilGroup stencil_groups[MAX_UOP_ID + 1] = {" + for opname, group in sorted(groups.items()): if opname == "trampoline": continue - yield f" [{opname}] = INIT_STENCIL_GROUP({opname})," + yield f" [{opname}] = {group.as_c(opname)}," yield "};" - yield "" - yield "static const StencilGroup trampoline = INIT_STENCIL_GROUP(trampoline);" - yield "" - yield "#define GET_PATCHES() { \\" - for value in _stencils.HoleValue: - yield f" [HoleValue_{value.name}] = (uintptr_t)0xBADBADBADBADBADB, \\" - yield "}" def _dump_stencil(opname: str, group: _stencils.StencilGroup) -> typing.Iterator[str]: - yield f"// {opname}" + yield "void" + yield f"emit_{opname}(" + yield " unsigned char *code, unsigned char *data, _PyExecutorObject *executor," + yield " const _PyUOpInstruction *instruction, uintptr_t instruction_starts[])" + yield "{" for part, stencil in [("code", group.code), ("data", group.data)]: for line in stencil.disassembly: - yield f"// {line}" + yield f" // {line}" if stencil.body: - size = len(stencil.body) + 1 - yield f"static const unsigned char {opname}_{part}_body[{size}] = {{" + yield f" const unsigned char {part}_body[{len(stencil.body)}] = {{" for i in range(0, len(stencil.body), 8): row = " ".join(f"{byte:#04x}," for byte in stencil.body[i : i + 8]) - yield f" {row}" - yield "};" - else: - yield f"static const unsigned char {opname}_{part}_body[1];" - if stencil.holes: - size = len(stencil.holes) + 1 - yield f"static const Hole {opname}_{part}_holes[{size}] = {{" - for hole in stencil.holes: - yield f" {hole.as_c()}," - yield "};" - else: - yield f"static const Hole {opname}_{part}_holes[1];" + yield f" {row}" + yield " };" + # Data is written first (so relaxations in the code work properly): + for part, stencil in [("data", group.data), ("code", group.code)]: + if stencil.body: + yield f" memcpy({part}, {part}_body, sizeof({part}_body));" + skip = False + stencil.holes.sort(key=lambda hole: hole.offset) + for hole, pair in itertools.zip_longest(stencil.holes, stencil.holes[1:]): + if skip: + skip = False + continue + if pair and (folded := hole.fold(pair)): + skip = True + hole = folded + yield f" {hole.as_c(part)}" + yield "}" yield "" def dump(groups: dict[str, _stencils.StencilGroup]) -> typing.Iterator[str]: """Yield a JIT compiler line-by-line as a C header file.""" - yield from _dump_header() - for opname, group in groups.items(): + for opname, group in sorted(groups.items()): yield from _dump_stencil(opname, group) yield from _dump_footer(groups) From 42dc5b4ace39a3983cd9853719527f4724693adc Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 3 May 2024 23:13:36 -0500 Subject: [PATCH 200/217] gh-115532 Add kde_random() to the statistic module (#118210) --- Doc/library/statistics.rst | 84 +++++++++-------------------- Doc/whatsnew/3.13.rst | 3 +- Lib/statistics.py | 103 ++++++++++++++++++++++++++++++++++-- Lib/test/test_statistics.py | 80 ++++++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 63 deletions(-) diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst index cc72396964342e..d5a316e45ee3e2 100644 --- a/Doc/library/statistics.rst +++ b/Doc/library/statistics.rst @@ -77,6 +77,7 @@ or sample. :func:`geometric_mean` Geometric mean of data. :func:`harmonic_mean` Harmonic mean of data. :func:`kde` Estimate the probability density distribution of the data. +:func:`kde_random` Random sampling from the PDF generated by kde(). :func:`median` Median (middle value) of data. :func:`median_low` Low median of data. :func:`median_high` High median of data. @@ -311,6 +312,30 @@ However, for reading convenience, most of the examples show sorted sequences. .. versionadded:: 3.13 +.. function:: kde_random(data, h, kernel='normal', *, seed=None) + + Return a function that makes a random selection from the estimated + probability density function produced by ``kde(data, h, kernel)``. + + Providing a *seed* allows reproducible selections. In the future, the + values may change slightly as more accurate kernel inverse CDF estimates + are implemented. The seed may be an integer, float, str, or bytes. + + A :exc:`StatisticsError` will be raised if the *data* sequence is empty. + + Continuing the example for :func:`kde`, we can use + :func:`kde_random` to generate new random selections from an + estimated probability density function: + + >>> data = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2] + >>> rand = kde_random(data, h=1.5, seed=8675309) + >>> new_selections = [rand() for i in range(10)] + >>> [round(x, 1) for x in new_selections] + [0.7, 6.2, 1.2, 6.9, 7.0, 1.8, 2.5, -0.5, -1.8, 5.6] + + .. versionadded:: 3.13 + + .. function:: median(data) Return the median (middle value) of numeric data, using the common "mean of @@ -1148,65 +1173,6 @@ The final prediction goes to the largest posterior. This is known as the 'female' -Sampling from kernel density estimation -*************************************** - -The :func:`kde()` function creates a continuous probability density -function from discrete samples. Some applications need a way to make -random selections from that distribution. - -The technique is to pick a sample from a bandwidth scaled kernel -function and recenter the result around a randomly chosen point from -the input data. This can be done with any kernel that has a known or -accurately approximated inverse cumulative distribution function. - -.. testcode:: - - from random import choice, random, seed - from math import sqrt, log, pi, tan, asin, cos, acos - from statistics import NormalDist - - kernel_invcdfs = { - 'normal': NormalDist().inv_cdf, - 'logistic': lambda p: log(p / (1 - p)), - 'sigmoid': lambda p: log(tan(p * pi/2)), - 'rectangular': lambda p: 2*p - 1, - 'triangular': lambda p: sqrt(2*p) - 1 if p < 0.5 else 1 - sqrt(2 - 2*p), - 'parabolic': lambda p: 2 * cos((acos(2*p-1) + pi) / 3), - 'cosine': lambda p: 2*asin(2*p - 1)/pi, - } - - def kde_random(data, h, kernel='normal'): - 'Return a function that samples from kde() smoothed data.' - kernel_invcdf = kernel_invcdfs[kernel] - def rand(): - return h * kernel_invcdf(random()) + choice(data) - return rand - -For example: - -.. doctest:: - - >>> discrete_samples = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2] - >>> rand = kde_random(discrete_samples, h=1.5) - >>> seed(8675309) - >>> selections = [rand() for i in range(10)] - >>> [round(x, 1) for x in selections] - [4.7, 7.4, 1.2, 7.8, 6.9, -1.3, 5.8, 0.2, -1.4, 5.7] - -.. testcode:: - :hide: - - from statistics import kde - from math import isclose - - # Verify that cdf / invcdf will round trip - xarr = [i/100 for i in range(-100, 101)] - for kernel, invcdf in kernel_invcdfs.items(): - cdf = kde([0.0], h=1.0, kernel=kernel, cumulative=True) - for x in xarr: - assert isclose(invcdf(cdf(x)), x, abs_tol=1E-9) - .. # This modelines must appear within the last ten lines of the file. kate: indent-width 3; remove-trailing-space on; replace-tabs on; encoding utf-8; diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index d996cf6c4d9b52..269a7cc985ad19 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -745,7 +745,8 @@ statistics * Add :func:`statistics.kde` for kernel density estimation. This makes it possible to estimate a continuous probability density function - from a fixed number of discrete samples. + from a fixed number of discrete samples. Also added :func:`statistics.kde_random` + for sampling from the estimated probability density function. (Contributed by Raymond Hettinger in :gh:`115863`.) .. _whatsnew313-subprocess: diff --git a/Lib/statistics.py b/Lib/statistics.py index fc00891b083dc3..f3ce2d8b6b442a 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -113,6 +113,7 @@ 'geometric_mean', 'harmonic_mean', 'kde', + 'kde_random', 'linear_regression', 'mean', 'median', @@ -138,12 +139,13 @@ from itertools import count, groupby, repeat from bisect import bisect_left, bisect_right from math import hypot, sqrt, fabs, exp, erf, tau, log, fsum, sumprod -from math import isfinite, isinf, pi, cos, sin, cosh, atan +from math import isfinite, isinf, pi, cos, sin, tan, cosh, asin, atan, acos from functools import reduce from operator import itemgetter from collections import Counter, namedtuple, defaultdict _SQRT2 = sqrt(2.0) +_random = random # === Exceptions === @@ -978,11 +980,9 @@ def pdf(x): return sum(K((x - x_i) / h) for x_i in data) / (n * h) def cdf(x): - n = len(data) return sum(W((x - x_i) / h) for x_i in data) / n - else: sample = sorted(data) @@ -1078,6 +1078,7 @@ def quantiles(data, *, n=4, method='exclusive'): if ld == 1: return data * (n - 1) raise StatisticsError('must have at least one data point') + if method == 'inclusive': m = ld - 1 result = [] @@ -1086,6 +1087,7 @@ def quantiles(data, *, n=4, method='exclusive'): interpolated = (data[j] * (n - delta) + data[j + 1] * delta) / n result.append(interpolated) return result + if method == 'exclusive': m = ld + 1 result = [] @@ -1096,6 +1098,7 @@ def quantiles(data, *, n=4, method='exclusive'): interpolated = (data[j - 1] * (n - delta) + data[j] * delta) / n result.append(interpolated) return result + raise ValueError(f'Unknown method: {method!r}') @@ -1709,3 +1712,97 @@ def __getstate__(self): def __setstate__(self, state): self._mu, self._sigma = state + + +## kde_random() ############################################################## + +def _newton_raphson(f_inv_estimate, f, f_prime, tolerance=1e-12): + def f_inv(y): + "Return x such that f(x) ≈ y within the specified tolerance." + x = f_inv_estimate(y) + while abs(diff := f(x) - y) > tolerance: + x -= diff / f_prime(x) + return x + return f_inv + +def _quartic_invcdf_estimate(p): + sign, p = (1.0, p) if p <= 1/2 else (-1.0, 1.0 - p) + x = (2.0 * p) ** 0.4258865685331 - 1.0 + if p >= 0.004 < 0.499: + x += 0.026818732 * sin(7.101753784 * p + 2.73230839482953) + return x * sign + +_quartic_invcdf = _newton_raphson( + f_inv_estimate = _quartic_invcdf_estimate, + f = lambda t: 3/16 * t**5 - 5/8 * t**3 + 15/16 * t + 1/2, + f_prime = lambda t: 15/16 * (1.0 - t * t) ** 2) + +def _triweight_invcdf_estimate(p): + sign, p = (1.0, p) if p <= 1/2 else (-1.0, 1.0 - p) + x = (2.0 * p) ** 0.3400218741872791 - 1.0 + return x * sign + +_triweight_invcdf = _newton_raphson( + f_inv_estimate = _triweight_invcdf_estimate, + f = lambda t: 35/32 * (-1/7*t**7 + 3/5*t**5 - t**3 + t) + 1/2, + f_prime = lambda t: 35/32 * (1.0 - t * t) ** 3) + +_kernel_invcdfs = { + 'normal': NormalDist().inv_cdf, + 'logistic': lambda p: log(p / (1 - p)), + 'sigmoid': lambda p: log(tan(p * pi/2)), + 'rectangular': lambda p: 2*p - 1, + 'parabolic': lambda p: 2 * cos((acos(2*p-1) + pi) / 3), + 'quartic': _quartic_invcdf, + 'triweight': _triweight_invcdf, + 'triangular': lambda p: sqrt(2*p) - 1 if p < 1/2 else 1 - sqrt(2 - 2*p), + 'cosine': lambda p: 2 * asin(2*p - 1) / pi, +} +_kernel_invcdfs['gauss'] = _kernel_invcdfs['normal'] +_kernel_invcdfs['uniform'] = _kernel_invcdfs['rectangular'] +_kernel_invcdfs['epanechnikov'] = _kernel_invcdfs['parabolic'] +_kernel_invcdfs['biweight'] = _kernel_invcdfs['quartic'] + +def kde_random(data, h, kernel='normal', *, seed=None): + """Return a function that makes a random selection from the estimated + probability density function created by kde(data, h, kernel). + + Providing a *seed* allows reproducible selections within a single + thread. The seed may be an integer, float, str, or bytes. + + A StatisticsError will be raised if the *data* sequence is empty. + + Example: + + >>> data = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2] + >>> rand = kde_random(data, h=1.5, seed=8675309) + >>> new_selections = [rand() for i in range(10)] + >>> [round(x, 1) for x in new_selections] + [0.7, 6.2, 1.2, 6.9, 7.0, 1.8, 2.5, -0.5, -1.8, 5.6] + + """ + n = len(data) + if not n: + raise StatisticsError('Empty data sequence') + + if not isinstance(data[0], (int, float)): + raise TypeError('Data sequence must contain ints or floats') + + if h <= 0.0: + raise StatisticsError(f'Bandwidth h must be positive, not {h=!r}') + + try: + kernel_invcdf = _kernel_invcdfs[kernel] + except KeyError: + raise StatisticsError(f'Unknown kernel name: {kernel!r}') + + prng = _random.Random(seed) + random = prng.random + choice = prng.choice + + def rand(): + return choice(data) + h * kernel_invcdf(random()) + + rand.__doc__ = f'Random KDE selection with {h=!r} and {kernel=!r}' + + return rand diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 204787a88a9c5f..fe6c59c30dae28 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -2426,6 +2426,86 @@ def integrate(func, low, high, steps=10_000): self.assertEqual(f_hat(-1.0), 1/2) self.assertEqual(f_hat(1.0), 1/2) + def test_kde_kernel_invcdfs(self): + kernel_invcdfs = statistics._kernel_invcdfs + kde = statistics.kde + + # Verify that cdf / invcdf will round trip + xarr = [i/100 for i in range(-100, 101)] + for kernel, invcdf in kernel_invcdfs.items(): + with self.subTest(kernel=kernel): + cdf = kde([0.0], h=1.0, kernel=kernel, cumulative=True) + for x in xarr: + self.assertAlmostEqual(invcdf(cdf(x)), x, places=5) + + def test_kde_random(self): + kde_random = statistics.kde_random + StatisticsError = statistics.StatisticsError + kernels = ['normal', 'gauss', 'logistic', 'sigmoid', 'rectangular', + 'uniform', 'triangular', 'parabolic', 'epanechnikov', + 'quartic', 'biweight', 'triweight', 'cosine'] + sample = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2] + + # Smoke test + + for kernel in kernels: + with self.subTest(kernel=kernel): + rand = kde_random(sample, h=1.5, kernel=kernel) + selections = [rand() for i in range(10)] + + # Check error cases + + with self.assertRaises(StatisticsError): + kde_random([], h=1.0) # Empty dataset + with self.assertRaises(TypeError): + kde_random(['abc', 'def'], 1.5) # Non-numeric data + with self.assertRaises(TypeError): + kde_random(iter(sample), 1.5) # Data is not a sequence + with self.assertRaises(StatisticsError): + kde_random(sample, h=0.0) # Zero bandwidth + with self.assertRaises(StatisticsError): + kde_random(sample, h=0.0) # Negative bandwidth + with self.assertRaises(TypeError): + kde_random(sample, h='str') # Wrong bandwidth type + with self.assertRaises(StatisticsError): + kde_random(sample, h=1.0, kernel='bogus') # Invalid kernel + + # Test name and docstring of the generated function + + h = 1.5 + kernel = 'cosine' + prng = kde_random(sample, h, kernel) + self.assertEqual(prng.__name__, 'rand') + self.assertIn(kernel, prng.__doc__) + self.assertIn(repr(h), prng.__doc__) + + # Approximate distribution test: Compare a random sample to the expected distribution + + data = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2, 7.8, 14.3, 15.1, 15.3, 15.8, 17.0] + n = 1_000_000 + h = 1.75 + dx = 0.1 + + def p_expected(x): + return F_hat(x + dx) - F_hat(x - dx) + + def p_observed(x): + # P(x-dx <= X < x+dx) / (2*dx) + i = bisect.bisect_left(big_sample, x - dx) + j = bisect.bisect_right(big_sample, x + dx) + return (j - i) / len(big_sample) + + for kernel in kernels: + with self.subTest(kernel=kernel): + + F_hat = statistics.kde(data, h, kernel, cumulative=True) + rand = kde_random(data, h, kernel, seed=8675309**2) + big_sample = sorted([rand() for i in range(n)]) + + for x in range(-40, 190): + x /= 10 + self.assertTrue(math.isclose(p_observed(x), p_expected(x), abs_tol=0.001)) + class TestQuantiles(unittest.TestCase): From 0b7814e0b638631fa2f5c81bcbab7b94064948d7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 4 May 2024 09:39:58 +0200 Subject: [PATCH 201/217] gh-110850: Use _PyDeadline_Get() in EnterNonRecursiveMutex() (#118556) Use _PyDeadline_Init() and _PyDeadline_Get() in EnterNonRecursiveMutex() of thread_nt.h. _PyDeadline_Get() uses the monotonic clock which is now the same as the perf counter clock on all platforms. So this change does not cause any behavior change. It just reuses existing helper functions. --- Python/thread_nt.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Python/thread_nt.h b/Python/thread_nt.h index 9dca833ff203ca..425658131c2fce 100644 --- a/Python/thread_nt.h +++ b/Python/thread_nt.h @@ -77,17 +77,18 @@ EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds) } } else if (milliseconds != 0) { /* wait at least until the deadline */ - PyTime_t nanoseconds = (PyTime_t)milliseconds * (1000 * 1000); - PyTime_t deadline = _PyTime_Add(_PyTime_PerfCounterUnchecked(), nanoseconds); + PyTime_t timeout = (PyTime_t)milliseconds * (1000 * 1000); + PyTime_t deadline = _PyDeadline_Init(timeout); while (mutex->locked) { - PyTime_t microseconds = _PyTime_AsMicroseconds(nanoseconds, - _PyTime_ROUND_TIMEOUT); + PyTime_t microseconds = _PyTime_AsMicroseconds(timeout, + _PyTime_ROUND_TIMEOUT); if (PyCOND_TIMEDWAIT(&mutex->cv, &mutex->cs, microseconds) < 0) { result = WAIT_FAILED; break; } - nanoseconds = deadline - _PyTime_PerfCounterUnchecked(); - if (nanoseconds <= 0) { + + timeout = _PyDeadline_Get(deadline); + if (timeout <= 0) { break; } } From da2cfc4cb6b756b819b45bf34dd735c27b74d803 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Sat, 4 May 2024 08:50:24 +0100 Subject: [PATCH 202/217] GH-113464: Remove the extra jump via `_SIDE_EXIT` in `_EXIT_TRACE` (GH-118545) --- Include/internal/pycore_uop_ids.h | 37 +++++++++++++------------- Include/internal/pycore_uop_metadata.h | 6 +---- Python/bytecodes.c | 6 +---- Python/executor_cases.c.h | 10 +------ Python/optimizer.c | 21 +++++++-------- Python/optimizer_cases.c.h | 4 --- 6 files changed, 31 insertions(+), 53 deletions(-) diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index aa3940456b62f2..988464bcc210c8 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -240,33 +240,32 @@ extern "C" { #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _SIDE_EXIT 429 -#define _START_EXECUTOR 430 -#define _STORE_ATTR 431 -#define _STORE_ATTR_INSTANCE_VALUE 432 -#define _STORE_ATTR_SLOT 433 +#define _START_EXECUTOR 429 +#define _STORE_ATTR 430 +#define _STORE_ATTR_INSTANCE_VALUE 431 +#define _STORE_ATTR_SLOT 432 #define _STORE_ATTR_WITH_HINT STORE_ATTR_WITH_HINT #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 434 -#define _STORE_FAST_0 435 -#define _STORE_FAST_1 436 -#define _STORE_FAST_2 437 -#define _STORE_FAST_3 438 -#define _STORE_FAST_4 439 -#define _STORE_FAST_5 440 -#define _STORE_FAST_6 441 -#define _STORE_FAST_7 442 +#define _STORE_FAST 433 +#define _STORE_FAST_0 434 +#define _STORE_FAST_1 435 +#define _STORE_FAST_2 436 +#define _STORE_FAST_3 437 +#define _STORE_FAST_4 438 +#define _STORE_FAST_5 439 +#define _STORE_FAST_6 440 +#define _STORE_FAST_7 441 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME #define _STORE_SLICE STORE_SLICE -#define _STORE_SUBSCR 443 +#define _STORE_SUBSCR 442 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 444 -#define _TO_BOOL 445 +#define _TIER2_RESUME_CHECK 443 +#define _TO_BOOL 444 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -276,13 +275,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 446 +#define _UNPACK_SEQUENCE 445 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 446 +#define MAX_UOP_ID 445 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index d0860024e0db4d..475924dcd9c6b8 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -237,7 +237,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_SET_IP] = 0, [_CHECK_STACK_SPACE_OPERAND] = HAS_DEOPT_FLAG, [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG, - [_EXIT_TRACE] = HAS_EXIT_FLAG, + [_EXIT_TRACE] = 0, [_CHECK_VALIDITY] = HAS_DEOPT_FLAG, [_LOAD_CONST_INLINE] = HAS_PURE_FLAG, [_LOAD_CONST_INLINE_BORROW] = HAS_PURE_FLAG, @@ -252,7 +252,6 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_FATAL_ERROR] = 0, [_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG, [_DEOPT] = 0, - [_SIDE_EXIT] = 0, [_ERROR_POP_N] = HAS_ARG_FLAG, [_TIER2_RESUME_CHECK] = HAS_DEOPT_FLAG, }; @@ -459,7 +458,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_SET_FUNCTION_ATTRIBUTE] = "_SET_FUNCTION_ATTRIBUTE", [_SET_IP] = "_SET_IP", [_SET_UPDATE] = "_SET_UPDATE", - [_SIDE_EXIT] = "_SIDE_EXIT", [_START_EXECUTOR] = "_START_EXECUTOR", [_STORE_ATTR] = "_STORE_ATTR", [_STORE_ATTR_INSTANCE_VALUE] = "_STORE_ATTR_INSTANCE_VALUE", @@ -970,8 +968,6 @@ int _PyUop_num_popped(int opcode, int oparg) return 0; case _DEOPT: return 0; - case _SIDE_EXIT: - return 0; case _ERROR_POP_N: return oparg; case _TIER2_RESUME_CHECK: diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e8383eda6a9d49..ddada96bea71b8 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4133,7 +4133,7 @@ dummy_func( } tier2 op(_EXIT_TRACE, (--)) { - EXIT_IF(1); + EXIT_TO_TRACE(); } tier2 op(_CHECK_VALIDITY, (--)) { @@ -4266,10 +4266,6 @@ dummy_func( EXIT_TO_TIER1(); } - tier2 op(_SIDE_EXIT, (--)) { - EXIT_TO_TRACE(); - } - tier2 op(_ERROR_POP_N, (target/2, unused[oparg] --)) { frame->instr_ptr = ((_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive) + target; SYNC_SP(); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index c3ee6e9039c900..d0b794c61ef4a8 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4127,10 +4127,7 @@ } case _EXIT_TRACE: { - if (1) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + EXIT_TO_TRACE(); break; } @@ -4319,11 +4316,6 @@ break; } - case _SIDE_EXIT: { - EXIT_TO_TRACE(); - break; - } - case _ERROR_POP_N: { oparg = CURRENT_OPARG(); uint32_t target = (uint32_t)CURRENT_OPERAND(); diff --git a/Python/optimizer.c b/Python/optimizer.c index 56768ae8f542f6..c0e1be96353d3f 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -976,7 +976,7 @@ count_exits(_PyUOpInstruction *buffer, int length) int exit_count = 0; for (int i = 0; i < length; i++) { int opcode = buffer[i].opcode; - if (opcode == _SIDE_EXIT || opcode == _DYNAMIC_EXIT) { + if (opcode == _EXIT_TRACE || opcode == _DYNAMIC_EXIT) { exit_count++; } } @@ -1021,7 +1021,7 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) int32_t target = (int32_t)uop_get_target(inst); if (_PyUop_Flags[opcode] & (HAS_EXIT_FLAG | HAS_DEOPT_FLAG)) { uint16_t exit_op = (_PyUop_Flags[opcode] & HAS_EXIT_FLAG) ? - _SIDE_EXIT : _DEOPT; + _EXIT_TRACE : _DEOPT; int32_t jump_target = target; if (is_for_iter_test[opcode]) { /* Target the POP_TOP immediately after the END_FOR, @@ -1112,7 +1112,7 @@ sanity_check(_PyExecutorObject *executor) CHECK(target_unused(opcode)); break; case UOP_FORMAT_EXIT: - CHECK(opcode == _SIDE_EXIT); + CHECK(opcode == _EXIT_TRACE); CHECK(inst->exit_index < executor->exit_count); break; case UOP_FORMAT_JUMP: @@ -1138,9 +1138,9 @@ sanity_check(_PyExecutorObject *executor) uint16_t opcode = inst->opcode; CHECK( opcode == _DEOPT || - opcode == _SIDE_EXIT || + opcode == _EXIT_TRACE || opcode == _ERROR_POP_N); - if (opcode == _SIDE_EXIT) { + if (opcode == _EXIT_TRACE) { CHECK(inst->format == UOP_FORMAT_EXIT); } } @@ -1178,7 +1178,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil dest--; *dest = buffer[i]; assert(opcode != _POP_JUMP_IF_FALSE && opcode != _POP_JUMP_IF_TRUE); - if (opcode == _SIDE_EXIT) { + if (opcode == _EXIT_TRACE) { executor->exits[next_exit].target = buffer[i].target; dest->exit_index = next_exit; dest->format = UOP_FORMAT_EXIT; @@ -1398,14 +1398,13 @@ counter_optimize( return 0; } _Py_CODEUNIT *target = instr + 1 + _PyOpcode_Caches[JUMP_BACKWARD] - oparg; - _PyUOpInstruction buffer[5] = { - { .opcode = _START_EXECUTOR, .jump_target = 4, .format=UOP_FORMAT_JUMP }, + _PyUOpInstruction buffer[4] = { + { .opcode = _START_EXECUTOR, .jump_target = 3, .format=UOP_FORMAT_JUMP }, { .opcode = _LOAD_CONST_INLINE_BORROW, .operand = (uintptr_t)self }, { .opcode = _INTERNAL_INCREMENT_OPT_COUNTER }, - { .opcode = _EXIT_TRACE, .jump_target = 4, .format=UOP_FORMAT_JUMP }, - { .opcode = _SIDE_EXIT, .target = (uint32_t)(target - _PyCode_CODE(code)), .format=UOP_FORMAT_TARGET } + { .opcode = _EXIT_TRACE, .target = (uint32_t)(target - _PyCode_CODE(code)), .format=UOP_FORMAT_TARGET } }; - _PyExecutorObject *executor = make_executor_from_uops(buffer, 5, &EMPTY_FILTER); + _PyExecutorObject *executor = make_executor_from_uops(buffer, 4, &EMPTY_FILTER); if (executor == NULL) { return -1; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index e680d76141776f..b602d663b08ba6 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2140,10 +2140,6 @@ break; } - case _SIDE_EXIT: { - break; - } - case _ERROR_POP_N: { stack_pointer += -oparg; break; From 85af78996117dbe8ad45716633a3d6c39ff7bab2 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sat, 4 May 2024 09:23:50 +0100 Subject: [PATCH 203/217] gh-111997: C-API for signalling monitoring events (#116413) --- Doc/c-api/index.rst | 1 + Doc/c-api/monitoring.rst | 164 ++++++ Doc/conf.py | 1 + Doc/library/sys.monitoring.rst | 5 +- Include/Python.h | 1 + Include/cpython/monitoring.h | 250 +++++++++ Include/internal/pycore_instruments.h | 32 -- Include/monitoring.h | 18 + Lib/test/test_monitoring.py | 183 ++++++- Makefile.pre.in | 2 + ...-03-13-17-48-24.gh-issue-111997.8ZbHlA.rst | 1 + Modules/Setup.stdlib.in | 2 +- Modules/_testcapi/monitoring.c | 507 ++++++++++++++++++ Modules/_testcapi/parts.h | 1 + Modules/_testcapimodule.c | 3 + PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 + PCbuild/_testinternalcapi.vcxproj.filters | 2 +- Python/instrumentation.c | 301 +++++++++++ Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 + 20 files changed, 1442 insertions(+), 37 deletions(-) create mode 100644 Doc/c-api/monitoring.rst create mode 100644 Include/cpython/monitoring.h create mode 100644 Include/monitoring.h create mode 100644 Misc/NEWS.d/next/C API/2024-03-13-17-48-24.gh-issue-111997.8ZbHlA.rst create mode 100644 Modules/_testcapi/monitoring.c diff --git a/Doc/c-api/index.rst b/Doc/c-api/index.rst index 9a8f1507b3f4cc..ba56b03c6ac8e7 100644 --- a/Doc/c-api/index.rst +++ b/Doc/c-api/index.rst @@ -25,3 +25,4 @@ document the API functions in detail. memory.rst objimpl.rst apiabiversion.rst + monitoring.rst diff --git a/Doc/c-api/monitoring.rst b/Doc/c-api/monitoring.rst new file mode 100644 index 00000000000000..763ec8ef761e4e --- /dev/null +++ b/Doc/c-api/monitoring.rst @@ -0,0 +1,164 @@ +.. highlight:: c + +.. _monitoring: + +Monitorong C API +================ + +Added in version 3.13. + +An extension may need to interact with the event monitoring system. Subscribing +to events and registering callbacks can be done via the Python API exposed in +:mod:`sys.monitoring`. + +Generating Execution Events +=========================== + +The functions below make it possible for an extension to fire monitoring +events as it emulates the execution of Python code. Each of these functions +accepts a ``PyMonitoringState`` struct which contains concise information +about the activation state of events, as well as the event arguments, which +include a ``PyObject*`` representing the code object, the instruction offset +and sometimes additional, event-specific arguments (see :mod:`sys.monitoring` +for details about the signatures of the different event callbacks). +The ``codelike`` argument should be an instance of :class:`types.CodeType` +or of a type that emulates it. + +The VM disables tracing when firing an event, so there is no need for user +code to do that. + +Monitoring functions should not be called with an exception set, +except those listed below as working with the current exception. + +.. c:type:: PyMonitoringState + + Representation of the state of an event type. It is allocated by the user + while its contents are maintained by the monitoring API functions described below. + + +All of the functions below return 0 on success and -1 (with an exception set) on error. + +See :mod:`sys.monitoring` for descriptions of the events. + +.. c:function:: int PyMonitoring_FirePyStartEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire a ``PY_START`` event. + + +.. c:function:: int PyMonitoring_FirePyResumeEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire a ``PY_RESUME`` event. + + +.. c:function:: int PyMonitoring_FirePyReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject* retval) + + Fire a ``PY_RETURN`` event. + + +.. c:function:: int PyMonitoring_FirePyYieldEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject* retval) + + Fire a ``PY_YIELD`` event. + + +.. c:function:: int PyMonitoring_FireCallEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject* callable, PyObject *arg0) + + Fire a ``CALL`` event. + + +.. c:function:: int PyMonitoring_FireLineEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, int lineno) + + Fire a ``LINE`` event. + + +.. c:function:: int PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset) + + Fire a ``JUMP`` event. + + +.. c:function:: int PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset) + + Fire a ``BRANCH`` event. + + +.. c:function:: int PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *retval) + + Fire a ``C_RETURN`` event. + + +.. c:function:: int PyMonitoring_FirePyThrowEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire a ``PY_THROW`` event with the current exception (as returned by + :c:func:`PyErr_GetRaisedException`). + + +.. c:function:: int PyMonitoring_FireRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire a ``RAISE`` event with the current exception (as returned by + :c:func:`PyErr_GetRaisedException`). + + +.. c:function:: int PyMonitoring_FireCRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire a ``C_RAISE`` event with the current exception (as returned by + :c:func:`PyErr_GetRaisedException`). + + +.. c:function:: int PyMonitoring_FireReraiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire a ``RERAISE`` event with the current exception (as returned by + :c:func:`PyErr_GetRaisedException`). + + +.. c:function:: int PyMonitoring_FireExceptionHandledEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire an ``EXCEPTION_HANDLED`` event with the current exception (as returned by + :c:func:`PyErr_GetRaisedException`). + + +.. c:function:: int PyMonitoring_FirePyUnwindEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire a ``PY_UNWIND`` event with the current exception (as returned by + :c:func:`PyErr_GetRaisedException`). + + +.. c:function:: int PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) + + Fire a ``STOP_ITERATION`` event with the current exception (as returned by + :c:func:`PyErr_GetRaisedException`). + + +Managing the Monitoring State +----------------------------- + +Monitoring states can be managed with the help of monitoring scopes. A scope +would typically correspond to a python function. + +.. :c:function:: int PyMonitoring_EnterScope(PyMonitoringState *state_array, uint64_t *version, const uint8_t *event_types, Py_ssize_t length) + + Enter a monitored scope. ``event_types`` is an array of the event IDs for + events that may be fired from the scope. For example, the ID of a ``PY_START`` + event is the value ``PY_MONITORING_EVENT_PY_START``, which is numerically equal + to the base-2 logarithm of ``sys.monitoring.events.PY_START``. + ``state_array`` is an array with a monitoring state entry for each event in + ``event_types``, it is allocated by the user but populated by + ``PyMonitoring_EnterScope`` with information about the activation state of + the event. The size of ``event_types`` (and hence also of ``state_array``) + is given in ``length``. + + The ``version`` argument is a pointer to a value which should be allocated + by the user together with ``state_array`` and initialized to 0, + and then set only by ``PyMonitoring_EnterScope`` itelf. It allows this + function to determine whether event states have changed since the previous call, + and to return quickly if they have not. + + The scopes referred to here are lexical scopes: a function, class or method. + ``PyMonitoring_EnterScope`` should be called whenever the lexical scope is + entered. Scopes can be reentered, reusing the same *state_array* and *version*, + in situations like when emulating a recursive Python function. When a code-like's + execution is paused, such as when emulating a generator, the scope needs to + be exited and re-entered. + + +.. :c:function:: int PyMonitoring_ExitScope(void) + + Exit the last scope that was entered with ``PyMonitoring_EnterScope``. diff --git a/Doc/conf.py b/Doc/conf.py index 73abe8276f29fe..86371d17ae742a 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -131,6 +131,7 @@ ('c:func', 'vsnprintf'), # Standard C types ('c:type', 'FILE'), + ('c:type', 'int32_t'), ('c:type', 'int64_t'), ('c:type', 'intmax_t'), ('c:type', 'off_t'), diff --git a/Doc/library/sys.monitoring.rst b/Doc/library/sys.monitoring.rst index 4980227c60b21e..0e0095e108e9c0 100644 --- a/Doc/library/sys.monitoring.rst +++ b/Doc/library/sys.monitoring.rst @@ -255,7 +255,10 @@ No events are active by default. Per code object events '''''''''''''''''''''' -Events can also be controlled on a per code object basis. +Events can also be controlled on a per code object basis. The functions +defined below which accept a :class:`types.CodeType` should be prepared +to accept a look-alike object from functions which are not defined +in Python (see :ref:`monitoring`). .. function:: get_local_events(tool_id: int, code: CodeType, /) -> int diff --git a/Include/Python.h b/Include/Python.h index bb771fb3aec980..e05901b9e52b5a 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -84,6 +84,7 @@ #include "setobject.h" #include "methodobject.h" #include "moduleobject.h" +#include "monitoring.h" #include "cpython/funcobject.h" #include "cpython/classobject.h" #include "fileobject.h" diff --git a/Include/cpython/monitoring.h b/Include/cpython/monitoring.h new file mode 100644 index 00000000000000..efb9ec0e587552 --- /dev/null +++ b/Include/cpython/monitoring.h @@ -0,0 +1,250 @@ +#ifndef Py_CPYTHON_MONITORING_H +# error "this header file must not be included directly" +#endif + +/* Local events. + * These require bytecode instrumentation */ + +#define PY_MONITORING_EVENT_PY_START 0 +#define PY_MONITORING_EVENT_PY_RESUME 1 +#define PY_MONITORING_EVENT_PY_RETURN 2 +#define PY_MONITORING_EVENT_PY_YIELD 3 +#define PY_MONITORING_EVENT_CALL 4 +#define PY_MONITORING_EVENT_LINE 5 +#define PY_MONITORING_EVENT_INSTRUCTION 6 +#define PY_MONITORING_EVENT_JUMP 7 +#define PY_MONITORING_EVENT_BRANCH 8 +#define PY_MONITORING_EVENT_STOP_ITERATION 9 + +#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \ + ((ev) < _PY_MONITORING_LOCAL_EVENTS) + +/* Other events, mainly exceptions */ + +#define PY_MONITORING_EVENT_RAISE 10 +#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 11 +#define PY_MONITORING_EVENT_PY_UNWIND 12 +#define PY_MONITORING_EVENT_PY_THROW 13 +#define PY_MONITORING_EVENT_RERAISE 14 + + +/* Ancillary events */ + +#define PY_MONITORING_EVENT_C_RETURN 15 +#define PY_MONITORING_EVENT_C_RAISE 16 + + +typedef struct _PyMonitoringState { + uint8_t active; + uint8_t opaque; +} PyMonitoringState; + + +PyAPI_FUNC(int) +PyMonitoring_EnterScope(PyMonitoringState *state_array, uint64_t *version, + const uint8_t *event_types, Py_ssize_t length); + +PyAPI_FUNC(int) +PyMonitoring_ExitScope(void); + + +PyAPI_FUNC(int) +_PyMonitoring_FirePyStartEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + +PyAPI_FUNC(int) +_PyMonitoring_FirePyResumeEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + +PyAPI_FUNC(int) +_PyMonitoring_FirePyReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *retval); + +PyAPI_FUNC(int) +_PyMonitoring_FirePyYieldEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *retval); + +PyAPI_FUNC(int) +_PyMonitoring_FireCallEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject* callable, PyObject *arg0); + +PyAPI_FUNC(int) +_PyMonitoring_FireLineEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + int lineno); + +PyAPI_FUNC(int) +_PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset); + +PyAPI_FUNC(int) +_PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset); + +PyAPI_FUNC(int) +_PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *retval); + +PyAPI_FUNC(int) +_PyMonitoring_FirePyThrowEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + +PyAPI_FUNC(int) +_PyMonitoring_FireRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + +PyAPI_FUNC(int) +_PyMonitoring_FireReraiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + +PyAPI_FUNC(int) +_PyMonitoring_FireExceptionHandledEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + +PyAPI_FUNC(int) +_PyMonitoring_FireCRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + +PyAPI_FUNC(int) +_PyMonitoring_FirePyUnwindEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + +PyAPI_FUNC(int) +_PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset); + + +#define _PYMONITORING_IF_ACTIVE(STATE, X) \ + if ((STATE)->active) { \ + return (X); \ + } \ + else { \ + return 0; \ + } + +static inline int +PyMonitoring_FirePyStartEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FirePyStartEvent(state, codelike, offset)); +} + +static inline int +PyMonitoring_FirePyResumeEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FirePyResumeEvent(state, codelike, offset)); +} + +static inline int +PyMonitoring_FirePyReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *retval) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FirePyReturnEvent(state, codelike, offset, retval)); +} + +static inline int +PyMonitoring_FirePyYieldEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *retval) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FirePyYieldEvent(state, codelike, offset, retval)); +} + +static inline int +PyMonitoring_FireCallEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject* callable, PyObject *arg0) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireCallEvent(state, codelike, offset, callable, arg0)); +} + +static inline int +PyMonitoring_FireLineEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + int lineno) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireLineEvent(state, codelike, offset, lineno)); +} + +static inline int +PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireJumpEvent(state, codelike, offset, target_offset)); +} + +static inline int +PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireBranchEvent(state, codelike, offset, target_offset)); +} + +static inline int +PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *retval) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireCReturnEvent(state, codelike, offset, retval)); +} + +static inline int +PyMonitoring_FirePyThrowEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FirePyThrowEvent(state, codelike, offset)); +} + +static inline int +PyMonitoring_FireRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireRaiseEvent(state, codelike, offset)); +} + +static inline int +PyMonitoring_FireReraiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireReraiseEvent(state, codelike, offset)); +} + +static inline int +PyMonitoring_FireExceptionHandledEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireExceptionHandledEvent(state, codelike, offset)); +} + +static inline int +PyMonitoring_FireCRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireCRaiseEvent(state, codelike, offset)); +} + +static inline int +PyMonitoring_FirePyUnwindEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FirePyUnwindEvent(state, codelike, offset)); +} + +static inline int +PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireStopIterationEvent(state, codelike, offset)); +} + +#undef _PYMONITORING_IF_ACTIVE diff --git a/Include/internal/pycore_instruments.h b/Include/internal/pycore_instruments.h index 7f84d4a763bbcf..c98e82c8be5546 100644 --- a/Include/internal/pycore_instruments.h +++ b/Include/internal/pycore_instruments.h @@ -13,38 +13,6 @@ extern "C" { #define PY_MONITORING_TOOL_IDS 8 -/* Local events. - * These require bytecode instrumentation */ - -#define PY_MONITORING_EVENT_PY_START 0 -#define PY_MONITORING_EVENT_PY_RESUME 1 -#define PY_MONITORING_EVENT_PY_RETURN 2 -#define PY_MONITORING_EVENT_PY_YIELD 3 -#define PY_MONITORING_EVENT_CALL 4 -#define PY_MONITORING_EVENT_LINE 5 -#define PY_MONITORING_EVENT_INSTRUCTION 6 -#define PY_MONITORING_EVENT_JUMP 7 -#define PY_MONITORING_EVENT_BRANCH 8 -#define PY_MONITORING_EVENT_STOP_ITERATION 9 - -#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \ - ((ev) < _PY_MONITORING_LOCAL_EVENTS) - -/* Other events, mainly exceptions */ - -#define PY_MONITORING_EVENT_RAISE 10 -#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 11 -#define PY_MONITORING_EVENT_PY_UNWIND 12 -#define PY_MONITORING_EVENT_PY_THROW 13 -#define PY_MONITORING_EVENT_RERAISE 14 - - -/* Ancillary events */ - -#define PY_MONITORING_EVENT_C_RETURN 15 -#define PY_MONITORING_EVENT_C_RAISE 16 - - typedef uint32_t _PyMonitoringEventSet; /* Tool IDs */ diff --git a/Include/monitoring.h b/Include/monitoring.h new file mode 100644 index 00000000000000..985f7f230e44e3 --- /dev/null +++ b/Include/monitoring.h @@ -0,0 +1,18 @@ +#ifndef Py_MONITORING_H +#define Py_MONITORING_H +#ifdef __cplusplus +extern "C" { +#endif + +// There is currently no limited API for monitoring + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_MONITORING_H +# include "cpython/monitoring.h" +# undef Py_CPYTHON_MONITORING_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MONITORING_H */ diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index a9140d4d3dd743..eeb3f88a081750 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -3,16 +3,20 @@ import collections import dis import functools +import math import operator import sys import textwrap import types import unittest import asyncio -from test import support + +import test.support from test.support import requires_specialization, script_helper from test.support.import_helper import import_module +_testcapi = test.support.import_helper.import_module("_testcapi") + PAIR = (0,1) def f1(): @@ -1887,5 +1891,180 @@ def test_monitoring_live_at_shutdown(self): # gh-115832: An object destructor running during the final GC of # interpreter shutdown triggered an infinite loop in the # instrumentation code. - script = support.findfile("_test_monitoring_shutdown.py") + script = test.support.findfile("_test_monitoring_shutdown.py") script_helper.run_test_script(script) + + +class TestCApiEventGeneration(MonitoringTestBase, unittest.TestCase): + + class Scope: + def __init__(self, *args): + self.args = args + + def __enter__(self): + _testcapi.monitoring_enter_scope(*self.args) + + def __exit__(self, *args): + _testcapi.monitoring_exit_scope() + + def setUp(self): + super(TestCApiEventGeneration, self).setUp() + + capi = _testcapi + + self.codelike = capi.CodeLike(2) + + self.cases = [ + # (Event, function, *args) + ( 1, E.PY_START, capi.fire_event_py_start), + ( 1, E.PY_RESUME, capi.fire_event_py_resume), + ( 1, E.PY_YIELD, capi.fire_event_py_yield, 10), + ( 1, E.PY_RETURN, capi.fire_event_py_return, 20), + ( 2, E.CALL, capi.fire_event_call, callable, 40), + ( 1, E.JUMP, capi.fire_event_jump, 60), + ( 1, E.BRANCH, capi.fire_event_branch, 70), + ( 1, E.PY_THROW, capi.fire_event_py_throw, ValueError(1)), + ( 1, E.RAISE, capi.fire_event_raise, ValueError(2)), + ( 1, E.EXCEPTION_HANDLED, capi.fire_event_exception_handled, ValueError(5)), + ( 1, E.PY_UNWIND, capi.fire_event_py_unwind, ValueError(6)), + ( 1, E.STOP_ITERATION, capi.fire_event_stop_iteration, ValueError(7)), + ] + + + def check_event_count(self, event, func, args, expected): + class Counter: + def __init__(self): + self.count = 0 + def __call__(self, *args): + self.count += 1 + + try: + counter = Counter() + sys.monitoring.register_callback(TEST_TOOL, event, counter) + if event == E.C_RETURN or event == E.C_RAISE: + sys.monitoring.set_events(TEST_TOOL, E.CALL) + else: + sys.monitoring.set_events(TEST_TOOL, event) + event_value = int(math.log2(event)) + with self.Scope(self.codelike, event_value): + counter.count = 0 + try: + func(*args) + except ValueError as e: + self.assertIsInstance(expected, ValueError) + self.assertEqual(str(e), str(expected)) + return + else: + self.assertEqual(counter.count, expected) + + prev = sys.monitoring.register_callback(TEST_TOOL, event, None) + with self.Scope(self.codelike, event_value): + counter.count = 0 + func(*args) + self.assertEqual(counter.count, 0) + self.assertEqual(prev, counter) + finally: + sys.monitoring.set_events(TEST_TOOL, 0) + + def test_fire_event(self): + for expected, event, function, *args in self.cases: + offset = 0 + self.codelike = _testcapi.CodeLike(1) + with self.subTest(function.__name__): + args_ = (self.codelike, offset) + tuple(args) + self.check_event_count(event, function, args_, expected) + + def test_missing_exception(self): + for _, event, function, *args in self.cases: + if not (args and isinstance(args[-1], BaseException)): + continue + offset = 0 + self.codelike = _testcapi.CodeLike(1) + with self.subTest(function.__name__): + args_ = (self.codelike, offset) + tuple(args[:-1]) + (None,) + evt = int(math.log2(event)) + expected = ValueError(f"Firing event {evt} with no exception set") + self.check_event_count(event, function, args_, expected) + + + CANNOT_DISABLE = { E.PY_THROW, E.RAISE, E.RERAISE, + E.EXCEPTION_HANDLED, E.PY_UNWIND } + + def check_disable(self, event, func, args, expected): + try: + counter = CounterWithDisable() + sys.monitoring.register_callback(TEST_TOOL, event, counter) + if event == E.C_RETURN or event == E.C_RAISE: + sys.monitoring.set_events(TEST_TOOL, E.CALL) + else: + sys.monitoring.set_events(TEST_TOOL, event) + event_value = int(math.log2(event)) + with self.Scope(self.codelike, event_value): + counter.count = 0 + func(*args) + self.assertEqual(counter.count, expected) + counter.disable = True + if event in self.CANNOT_DISABLE: + # use try-except rather then assertRaises to avoid + # events from framework code + try: + counter.count = 0 + func(*args) + self.assertEqual(counter.count, expected) + except ValueError: + pass + else: + self.Error("Expected a ValueError") + else: + counter.count = 0 + func(*args) + self.assertEqual(counter.count, expected) + counter.count = 0 + func(*args) + self.assertEqual(counter.count, expected - 1) + finally: + sys.monitoring.set_events(TEST_TOOL, 0) + + def test_disable_event(self): + for expected, event, function, *args in self.cases: + offset = 0 + self.codelike = _testcapi.CodeLike(2) + with self.subTest(function.__name__): + args_ = (self.codelike, 0) + tuple(args) + self.check_disable(event, function, args_, expected) + + def test_enter_scope_two_events(self): + try: + yield_counter = CounterWithDisable() + unwind_counter = CounterWithDisable() + sys.monitoring.register_callback(TEST_TOOL, E.PY_YIELD, yield_counter) + sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, unwind_counter) + sys.monitoring.set_events(TEST_TOOL, E.PY_YIELD | E.PY_UNWIND) + + yield_value = int(math.log2(E.PY_YIELD)) + unwind_value = int(math.log2(E.PY_UNWIND)) + cl = _testcapi.CodeLike(2) + common_args = (cl, 0) + with self.Scope(cl, yield_value, unwind_value): + yield_counter.count = 0 + unwind_counter.count = 0 + + _testcapi.fire_event_py_unwind(*common_args, ValueError(42)) + assert(yield_counter.count == 0) + assert(unwind_counter.count == 1) + + _testcapi.fire_event_py_yield(*common_args, ValueError(42)) + assert(yield_counter.count == 1) + assert(unwind_counter.count == 1) + + yield_counter.disable = True + _testcapi.fire_event_py_yield(*common_args, ValueError(42)) + assert(yield_counter.count == 2) + assert(unwind_counter.count == 1) + + _testcapi.fire_event_py_yield(*common_args, ValueError(42)) + assert(yield_counter.count == 2) + assert(unwind_counter.count == 1) + + finally: + sys.monitoring.set_events(TEST_TOOL, 0) diff --git a/Makefile.pre.in b/Makefile.pre.in index e69d1fe6e2dd14..bd17debf309f0e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1022,6 +1022,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/methodobject.h \ $(srcdir)/Include/modsupport.h \ $(srcdir)/Include/moduleobject.h \ + $(srcdir)/Include/monitoring.h \ $(srcdir)/Include/object.h \ $(srcdir)/Include/objimpl.h \ $(srcdir)/Include/opcode.h \ @@ -1091,6 +1092,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/cpython/longobject.h \ $(srcdir)/Include/cpython/memoryobject.h \ $(srcdir)/Include/cpython/methodobject.h \ + $(srcdir)/Include/cpython/monitoring.h \ $(srcdir)/Include/cpython/object.h \ $(srcdir)/Include/cpython/objimpl.h \ $(srcdir)/Include/cpython/odictobject.h \ diff --git a/Misc/NEWS.d/next/C API/2024-03-13-17-48-24.gh-issue-111997.8ZbHlA.rst b/Misc/NEWS.d/next/C API/2024-03-13-17-48-24.gh-issue-111997.8ZbHlA.rst new file mode 100644 index 00000000000000..e74c0397b85aa1 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-03-13-17-48-24.gh-issue-111997.8ZbHlA.rst @@ -0,0 +1 @@ +Add a C-API for firing monitoring events. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 61037f592f82f1..78b979698fcd75 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -163,7 +163,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c @MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testcapi/monitoring.c b/Modules/_testcapi/monitoring.c new file mode 100644 index 00000000000000..aa90cfc06c1536 --- /dev/null +++ b/Modules/_testcapi/monitoring.c @@ -0,0 +1,507 @@ +#include "parts.h" +#include "util.h" + +#include "monitoring.h" + +#define Py_BUILD_CORE +#include "internal/pycore_instruments.h" + +typedef struct { + PyObject_HEAD + PyMonitoringState *monitoring_states; + uint64_t version; + int num_events; + /* Other fields */ +} PyCodeLikeObject; + + +static PyObject * +CodeLike_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + int num_events; + if (!PyArg_ParseTuple(args, "i", &num_events)) { + return NULL; + } + PyMonitoringState *states = (PyMonitoringState *)PyMem_Calloc( + num_events, sizeof(PyMonitoringState)); + if (states == NULL) { + return NULL; + } + PyCodeLikeObject *self = (PyCodeLikeObject *) type->tp_alloc(type, 0); + if (self != NULL) { + self->version = 0; + self->monitoring_states = states; + self->num_events = num_events; + } + else { + PyMem_Free(states); + } + return (PyObject *) self; +} + +static void +CodeLike_dealloc(PyCodeLikeObject *self) +{ + if (self->monitoring_states) { + PyMem_Free(self->monitoring_states); + } + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject * +CodeLike_str(PyCodeLikeObject *self) +{ + PyObject *res = NULL; + PyObject *sep = NULL; + PyObject *parts = NULL; + if (self->monitoring_states) { + parts = PyList_New(0); + if (parts == NULL) { + goto end; + } + + PyObject *heading = PyUnicode_FromString("PyCodeLikeObject"); + if (heading == NULL) { + goto end; + } + int err = PyList_Append(parts, heading); + Py_DECREF(heading); + if (err < 0) { + goto end; + } + + for (int i = 0; i < self->num_events; i++) { + PyObject *part = PyUnicode_FromFormat(" %d", self->monitoring_states[i].active); + if (part == NULL) { + goto end; + } + int err = PyList_Append(parts, part); + Py_XDECREF(part); + if (err < 0) { + goto end; + } + } + sep = PyUnicode_FromString(": "); + if (sep == NULL) { + goto end; + } + res = PyUnicode_Join(sep, parts); + } +end: + Py_XDECREF(sep); + Py_XDECREF(parts); + return res; +} + +static PyTypeObject PyCodeLike_Type = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "monitoring.CodeLike", + .tp_doc = PyDoc_STR("CodeLike objects"), + .tp_basicsize = sizeof(PyCodeLikeObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_new = CodeLike_new, + .tp_dealloc = (destructor) CodeLike_dealloc, + .tp_str = (reprfunc) CodeLike_str, +}; + +#define RAISE_UNLESS_CODELIKE(v) if (!Py_IS_TYPE((v), &PyCodeLike_Type)) { \ + PyErr_Format(PyExc_TypeError, "expected a code-like, got %s", Py_TYPE(v)->tp_name); \ + return NULL; \ + } + +/*******************************************************************/ + +static PyMonitoringState * +setup_fire(PyObject *codelike, int offset, PyObject *exc) +{ + RAISE_UNLESS_CODELIKE(codelike); + PyCodeLikeObject *cl = ((PyCodeLikeObject *)codelike); + assert(offset >= 0 && offset < cl->num_events); + PyMonitoringState *state = &cl->monitoring_states[offset]; + + if (exc != NULL) { + PyErr_SetRaisedException(Py_NewRef(exc)); + } + return state; +} + +static int +teardown_fire(int res, PyMonitoringState *state, PyObject *exception) +{ + if (res == -1) { + return -1; + } + if (exception) { + assert(PyErr_Occurred()); + assert(((PyObject*)Py_TYPE(exception)) == PyErr_Occurred()); + } + + else { + assert(!PyErr_Occurred()); + } + PyErr_Clear(); + return state->active; +} + +static PyObject * +fire_event_py_start(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + if (!PyArg_ParseTuple(args, "Oi", &codelike, &offset)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FirePyStartEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_py_resume(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + if (!PyArg_ParseTuple(args, "Oi", &codelike, &offset)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FirePyResumeEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_py_return(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *retval; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &retval)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FirePyReturnEvent(state, codelike, offset, retval); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_c_return(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *retval; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &retval)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireCReturnEvent(state, codelike, offset, retval); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_py_yield(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *retval; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &retval)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FirePyYieldEvent(state, codelike, offset, retval); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_call(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *callable, *arg0; + if (!PyArg_ParseTuple(args, "OiOO", &codelike, &offset, &callable, &arg0)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireCallEvent(state, codelike, offset, callable, arg0); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_line(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset, lineno; + if (!PyArg_ParseTuple(args, "Oii", &codelike, &offset, &lineno)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireLineEvent(state, codelike, offset, lineno); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_jump(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *target_offset; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &target_offset)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireJumpEvent(state, codelike, offset, target_offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_branch(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *target_offset; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &target_offset)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireBranchEvent(state, codelike, offset, target_offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_py_throw(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *exception; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { + return NULL; + } + NULLABLE(exception); + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FirePyThrowEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_raise(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *exception; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { + return NULL; + } + NULLABLE(exception); + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireRaiseEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_c_raise(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *exception; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { + return NULL; + } + NULLABLE(exception); + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireCRaiseEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_reraise(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *exception; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { + return NULL; + } + NULLABLE(exception); + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireReraiseEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_exception_handled(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *exception; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { + return NULL; + } + NULLABLE(exception); + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireExceptionHandledEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_py_unwind(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *exception; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { + return NULL; + } + NULLABLE(exception); + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FirePyUnwindEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_stop_iteration(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *exception; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { + return NULL; + } + NULLABLE(exception); + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireStopIterationEvent(state, codelike, offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +/*******************************************************************/ + +static PyObject * +enter_scope(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int event1, event2=0; + Py_ssize_t num_events = PyTuple_Size(args) - 1; + if (num_events == 1) { + if (!PyArg_ParseTuple(args, "Oi", &codelike, &event1)) { + return NULL; + } + } + else { + assert(num_events == 2); + if (!PyArg_ParseTuple(args, "Oii", &codelike, &event1, &event2)) { + return NULL; + } + } + RAISE_UNLESS_CODELIKE(codelike); + PyCodeLikeObject *cl = (PyCodeLikeObject *) codelike; + + uint8_t events[] = { event1, event2 }; + + PyMonitoring_EnterScope(cl->monitoring_states, + &cl->version, + events, + num_events); + + Py_RETURN_NONE; +} + +static PyObject * +exit_scope(PyObject *self, PyObject *args) +{ + PyMonitoring_ExitScope(); + Py_RETURN_NONE; +} + +static PyMethodDef TestMethods[] = { + {"fire_event_py_start", fire_event_py_start, METH_VARARGS}, + {"fire_event_py_resume", fire_event_py_resume, METH_VARARGS}, + {"fire_event_py_return", fire_event_py_return, METH_VARARGS}, + {"fire_event_c_return", fire_event_c_return, METH_VARARGS}, + {"fire_event_py_yield", fire_event_py_yield, METH_VARARGS}, + {"fire_event_call", fire_event_call, METH_VARARGS}, + {"fire_event_line", fire_event_line, METH_VARARGS}, + {"fire_event_jump", fire_event_jump, METH_VARARGS}, + {"fire_event_branch", fire_event_branch, METH_VARARGS}, + {"fire_event_py_throw", fire_event_py_throw, METH_VARARGS}, + {"fire_event_raise", fire_event_raise, METH_VARARGS}, + {"fire_event_c_raise", fire_event_c_raise, METH_VARARGS}, + {"fire_event_reraise", fire_event_reraise, METH_VARARGS}, + {"fire_event_exception_handled", fire_event_exception_handled, METH_VARARGS}, + {"fire_event_py_unwind", fire_event_py_unwind, METH_VARARGS}, + {"fire_event_stop_iteration", fire_event_stop_iteration, METH_VARARGS}, + {"monitoring_enter_scope", enter_scope, METH_VARARGS}, + {"monitoring_exit_scope", exit_scope, METH_VARARGS}, + {NULL}, +}; + +int +_PyTestCapi_Init_Monitoring(PyObject *m) +{ + if (PyType_Ready(&PyCodeLike_Type) < 0) { + return -1; + } + if (PyModule_AddObjectRef(m, "CodeLike", (PyObject *) &PyCodeLike_Type) < 0) { + Py_DECREF(m); + return -1; + } + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + return 0; +} diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index 0e24e44083ea05..41d190961c69ee 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -58,6 +58,7 @@ int _PyTestCapi_Init_Immortal(PyObject *module); int _PyTestCapi_Init_GC(PyObject *module); int _PyTestCapi_Init_Hash(PyObject *module); int _PyTestCapi_Init_Time(PyObject *module); +int _PyTestCapi_Init_Monitoring(PyObject *module); int _PyTestCapi_Init_Object(PyObject *module); #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index beae13cd74c731..e7e342e529eff4 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4135,6 +4135,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Time(m) < 0) { return NULL; } + if (_PyTestCapi_Init_Monitoring(m) < 0) { + return NULL; + } if (_PyTestCapi_Init_Object(m) < 0) { return NULL; } diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index cc25b6ebd7c673..44dbf2348137e1 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -125,6 +125,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index 28c82254d85d4c..cae44bc955f7f1 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -108,6 +108,9 @@ Source Files + + Source Files + diff --git a/PCbuild/_testinternalcapi.vcxproj.filters b/PCbuild/_testinternalcapi.vcxproj.filters index abfeeb39630daf..27429ea5833077 100644 --- a/PCbuild/_testinternalcapi.vcxproj.filters +++ b/PCbuild/_testinternalcapi.vcxproj.filters @@ -27,4 +27,4 @@ Resource Files - \ No newline at end of file + diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 8085d7335fe21a..72c9d2af5b3202 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -2424,3 +2424,304 @@ PyObject *_Py_CreateMonitoringObject(void) Py_DECREF(mod); return NULL; } + + +static int +capi_call_instrumentation(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject **args, Py_ssize_t nargs, int event) +{ + PyThreadState *tstate = _PyThreadState_GET(); + PyInterpreterState *interp = tstate->interp; + + uint8_t tools = state->active; + assert(args[1] == NULL); + args[1] = codelike; + if (offset < 0) { + PyErr_SetString(PyExc_ValueError, "offset must be non-negative"); + return -1; + } + PyObject *offset_obj = PyLong_FromLong(offset); + if (offset_obj == NULL) { + return -1; + } + assert(args[2] == NULL); + args[2] = offset_obj; + Py_ssize_t nargsf = nargs | PY_VECTORCALL_ARGUMENTS_OFFSET; + PyObject **callargs = &args[1]; + int err = 0; + + while (tools) { + int tool = most_significant_bit(tools); + assert(tool >= 0 && tool < 8); + assert(tools & (1 << tool)); + tools ^= (1 << tool); + int res = call_one_instrument(interp, tstate, callargs, nargsf, tool, event); + if (res == 0) { + /* Nothing to do */ + } + else if (res < 0) { + /* error */ + err = -1; + break; + } + else { + /* DISABLE */ + if (!PY_MONITORING_IS_INSTRUMENTED_EVENT(event)) { + PyErr_Format(PyExc_ValueError, + "Cannot disable %s events. Callback removed.", + event_names[event]); + /* Clear tool to prevent infinite loop */ + Py_CLEAR(interp->monitoring_callables[tool][event]); + err = -1; + break; + } + else { + state->active &= ~(1 << tool); + } + } + } + return err; +} + +int +PyMonitoring_EnterScope(PyMonitoringState *state_array, uint64_t *version, + const uint8_t *event_types, Py_ssize_t length) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (global_version(interp) == *version) { + return 0; + } + + _Py_GlobalMonitors *m = &interp->monitors; + for (Py_ssize_t i = 0; i < length; i++) { + int event = event_types[i]; + state_array[i].active = m->tools[event]; + } + *version = global_version(interp); + return 0; +} + +int +PyMonitoring_ExitScope(void) +{ + return 0; +} + +int +_PyMonitoring_FirePyStartEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + assert(state->active); + PyObject *args[3] = { NULL, NULL, NULL }; + return capi_call_instrumentation(state, codelike, offset, args, 2, + PY_MONITORING_EVENT_PY_START); +} + +int +_PyMonitoring_FirePyResumeEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + assert(state->active); + PyObject *args[3] = { NULL, NULL, NULL }; + return capi_call_instrumentation(state, codelike, offset, args, 2, + PY_MONITORING_EVENT_PY_RESUME); +} + + + +int +_PyMonitoring_FirePyReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject* retval) +{ + assert(state->active); + PyObject *args[4] = { NULL, NULL, NULL, retval }; + return capi_call_instrumentation(state, codelike, offset, args, 3, + PY_MONITORING_EVENT_PY_RETURN); +} + +int +_PyMonitoring_FirePyYieldEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject* retval) +{ + assert(state->active); + PyObject *args[4] = { NULL, NULL, NULL, retval }; + return capi_call_instrumentation(state, codelike, offset, args, 3, + PY_MONITORING_EVENT_PY_YIELD); +} + +int +_PyMonitoring_FireCallEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject* callable, PyObject *arg0) +{ + assert(state->active); + PyObject *args[5] = { NULL, NULL, NULL, callable, arg0 }; + return capi_call_instrumentation(state, codelike, offset, args, 4, + PY_MONITORING_EVENT_CALL); +} + +int +_PyMonitoring_FireLineEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + int lineno) +{ + assert(state->active); + PyObject *lno = PyLong_FromLong(lineno); + if (lno == NULL) { + return -1; + } + PyObject *args[4] = { NULL, NULL, NULL, lno }; + int res= capi_call_instrumentation(state, codelike, offset, args, 3, + PY_MONITORING_EVENT_LINE); + Py_DECREF(lno); + return res; +} + +int +_PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset) +{ + assert(state->active); + PyObject *args[4] = { NULL, NULL, NULL, target_offset }; + return capi_call_instrumentation(state, codelike, offset, args, 3, + PY_MONITORING_EVENT_JUMP); +} + +int +_PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset) +{ + assert(state->active); + PyObject *args[4] = { NULL, NULL, NULL, target_offset }; + return capi_call_instrumentation(state, codelike, offset, args, 3, + PY_MONITORING_EVENT_BRANCH); +} + +int +_PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *retval) +{ + assert(state->active); + PyObject *args[4] = { NULL, NULL, NULL, retval }; + return capi_call_instrumentation(state, codelike, offset, args, 3, + PY_MONITORING_EVENT_C_RETURN); +} + +static inline int +exception_event_setup(PyObject **exc, int event) { + *exc = PyErr_GetRaisedException(); + if (*exc == NULL) { + PyErr_Format(PyExc_ValueError, + "Firing event %d with no exception set", + event); + return -1; + } + return 0; +} + + +static inline int +exception_event_teardown(int err, PyObject *exc) { + if (err == 0) { + PyErr_SetRaisedException(exc); + } + else { + assert(PyErr_Occurred()); + Py_DECREF(exc); + } + return err; +} + +int +_PyMonitoring_FirePyThrowEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + int event = PY_MONITORING_EVENT_PY_THROW; + assert(state->active); + PyObject *exc; + if (exception_event_setup(&exc, event) < 0) { + return -1; + } + PyObject *args[4] = { NULL, NULL, NULL, exc }; + int err = capi_call_instrumentation(state, codelike, offset, args, 3, event); + return exception_event_teardown(err, exc); +} + +int +_PyMonitoring_FireRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + int event = PY_MONITORING_EVENT_RAISE; + assert(state->active); + PyObject *exc; + if (exception_event_setup(&exc, event) < 0) { + return -1; + } + PyObject *args[4] = { NULL, NULL, NULL, exc }; + int err = capi_call_instrumentation(state, codelike, offset, args, 3, event); + return exception_event_teardown(err, exc); +} + +int +_PyMonitoring_FireCRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + int event = PY_MONITORING_EVENT_C_RAISE; + assert(state->active); + PyObject *exc; + if (exception_event_setup(&exc, event) < 0) { + return -1; + } + PyObject *args[4] = { NULL, NULL, NULL, exc }; + int err = capi_call_instrumentation(state, codelike, offset, args, 3, event); + return exception_event_teardown(err, exc); +} + +int +_PyMonitoring_FireReraiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + int event = PY_MONITORING_EVENT_RERAISE; + assert(state->active); + PyObject *exc; + if (exception_event_setup(&exc, event) < 0) { + return -1; + } + PyObject *args[4] = { NULL, NULL, NULL, exc }; + int err = capi_call_instrumentation(state, codelike, offset, args, 3, event); + return exception_event_teardown(err, exc); +} + +int +_PyMonitoring_FireExceptionHandledEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + int event = PY_MONITORING_EVENT_EXCEPTION_HANDLED; + assert(state->active); + PyObject *exc; + if (exception_event_setup(&exc, event) < 0) { + return -1; + } + PyObject *args[4] = { NULL, NULL, NULL, exc }; + int err = capi_call_instrumentation(state, codelike, offset, args, 3, event); + return exception_event_teardown(err, exc); +} + +int +_PyMonitoring_FirePyUnwindEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + int event = PY_MONITORING_EVENT_PY_UNWIND; + assert(state->active); + PyObject *exc; + if (exception_event_setup(&exc, event) < 0) { + return -1; + } + PyObject *args[4] = { NULL, NULL, NULL, exc }; + int err = capi_call_instrumentation(state, codelike, offset, args, 3, event); + return exception_event_teardown(err, exc); +} + +int +_PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset) +{ + int event = PY_MONITORING_EVENT_STOP_ITERATION; + assert(state->active); + PyObject *exc; + if (exception_event_setup(&exc, event) < 0) { + return -1; + } + PyObject *args[4] = { NULL, NULL, NULL, exc }; + int err = capi_call_instrumentation(state, codelike, offset, args, 3, event); + return exception_event_teardown(err, exc); +} diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index b58e9d9fae380f..285129fd361665 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -397,6 +397,7 @@ Modules/xxmodule.c - Str_Type - Modules/xxmodule.c - Xxo_Type - Modules/xxsubtype.c - spamdict_type - Modules/xxsubtype.c - spamlist_type - +Modules/_testcapi/monitoring.c - PyCodeLike_Type - ##----------------------- ## non-static types - initialized once From 00da0afa0d98ce1fae67f7258c7f3db2b81a07e7 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Sat, 4 May 2024 03:26:40 -0700 Subject: [PATCH 204/217] gh-113081: Print colorized exception just like built-in traceback in pdb (#113082) --- Lib/pdb.py | 3 ++- Lib/test/test_pdb.py | 4 +++- .../Library/2023-12-14-02-51-38.gh-issue-113081.S-9Qyn.rst | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-14-02-51-38.gh-issue-113081.S-9Qyn.rst diff --git a/Lib/pdb.py b/Lib/pdb.py index bb669a0d2c1ce5..e6450861328b28 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -84,6 +84,7 @@ import tokenize import traceback import linecache +import _colorize from contextlib import contextmanager from rlcompleter import Completer @@ -2347,7 +2348,7 @@ def main(): print("The program exited via sys.exit(). Exit status:", end=' ') print(e) except BaseException as e: - traceback.print_exc() + traceback.print_exception(e, colorize=_colorize.can_colorize()) print("Uncaught exception. Entering post mortem debugging") print("Running 'cont' or 'step' will restart the program") pdb.interaction(None, e) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index a3d2dda43b086d..c9ea52717490b1 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -15,7 +15,7 @@ from contextlib import ExitStack, redirect_stdout from io import StringIO from test import support -from test.support import os_helper +from test.support import force_not_colorized, os_helper from test.support.import_helper import import_module from test.support.pty_helper import run_pty, FakeInput from unittest.mock import patch @@ -2919,6 +2919,7 @@ def start_pdb(): self.assertNotIn(b'Error', stdout, "Got an error running test script under PDB") + @force_not_colorized def test_issue16180(self): # A syntax error in the debuggee. script = "def f: pass\n" @@ -2932,6 +2933,7 @@ def test_issue16180(self): 'Fail to handle a syntax error in the debuggee.' .format(expected, stderr)) + @force_not_colorized def test_issue84583(self): # A syntax error from ast.literal_eval should not make pdb exit. script = "import ast; ast.literal_eval('')\n" diff --git a/Misc/NEWS.d/next/Library/2023-12-14-02-51-38.gh-issue-113081.S-9Qyn.rst b/Misc/NEWS.d/next/Library/2023-12-14-02-51-38.gh-issue-113081.S-9Qyn.rst new file mode 100644 index 00000000000000..e6b2d01837c7df --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-14-02-51-38.gh-issue-113081.S-9Qyn.rst @@ -0,0 +1 @@ +Print colorized exception just like built-in traceback in :mod:`pdb` From 1ab6356ebec25f216a0eddbd81225abcb93f2d55 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Sat, 4 May 2024 12:11:11 +0100 Subject: [PATCH 205/217] GH-118095: Use broader specializations of CALL in tier 1, for better tier 2 support of calls. (GH-118322) * Add CALL_PY_GENERAL, CALL_BOUND_METHOD_GENERAL and call CALL_NON_PY_GENERAL specializations. * Remove CALL_PY_WITH_DEFAULTS specialization * Use CALL_NON_PY_GENERAL in more cases when otherwise failing to specialize --- Include/internal/pycore_frame.h | 5 + Include/internal/pycore_opcode_metadata.h | 31 ++- Include/internal/pycore_uop_ids.h | 261 +++++++++++----------- Include/internal/pycore_uop_metadata.h | 26 ++- Include/opcode_ids.h | 116 +++++----- Lib/_opcode_metadata.py | 120 +++++----- Lib/test/test_call.py | 13 +- Lib/test/test_dis.py | 2 +- Lib/test/test_dynamic.py | 3 +- Lib/test/test_glob.py | 3 + Python/bytecodes.c | 141 +++++++++--- Python/ceval.c | 8 +- Python/executor_cases.c.h | 149 +++++++++++- Python/generated_cases.c.h | 235 ++++++++++++++++--- Python/opcode_targets.h | 6 +- Python/optimizer.c | 1 + Python/optimizer_bytecodes.c | 11 +- Python/optimizer_cases.c.h | 56 ++++- Python/specialize.c | 124 ++-------- 19 files changed, 863 insertions(+), 448 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 37ae5ae850389b..e13fdd9bb2e01c 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -318,6 +318,11 @@ PyGenObject *_PyFrame_GetGenerator(_PyInterpreterFrame *frame) return (PyGenObject *)(((char *)frame) - offset_in_gen); } +PyAPI_FUNC(_PyInterpreterFrame *) +_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, + PyObject *locals, PyObject* const* args, + size_t argcount, PyObject *kwnames); + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index d10224c70f82f5..2a237bc6dd8ee5 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -95,6 +95,8 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 2 + oparg; case CALL_BOUND_METHOD_EXACT_ARGS: return 2 + oparg; + case CALL_BOUND_METHOD_GENERAL: + return 2 + oparg; case CALL_BUILTIN_CLASS: return 2 + oparg; case CALL_BUILTIN_FAST: @@ -125,9 +127,11 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 2 + oparg; case CALL_METHOD_DESCRIPTOR_O: return 2 + oparg; + case CALL_NON_PY_GENERAL: + return 2 + oparg; case CALL_PY_EXACT_ARGS: return 2 + oparg; - case CALL_PY_WITH_DEFAULTS: + case CALL_PY_GENERAL: return 2 + oparg; case CALL_STR_1: return 3; @@ -524,6 +528,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 1; case CALL_BOUND_METHOD_EXACT_ARGS: return 0; + case CALL_BOUND_METHOD_GENERAL: + return 0; case CALL_BUILTIN_CLASS: return 1; case CALL_BUILTIN_FAST: @@ -554,10 +560,12 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 1; case CALL_METHOD_DESCRIPTOR_O: return 1; + case CALL_NON_PY_GENERAL: + return 1; case CALL_PY_EXACT_ARGS: return 0; - case CALL_PY_WITH_DEFAULTS: - return 1; + case CALL_PY_GENERAL: + return 0; case CALL_STR_1: return 1; case CALL_TUPLE_1: @@ -985,6 +993,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_ALLOC_AND_ENTER_INIT] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, + [CALL_BOUND_METHOD_GENERAL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_BUILTIN_CLASS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_BUILTIN_FAST] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1000,8 +1009,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_NOARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_O] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [CALL_NON_PY_GENERAL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_PY_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, - [CALL_PY_WITH_DEFAULTS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [CALL_PY_GENERAL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_STR_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_TUPLE_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_TYPE_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, @@ -1211,6 +1221,7 @@ _PyOpcode_macro_expansion[256] = { [BUILD_STRING] = { .nuops = 1, .uops = { { _BUILD_STRING, 0, 0 } } }, [BUILD_TUPLE] = { .nuops = 1, .uops = { { _BUILD_TUPLE, 0, 0 } } }, [CALL_BOUND_METHOD_EXACT_ARGS] = { .nuops = 8, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _INIT_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _CHECK_FUNCTION_EXACT_ARGS, 2, 1 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } }, + [CALL_BOUND_METHOD_GENERAL] = { .nuops = 6, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_METHOD_VERSION, 2, 1 }, { _EXPAND_METHOD, 0, 0 }, { _PY_FRAME_GENERAL, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } }, [CALL_BUILTIN_CLASS] = { .nuops = 2, .uops = { { _CALL_BUILTIN_CLASS, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, [CALL_BUILTIN_FAST] = { .nuops = 2, .uops = { { _CALL_BUILTIN_FAST, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_BUILTIN_FAST_WITH_KEYWORDS, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, @@ -1223,7 +1234,9 @@ _PyOpcode_macro_expansion[256] = { [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, [CALL_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_NOARGS, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, [CALL_METHOD_DESCRIPTOR_O] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_O, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, + [CALL_NON_PY_GENERAL] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE, 0, 0 }, { _CALL_NON_PY_GENERAL, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, [CALL_PY_EXACT_ARGS] = { .nuops = 6, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_FUNCTION_EXACT_ARGS, 2, 1 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } }, + [CALL_PY_GENERAL] = { .nuops = 5, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _PY_FRAME_GENERAL, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } }, [CALL_STR_1] = { .nuops = 2, .uops = { { _CALL_STR_1, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, [CALL_TUPLE_1] = { .nuops = 2, .uops = { { _CALL_TUPLE_1, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } }, [CALL_TYPE_1] = { .nuops = 1, .uops = { { _CALL_TYPE_1, 0, 0 } } }, @@ -1383,6 +1396,7 @@ const char *_PyOpcode_OpName[268] = { [CALL] = "CALL", [CALL_ALLOC_AND_ENTER_INIT] = "CALL_ALLOC_AND_ENTER_INIT", [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS", + [CALL_BOUND_METHOD_GENERAL] = "CALL_BOUND_METHOD_GENERAL", [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS", [CALL_BUILTIN_FAST] = "CALL_BUILTIN_FAST", [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS", @@ -1398,8 +1412,9 @@ const char *_PyOpcode_OpName[268] = { [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", [CALL_METHOD_DESCRIPTOR_NOARGS] = "CALL_METHOD_DESCRIPTOR_NOARGS", [CALL_METHOD_DESCRIPTOR_O] = "CALL_METHOD_DESCRIPTOR_O", + [CALL_NON_PY_GENERAL] = "CALL_NON_PY_GENERAL", [CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS", - [CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS", + [CALL_PY_GENERAL] = "CALL_PY_GENERAL", [CALL_STR_1] = "CALL_STR_1", [CALL_TUPLE_1] = "CALL_TUPLE_1", [CALL_TYPE_1] = "CALL_TYPE_1", @@ -1636,6 +1651,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [CALL] = CALL, [CALL_ALLOC_AND_ENTER_INIT] = CALL, [CALL_BOUND_METHOD_EXACT_ARGS] = CALL, + [CALL_BOUND_METHOD_GENERAL] = CALL, [CALL_BUILTIN_CLASS] = CALL, [CALL_BUILTIN_FAST] = CALL, [CALL_BUILTIN_FAST_WITH_KEYWORDS] = CALL, @@ -1651,8 +1667,9 @@ const uint8_t _PyOpcode_Deopt[256] = { [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = CALL, [CALL_METHOD_DESCRIPTOR_NOARGS] = CALL, [CALL_METHOD_DESCRIPTOR_O] = CALL, + [CALL_NON_PY_GENERAL] = CALL, [CALL_PY_EXACT_ARGS] = CALL, - [CALL_PY_WITH_DEFAULTS] = CALL, + [CALL_PY_GENERAL] = CALL, [CALL_STR_1] = CALL, [CALL_TUPLE_1] = CALL, [CALL_TYPE_1] = CALL, @@ -1852,8 +1869,6 @@ const uint8_t _PyOpcode_Deopt[256] = { case 146: \ case 147: \ case 148: \ - case 221: \ - case 222: \ case 223: \ case 224: \ case 225: \ diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 988464bcc210c8..1e6ef8e54a221a 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -51,32 +51,35 @@ extern "C" { #define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 317 #define _CALL_METHOD_DESCRIPTOR_NOARGS 318 #define _CALL_METHOD_DESCRIPTOR_O 319 -#define _CALL_PY_WITH_DEFAULTS CALL_PY_WITH_DEFAULTS -#define _CALL_STR_1 320 -#define _CALL_TUPLE_1 321 +#define _CALL_NON_PY_GENERAL 320 +#define _CALL_STR_1 321 +#define _CALL_TUPLE_1 322 #define _CALL_TYPE_1 CALL_TYPE_1 -#define _CHECK_ATTR_CLASS 322 -#define _CHECK_ATTR_METHOD_LAZY_DICT 323 -#define _CHECK_ATTR_MODULE 324 -#define _CHECK_ATTR_WITH_HINT 325 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 326 +#define _CHECK_ATTR_CLASS 323 +#define _CHECK_ATTR_METHOD_LAZY_DICT 324 +#define _CHECK_ATTR_MODULE 325 +#define _CHECK_ATTR_WITH_HINT 326 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 327 #define _CHECK_EG_MATCH CHECK_EG_MATCH #define _CHECK_EXC_MATCH CHECK_EXC_MATCH -#define _CHECK_FUNCTION 327 -#define _CHECK_FUNCTION_EXACT_ARGS 328 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES 329 -#define _CHECK_PEP_523 330 -#define _CHECK_PERIODIC 331 -#define _CHECK_STACK_SPACE 332 -#define _CHECK_STACK_SPACE_OPERAND 333 -#define _CHECK_VALIDITY 334 -#define _CHECK_VALIDITY_AND_SET_IP 335 -#define _COLD_EXIT 336 -#define _COMPARE_OP 337 -#define _COMPARE_OP_FLOAT 338 -#define _COMPARE_OP_INT 339 -#define _COMPARE_OP_STR 340 -#define _CONTAINS_OP 341 +#define _CHECK_FUNCTION 328 +#define _CHECK_FUNCTION_EXACT_ARGS 329 +#define _CHECK_FUNCTION_VERSION 330 +#define _CHECK_IS_NOT_PY_CALLABLE 331 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES 332 +#define _CHECK_METHOD_VERSION 333 +#define _CHECK_PEP_523 334 +#define _CHECK_PERIODIC 335 +#define _CHECK_STACK_SPACE 336 +#define _CHECK_STACK_SPACE_OPERAND 337 +#define _CHECK_VALIDITY 338 +#define _CHECK_VALIDITY_AND_SET_IP 339 +#define _COLD_EXIT 340 +#define _COMPARE_OP 341 +#define _COMPARE_OP_FLOAT 342 +#define _COMPARE_OP_INT 343 +#define _COMPARE_OP_STR 344 +#define _CONTAINS_OP 345 #define _CONTAINS_OP_DICT CONTAINS_OP_DICT #define _CONTAINS_OP_SET CONTAINS_OP_SET #define _CONVERT_VALUE CONVERT_VALUE @@ -88,52 +91,53 @@ extern "C" { #define _DELETE_GLOBAL DELETE_GLOBAL #define _DELETE_NAME DELETE_NAME #define _DELETE_SUBSCR DELETE_SUBSCR -#define _DEOPT 342 +#define _DEOPT 346 #define _DICT_MERGE DICT_MERGE #define _DICT_UPDATE DICT_UPDATE -#define _DYNAMIC_EXIT 343 +#define _DYNAMIC_EXIT 347 #define _END_SEND END_SEND -#define _ERROR_POP_N 344 +#define _ERROR_POP_N 348 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK -#define _FATAL_ERROR 345 +#define _EXPAND_METHOD 349 +#define _FATAL_ERROR 350 #define _FORMAT_SIMPLE FORMAT_SIMPLE #define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC -#define _FOR_ITER 346 -#define _FOR_ITER_GEN_FRAME 347 -#define _FOR_ITER_TIER_TWO 348 +#define _FOR_ITER 351 +#define _FOR_ITER_GEN_FRAME 352 +#define _FOR_ITER_TIER_TWO 353 #define _GET_AITER GET_AITER #define _GET_ANEXT GET_ANEXT #define _GET_AWAITABLE GET_AWAITABLE #define _GET_ITER GET_ITER #define _GET_LEN GET_LEN #define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER -#define _GUARD_BOTH_FLOAT 349 -#define _GUARD_BOTH_INT 350 -#define _GUARD_BOTH_UNICODE 351 -#define _GUARD_BUILTINS_VERSION 352 -#define _GUARD_DORV_NO_DICT 353 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 354 -#define _GUARD_GLOBALS_VERSION 355 -#define _GUARD_IS_FALSE_POP 356 -#define _GUARD_IS_NONE_POP 357 -#define _GUARD_IS_NOT_NONE_POP 358 -#define _GUARD_IS_TRUE_POP 359 -#define _GUARD_KEYS_VERSION 360 -#define _GUARD_NOS_FLOAT 361 -#define _GUARD_NOS_INT 362 -#define _GUARD_NOT_EXHAUSTED_LIST 363 -#define _GUARD_NOT_EXHAUSTED_RANGE 364 -#define _GUARD_NOT_EXHAUSTED_TUPLE 365 -#define _GUARD_TOS_FLOAT 366 -#define _GUARD_TOS_INT 367 -#define _GUARD_TYPE_VERSION 368 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 369 -#define _INIT_CALL_PY_EXACT_ARGS 370 -#define _INIT_CALL_PY_EXACT_ARGS_0 371 -#define _INIT_CALL_PY_EXACT_ARGS_1 372 -#define _INIT_CALL_PY_EXACT_ARGS_2 373 -#define _INIT_CALL_PY_EXACT_ARGS_3 374 -#define _INIT_CALL_PY_EXACT_ARGS_4 375 +#define _GUARD_BOTH_FLOAT 354 +#define _GUARD_BOTH_INT 355 +#define _GUARD_BOTH_UNICODE 356 +#define _GUARD_BUILTINS_VERSION 357 +#define _GUARD_DORV_NO_DICT 358 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 359 +#define _GUARD_GLOBALS_VERSION 360 +#define _GUARD_IS_FALSE_POP 361 +#define _GUARD_IS_NONE_POP 362 +#define _GUARD_IS_NOT_NONE_POP 363 +#define _GUARD_IS_TRUE_POP 364 +#define _GUARD_KEYS_VERSION 365 +#define _GUARD_NOS_FLOAT 366 +#define _GUARD_NOS_INT 367 +#define _GUARD_NOT_EXHAUSTED_LIST 368 +#define _GUARD_NOT_EXHAUSTED_RANGE 369 +#define _GUARD_NOT_EXHAUSTED_TUPLE 370 +#define _GUARD_TOS_FLOAT 371 +#define _GUARD_TOS_INT 372 +#define _GUARD_TYPE_VERSION 373 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 374 +#define _INIT_CALL_PY_EXACT_ARGS 375 +#define _INIT_CALL_PY_EXACT_ARGS_0 376 +#define _INIT_CALL_PY_EXACT_ARGS_1 377 +#define _INIT_CALL_PY_EXACT_ARGS_2 378 +#define _INIT_CALL_PY_EXACT_ARGS_3 379 +#define _INIT_CALL_PY_EXACT_ARGS_4 380 #define _INSTRUMENTED_CALL INSTRUMENTED_CALL #define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX #define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW @@ -150,65 +154,65 @@ extern "C" { #define _INSTRUMENTED_RETURN_CONST INSTRUMENTED_RETURN_CONST #define _INSTRUMENTED_RETURN_VALUE INSTRUMENTED_RETURN_VALUE #define _INSTRUMENTED_YIELD_VALUE INSTRUMENTED_YIELD_VALUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 376 -#define _IS_NONE 377 +#define _INTERNAL_INCREMENT_OPT_COUNTER 381 +#define _IS_NONE 382 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 378 -#define _ITER_CHECK_RANGE 379 -#define _ITER_CHECK_TUPLE 380 -#define _ITER_JUMP_LIST 381 -#define _ITER_JUMP_RANGE 382 -#define _ITER_JUMP_TUPLE 383 -#define _ITER_NEXT_LIST 384 -#define _ITER_NEXT_RANGE 385 -#define _ITER_NEXT_TUPLE 386 -#define _JUMP_TO_TOP 387 +#define _ITER_CHECK_LIST 383 +#define _ITER_CHECK_RANGE 384 +#define _ITER_CHECK_TUPLE 385 +#define _ITER_JUMP_LIST 386 +#define _ITER_JUMP_RANGE 387 +#define _ITER_JUMP_TUPLE 388 +#define _ITER_NEXT_LIST 389 +#define _ITER_NEXT_RANGE 390 +#define _ITER_NEXT_TUPLE 391 +#define _JUMP_TO_TOP 392 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND #define _LOAD_ASSERTION_ERROR LOAD_ASSERTION_ERROR -#define _LOAD_ATTR 388 -#define _LOAD_ATTR_CLASS 389 -#define _LOAD_ATTR_CLASS_0 390 -#define _LOAD_ATTR_CLASS_1 391 +#define _LOAD_ATTR 393 +#define _LOAD_ATTR_CLASS 394 +#define _LOAD_ATTR_CLASS_0 395 +#define _LOAD_ATTR_CLASS_1 396 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 392 -#define _LOAD_ATTR_INSTANCE_VALUE_0 393 -#define _LOAD_ATTR_INSTANCE_VALUE_1 394 -#define _LOAD_ATTR_METHOD_LAZY_DICT 395 -#define _LOAD_ATTR_METHOD_NO_DICT 396 -#define _LOAD_ATTR_METHOD_WITH_VALUES 397 -#define _LOAD_ATTR_MODULE 398 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 399 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 400 +#define _LOAD_ATTR_INSTANCE_VALUE 397 +#define _LOAD_ATTR_INSTANCE_VALUE_0 398 +#define _LOAD_ATTR_INSTANCE_VALUE_1 399 +#define _LOAD_ATTR_METHOD_LAZY_DICT 400 +#define _LOAD_ATTR_METHOD_NO_DICT 401 +#define _LOAD_ATTR_METHOD_WITH_VALUES 402 +#define _LOAD_ATTR_MODULE 403 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 404 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 405 #define _LOAD_ATTR_PROPERTY LOAD_ATTR_PROPERTY -#define _LOAD_ATTR_SLOT 401 -#define _LOAD_ATTR_SLOT_0 402 -#define _LOAD_ATTR_SLOT_1 403 -#define _LOAD_ATTR_WITH_HINT 404 +#define _LOAD_ATTR_SLOT 406 +#define _LOAD_ATTR_SLOT_0 407 +#define _LOAD_ATTR_SLOT_1 408 +#define _LOAD_ATTR_WITH_HINT 409 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS #define _LOAD_CONST LOAD_CONST -#define _LOAD_CONST_INLINE 405 -#define _LOAD_CONST_INLINE_BORROW 406 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 407 -#define _LOAD_CONST_INLINE_WITH_NULL 408 +#define _LOAD_CONST_INLINE 410 +#define _LOAD_CONST_INLINE_BORROW 411 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 412 +#define _LOAD_CONST_INLINE_WITH_NULL 413 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 409 -#define _LOAD_FAST_0 410 -#define _LOAD_FAST_1 411 -#define _LOAD_FAST_2 412 -#define _LOAD_FAST_3 413 -#define _LOAD_FAST_4 414 -#define _LOAD_FAST_5 415 -#define _LOAD_FAST_6 416 -#define _LOAD_FAST_7 417 +#define _LOAD_FAST 414 +#define _LOAD_FAST_0 415 +#define _LOAD_FAST_1 416 +#define _LOAD_FAST_2 417 +#define _LOAD_FAST_3 418 +#define _LOAD_FAST_4 419 +#define _LOAD_FAST_5 420 +#define _LOAD_FAST_6 421 +#define _LOAD_FAST_7 422 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 418 -#define _LOAD_GLOBAL_BUILTINS 419 -#define _LOAD_GLOBAL_MODULE 420 +#define _LOAD_GLOBAL 423 +#define _LOAD_GLOBAL_BUILTINS 424 +#define _LOAD_GLOBAL_MODULE 425 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR @@ -222,50 +226,51 @@ extern "C" { #define _MATCH_SEQUENCE MATCH_SEQUENCE #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_FRAME 421 -#define _POP_JUMP_IF_FALSE 422 -#define _POP_JUMP_IF_TRUE 423 +#define _POP_FRAME 426 +#define _POP_JUMP_IF_FALSE 427 +#define _POP_JUMP_IF_TRUE 428 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 424 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 429 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 425 +#define _PUSH_FRAME 430 #define _PUSH_NULL PUSH_NULL -#define _REPLACE_WITH_TRUE 426 +#define _PY_FRAME_GENERAL 431 +#define _REPLACE_WITH_TRUE 432 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR -#define _SAVE_RETURN_OFFSET 427 -#define _SEND 428 +#define _SAVE_RETURN_OFFSET 433 +#define _SEND 434 #define _SEND_GEN SEND_GEN #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 429 -#define _STORE_ATTR 430 -#define _STORE_ATTR_INSTANCE_VALUE 431 -#define _STORE_ATTR_SLOT 432 +#define _START_EXECUTOR 435 +#define _STORE_ATTR 436 +#define _STORE_ATTR_INSTANCE_VALUE 437 +#define _STORE_ATTR_SLOT 438 #define _STORE_ATTR_WITH_HINT STORE_ATTR_WITH_HINT #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 433 -#define _STORE_FAST_0 434 -#define _STORE_FAST_1 435 -#define _STORE_FAST_2 436 -#define _STORE_FAST_3 437 -#define _STORE_FAST_4 438 -#define _STORE_FAST_5 439 -#define _STORE_FAST_6 440 -#define _STORE_FAST_7 441 +#define _STORE_FAST 439 +#define _STORE_FAST_0 440 +#define _STORE_FAST_1 441 +#define _STORE_FAST_2 442 +#define _STORE_FAST_3 443 +#define _STORE_FAST_4 444 +#define _STORE_FAST_5 445 +#define _STORE_FAST_6 446 +#define _STORE_FAST_7 447 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME #define _STORE_SLICE STORE_SLICE -#define _STORE_SUBSCR 442 +#define _STORE_SUBSCR 448 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 443 -#define _TO_BOOL 444 +#define _TIER2_RESUME_CHECK 449 +#define _TO_BOOL 450 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -275,13 +280,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 445 +#define _UNPACK_SEQUENCE 451 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 445 +#define MAX_UOP_ID 451 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 475924dcd9c6b8..470e95e2b3b041 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -193,7 +193,13 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CHECK_ATTR_METHOD_LAZY_DICT] = HAS_DEOPT_FLAG, [_LOAD_ATTR_METHOD_LAZY_DICT] = HAS_ARG_FLAG, [_CHECK_PERIODIC] = HAS_EVAL_BREAK_FLAG, - [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, + [_PY_FRAME_GENERAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, + [_CHECK_FUNCTION_VERSION] = HAS_ARG_FLAG | HAS_EXIT_FLAG, + [_CHECK_METHOD_VERSION] = HAS_ARG_FLAG | HAS_EXIT_FLAG, + [_EXPAND_METHOD] = HAS_ARG_FLAG, + [_CHECK_IS_NOT_PY_CALLABLE] = HAS_ARG_FLAG | HAS_EXIT_FLAG, + [_CALL_NON_PY_GENERAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG, [_CHECK_PEP_523] = HAS_DEOPT_FLAG, [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_EXIT_FLAG, @@ -295,6 +301,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", [_CALL_METHOD_DESCRIPTOR_NOARGS] = "_CALL_METHOD_DESCRIPTOR_NOARGS", [_CALL_METHOD_DESCRIPTOR_O] = "_CALL_METHOD_DESCRIPTOR_O", + [_CALL_NON_PY_GENERAL] = "_CALL_NON_PY_GENERAL", [_CALL_STR_1] = "_CALL_STR_1", [_CALL_TUPLE_1] = "_CALL_TUPLE_1", [_CALL_TYPE_1] = "_CALL_TYPE_1", @@ -307,7 +314,10 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_CHECK_EXC_MATCH] = "_CHECK_EXC_MATCH", [_CHECK_FUNCTION] = "_CHECK_FUNCTION", [_CHECK_FUNCTION_EXACT_ARGS] = "_CHECK_FUNCTION_EXACT_ARGS", + [_CHECK_FUNCTION_VERSION] = "_CHECK_FUNCTION_VERSION", + [_CHECK_IS_NOT_PY_CALLABLE] = "_CHECK_IS_NOT_PY_CALLABLE", [_CHECK_MANAGED_OBJECT_HAS_VALUES] = "_CHECK_MANAGED_OBJECT_HAS_VALUES", + [_CHECK_METHOD_VERSION] = "_CHECK_METHOD_VERSION", [_CHECK_PEP_523] = "_CHECK_PEP_523", [_CHECK_PERIODIC] = "_CHECK_PERIODIC", [_CHECK_STACK_SPACE] = "_CHECK_STACK_SPACE", @@ -339,6 +349,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_ERROR_POP_N] = "_ERROR_POP_N", [_EXIT_INIT_CHECK] = "_EXIT_INIT_CHECK", [_EXIT_TRACE] = "_EXIT_TRACE", + [_EXPAND_METHOD] = "_EXPAND_METHOD", [_FATAL_ERROR] = "_FATAL_ERROR", [_FORMAT_SIMPLE] = "_FORMAT_SIMPLE", [_FORMAT_WITH_SPEC] = "_FORMAT_WITH_SPEC", @@ -449,6 +460,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_PUSH_EXC_INFO] = "_PUSH_EXC_INFO", [_PUSH_FRAME] = "_PUSH_FRAME", [_PUSH_NULL] = "_PUSH_NULL", + [_PY_FRAME_GENERAL] = "_PY_FRAME_GENERAL", [_REPLACE_WITH_TRUE] = "_REPLACE_WITH_TRUE", [_RESUME_CHECK] = "_RESUME_CHECK", [_RETURN_GENERATOR] = "_RETURN_GENERATOR", @@ -850,6 +862,18 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _CHECK_PERIODIC: return 0; + case _PY_FRAME_GENERAL: + return 2 + oparg; + case _CHECK_FUNCTION_VERSION: + return 2 + oparg; + case _CHECK_METHOD_VERSION: + return 2 + oparg; + case _EXPAND_METHOD: + return 2 + oparg; + case _CHECK_IS_NOT_PY_CALLABLE: + return 2 + oparg; + case _CALL_NON_PY_GENERAL: + return 2 + oparg; case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: return 2 + oparg; case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index 185205c6870edc..647f7c0ecb1ec8 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -144,63 +144,65 @@ extern "C" { #define BINARY_SUBSCR_TUPLE_INT 161 #define CALL_ALLOC_AND_ENTER_INIT 162 #define CALL_BOUND_METHOD_EXACT_ARGS 163 -#define CALL_BUILTIN_CLASS 164 -#define CALL_BUILTIN_FAST 165 -#define CALL_BUILTIN_FAST_WITH_KEYWORDS 166 -#define CALL_BUILTIN_O 167 -#define CALL_ISINSTANCE 168 -#define CALL_LEN 169 -#define CALL_LIST_APPEND 170 -#define CALL_METHOD_DESCRIPTOR_FAST 171 -#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 172 -#define CALL_METHOD_DESCRIPTOR_NOARGS 173 -#define CALL_METHOD_DESCRIPTOR_O 174 -#define CALL_PY_EXACT_ARGS 175 -#define CALL_PY_WITH_DEFAULTS 176 -#define CALL_STR_1 177 -#define CALL_TUPLE_1 178 -#define CALL_TYPE_1 179 -#define COMPARE_OP_FLOAT 180 -#define COMPARE_OP_INT 181 -#define COMPARE_OP_STR 182 -#define CONTAINS_OP_DICT 183 -#define CONTAINS_OP_SET 184 -#define FOR_ITER_GEN 185 -#define FOR_ITER_LIST 186 -#define FOR_ITER_RANGE 187 -#define FOR_ITER_TUPLE 188 -#define LOAD_ATTR_CLASS 189 -#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 190 -#define LOAD_ATTR_INSTANCE_VALUE 191 -#define LOAD_ATTR_METHOD_LAZY_DICT 192 -#define LOAD_ATTR_METHOD_NO_DICT 193 -#define LOAD_ATTR_METHOD_WITH_VALUES 194 -#define LOAD_ATTR_MODULE 195 -#define LOAD_ATTR_NONDESCRIPTOR_NO_DICT 196 -#define LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 197 -#define LOAD_ATTR_PROPERTY 198 -#define LOAD_ATTR_SLOT 199 -#define LOAD_ATTR_WITH_HINT 200 -#define LOAD_GLOBAL_BUILTIN 201 -#define LOAD_GLOBAL_MODULE 202 -#define LOAD_SUPER_ATTR_ATTR 203 -#define LOAD_SUPER_ATTR_METHOD 204 -#define RESUME_CHECK 205 -#define SEND_GEN 206 -#define STORE_ATTR_INSTANCE_VALUE 207 -#define STORE_ATTR_SLOT 208 -#define STORE_ATTR_WITH_HINT 209 -#define STORE_SUBSCR_DICT 210 -#define STORE_SUBSCR_LIST_INT 211 -#define TO_BOOL_ALWAYS_TRUE 212 -#define TO_BOOL_BOOL 213 -#define TO_BOOL_INT 214 -#define TO_BOOL_LIST 215 -#define TO_BOOL_NONE 216 -#define TO_BOOL_STR 217 -#define UNPACK_SEQUENCE_LIST 218 -#define UNPACK_SEQUENCE_TUPLE 219 -#define UNPACK_SEQUENCE_TWO_TUPLE 220 +#define CALL_BOUND_METHOD_GENERAL 164 +#define CALL_BUILTIN_CLASS 165 +#define CALL_BUILTIN_FAST 166 +#define CALL_BUILTIN_FAST_WITH_KEYWORDS 167 +#define CALL_BUILTIN_O 168 +#define CALL_ISINSTANCE 169 +#define CALL_LEN 170 +#define CALL_LIST_APPEND 171 +#define CALL_METHOD_DESCRIPTOR_FAST 172 +#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 173 +#define CALL_METHOD_DESCRIPTOR_NOARGS 174 +#define CALL_METHOD_DESCRIPTOR_O 175 +#define CALL_NON_PY_GENERAL 176 +#define CALL_PY_EXACT_ARGS 177 +#define CALL_PY_GENERAL 178 +#define CALL_STR_1 179 +#define CALL_TUPLE_1 180 +#define CALL_TYPE_1 181 +#define COMPARE_OP_FLOAT 182 +#define COMPARE_OP_INT 183 +#define COMPARE_OP_STR 184 +#define CONTAINS_OP_DICT 185 +#define CONTAINS_OP_SET 186 +#define FOR_ITER_GEN 187 +#define FOR_ITER_LIST 188 +#define FOR_ITER_RANGE 189 +#define FOR_ITER_TUPLE 190 +#define LOAD_ATTR_CLASS 191 +#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 192 +#define LOAD_ATTR_INSTANCE_VALUE 193 +#define LOAD_ATTR_METHOD_LAZY_DICT 194 +#define LOAD_ATTR_METHOD_NO_DICT 195 +#define LOAD_ATTR_METHOD_WITH_VALUES 196 +#define LOAD_ATTR_MODULE 197 +#define LOAD_ATTR_NONDESCRIPTOR_NO_DICT 198 +#define LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 199 +#define LOAD_ATTR_PROPERTY 200 +#define LOAD_ATTR_SLOT 201 +#define LOAD_ATTR_WITH_HINT 202 +#define LOAD_GLOBAL_BUILTIN 203 +#define LOAD_GLOBAL_MODULE 204 +#define LOAD_SUPER_ATTR_ATTR 205 +#define LOAD_SUPER_ATTR_METHOD 206 +#define RESUME_CHECK 207 +#define SEND_GEN 208 +#define STORE_ATTR_INSTANCE_VALUE 209 +#define STORE_ATTR_SLOT 210 +#define STORE_ATTR_WITH_HINT 211 +#define STORE_SUBSCR_DICT 212 +#define STORE_SUBSCR_LIST_INT 213 +#define TO_BOOL_ALWAYS_TRUE 214 +#define TO_BOOL_BOOL 215 +#define TO_BOOL_INT 216 +#define TO_BOOL_LIST 217 +#define TO_BOOL_NONE 218 +#define TO_BOOL_STR 219 +#define UNPACK_SEQUENCE_LIST 220 +#define UNPACK_SEQUENCE_TUPLE 221 +#define UNPACK_SEQUENCE_TWO_TUPLE 222 #define INSTRUMENTED_RESUME 236 #define INSTRUMENTED_END_FOR 237 #define INSTRUMENTED_END_SEND 238 diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index b5bafe6302bc9e..b3d7b8103e86c4 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -88,7 +88,6 @@ "CALL": [ "CALL_BOUND_METHOD_EXACT_ARGS", "CALL_PY_EXACT_ARGS", - "CALL_PY_WITH_DEFAULTS", "CALL_TYPE_1", "CALL_STR_1", "CALL_TUPLE_1", @@ -104,6 +103,9 @@ "CALL_METHOD_DESCRIPTOR_NOARGS", "CALL_METHOD_DESCRIPTOR_FAST", "CALL_ALLOC_AND_ENTER_INIT", + "CALL_PY_GENERAL", + "CALL_BOUND_METHOD_GENERAL", + "CALL_NON_PY_GENERAL", ], } @@ -123,63 +125,65 @@ 'BINARY_SUBSCR_TUPLE_INT': 161, 'CALL_ALLOC_AND_ENTER_INIT': 162, 'CALL_BOUND_METHOD_EXACT_ARGS': 163, - 'CALL_BUILTIN_CLASS': 164, - 'CALL_BUILTIN_FAST': 165, - 'CALL_BUILTIN_FAST_WITH_KEYWORDS': 166, - 'CALL_BUILTIN_O': 167, - 'CALL_ISINSTANCE': 168, - 'CALL_LEN': 169, - 'CALL_LIST_APPEND': 170, - 'CALL_METHOD_DESCRIPTOR_FAST': 171, - 'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 172, - 'CALL_METHOD_DESCRIPTOR_NOARGS': 173, - 'CALL_METHOD_DESCRIPTOR_O': 174, - 'CALL_PY_EXACT_ARGS': 175, - 'CALL_PY_WITH_DEFAULTS': 176, - 'CALL_STR_1': 177, - 'CALL_TUPLE_1': 178, - 'CALL_TYPE_1': 179, - 'COMPARE_OP_FLOAT': 180, - 'COMPARE_OP_INT': 181, - 'COMPARE_OP_STR': 182, - 'CONTAINS_OP_DICT': 183, - 'CONTAINS_OP_SET': 184, - 'FOR_ITER_GEN': 185, - 'FOR_ITER_LIST': 186, - 'FOR_ITER_RANGE': 187, - 'FOR_ITER_TUPLE': 188, - 'LOAD_ATTR_CLASS': 189, - 'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 190, - 'LOAD_ATTR_INSTANCE_VALUE': 191, - 'LOAD_ATTR_METHOD_LAZY_DICT': 192, - 'LOAD_ATTR_METHOD_NO_DICT': 193, - 'LOAD_ATTR_METHOD_WITH_VALUES': 194, - 'LOAD_ATTR_MODULE': 195, - 'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 196, - 'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 197, - 'LOAD_ATTR_PROPERTY': 198, - 'LOAD_ATTR_SLOT': 199, - 'LOAD_ATTR_WITH_HINT': 200, - 'LOAD_GLOBAL_BUILTIN': 201, - 'LOAD_GLOBAL_MODULE': 202, - 'LOAD_SUPER_ATTR_ATTR': 203, - 'LOAD_SUPER_ATTR_METHOD': 204, - 'RESUME_CHECK': 205, - 'SEND_GEN': 206, - 'STORE_ATTR_INSTANCE_VALUE': 207, - 'STORE_ATTR_SLOT': 208, - 'STORE_ATTR_WITH_HINT': 209, - 'STORE_SUBSCR_DICT': 210, - 'STORE_SUBSCR_LIST_INT': 211, - 'TO_BOOL_ALWAYS_TRUE': 212, - 'TO_BOOL_BOOL': 213, - 'TO_BOOL_INT': 214, - 'TO_BOOL_LIST': 215, - 'TO_BOOL_NONE': 216, - 'TO_BOOL_STR': 217, - 'UNPACK_SEQUENCE_LIST': 218, - 'UNPACK_SEQUENCE_TUPLE': 219, - 'UNPACK_SEQUENCE_TWO_TUPLE': 220, + 'CALL_BOUND_METHOD_GENERAL': 164, + 'CALL_BUILTIN_CLASS': 165, + 'CALL_BUILTIN_FAST': 166, + 'CALL_BUILTIN_FAST_WITH_KEYWORDS': 167, + 'CALL_BUILTIN_O': 168, + 'CALL_ISINSTANCE': 169, + 'CALL_LEN': 170, + 'CALL_LIST_APPEND': 171, + 'CALL_METHOD_DESCRIPTOR_FAST': 172, + 'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 173, + 'CALL_METHOD_DESCRIPTOR_NOARGS': 174, + 'CALL_METHOD_DESCRIPTOR_O': 175, + 'CALL_NON_PY_GENERAL': 176, + 'CALL_PY_EXACT_ARGS': 177, + 'CALL_PY_GENERAL': 178, + 'CALL_STR_1': 179, + 'CALL_TUPLE_1': 180, + 'CALL_TYPE_1': 181, + 'COMPARE_OP_FLOAT': 182, + 'COMPARE_OP_INT': 183, + 'COMPARE_OP_STR': 184, + 'CONTAINS_OP_DICT': 185, + 'CONTAINS_OP_SET': 186, + 'FOR_ITER_GEN': 187, + 'FOR_ITER_LIST': 188, + 'FOR_ITER_RANGE': 189, + 'FOR_ITER_TUPLE': 190, + 'LOAD_ATTR_CLASS': 191, + 'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 192, + 'LOAD_ATTR_INSTANCE_VALUE': 193, + 'LOAD_ATTR_METHOD_LAZY_DICT': 194, + 'LOAD_ATTR_METHOD_NO_DICT': 195, + 'LOAD_ATTR_METHOD_WITH_VALUES': 196, + 'LOAD_ATTR_MODULE': 197, + 'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 198, + 'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 199, + 'LOAD_ATTR_PROPERTY': 200, + 'LOAD_ATTR_SLOT': 201, + 'LOAD_ATTR_WITH_HINT': 202, + 'LOAD_GLOBAL_BUILTIN': 203, + 'LOAD_GLOBAL_MODULE': 204, + 'LOAD_SUPER_ATTR_ATTR': 205, + 'LOAD_SUPER_ATTR_METHOD': 206, + 'RESUME_CHECK': 207, + 'SEND_GEN': 208, + 'STORE_ATTR_INSTANCE_VALUE': 209, + 'STORE_ATTR_SLOT': 210, + 'STORE_ATTR_WITH_HINT': 211, + 'STORE_SUBSCR_DICT': 212, + 'STORE_SUBSCR_LIST_INT': 213, + 'TO_BOOL_ALWAYS_TRUE': 214, + 'TO_BOOL_BOOL': 215, + 'TO_BOOL_INT': 216, + 'TO_BOOL_LIST': 217, + 'TO_BOOL_NONE': 218, + 'TO_BOOL_STR': 219, + 'UNPACK_SEQUENCE_LIST': 220, + 'UNPACK_SEQUENCE_TUPLE': 221, + 'UNPACK_SEQUENCE_TWO_TUPLE': 222, } opmap = { diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index d3f4d6c29c5536..7ea27929138da3 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -46,11 +46,16 @@ def test_frames_are_popped_after_failed_calls(self): # recovering from failed calls: def f(): pass - for _ in range(1000): - try: - f(None) - except TypeError: + class C: + def m(self): pass + callables = [f, C.m, [].__len__] + for c in callables: + for _ in range(1000): + try: + c(None) + except TypeError: + pass # BOOM! diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 747a73829fa705..b68ed3baadc652 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -840,7 +840,7 @@ def loop_test(): %3d LOAD_GLOBAL_MODULE 1 (load_test + NULL) LOAD_FAST 0 (i) - CALL_PY_WITH_DEFAULTS 1 + CALL_PY_GENERAL 1 POP_TOP JUMP_BACKWARD 16 (to L1) diff --git a/Lib/test/test_dynamic.py b/Lib/test/test_dynamic.py index 0aa3be6a1bde6a..21bece26b893c6 100644 --- a/Lib/test/test_dynamic.py +++ b/Lib/test/test_dynamic.py @@ -4,7 +4,7 @@ import sys import unittest -from test.support import swap_item, swap_attr +from test.support import swap_item, swap_attr, is_wasi, Py_DEBUG class RebindBuiltinsTests(unittest.TestCase): @@ -134,6 +134,7 @@ def test_eval_gives_lambda_custom_globals(self): self.assertEqual(foo(), 7) + @unittest.skipIf(is_wasi and Py_DEBUG, "requires too much stack") def test_load_global_specialization_failure_keeps_oparg(self): # https://github.com/python/cpython/issues/91625 class MyGlobals(dict): diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 70ee35ed2850bc..b72640bd871ba6 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -6,6 +6,7 @@ import unittest import warnings +from test.support import is_wasi, Py_DEBUG from test.support.os_helper import (TESTFN, skip_unless_symlink, can_symlink, create_empty_file, change_cwd) @@ -366,6 +367,8 @@ def test_glob_named_pipe(self): self.assertEqual(self.rglob('mypipe', 'sub'), []) self.assertEqual(self.rglob('mypipe', '*'), []) + + @unittest.skipIf(is_wasi and Py_DEBUG, "requires too much stack") def test_glob_many_open_files(self): depth = 30 base = os.path.join(self.tempdir, 'deep') diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ddada96bea71b8..b2a0dc030e20cc 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3042,7 +3042,6 @@ dummy_func( family(CALL, INLINE_CACHE_ENTRIES_CALL) = { CALL_BOUND_METHOD_EXACT_ARGS, CALL_PY_EXACT_ARGS, - CALL_PY_WITH_DEFAULTS, CALL_TYPE_1, CALL_STR_1, CALL_TUPLE_1, @@ -3058,6 +3057,9 @@ dummy_func( CALL_METHOD_DESCRIPTOR_NOARGS, CALL_METHOD_DESCRIPTOR_FAST, CALL_ALLOC_AND_ENTER_INIT, + CALL_PY_GENERAL, + CALL_BOUND_METHOD_GENERAL, + CALL_NON_PY_GENERAL, }; specializing op(_SPECIALIZE_CALL, (counter/1, callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { @@ -3147,9 +3149,108 @@ dummy_func( macro(CALL) = _SPECIALIZE_CALL + unused/2 + _CALL + _CHECK_PERIODIC; + op(_PY_FRAME_GENERAL, (callable, self_or_null, args[oparg] -- new_frame: _PyInterpreterFrame*)) { + // oparg counts all of the args, but *not* self: + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + assert(Py_TYPE(callable) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); + new_frame = _PyEvalFramePushAndInit( + tstate, (PyFunctionObject *)callable, locals, + args, total_args, NULL + ); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + SYNC_SP(); + if (new_frame == NULL) { + ERROR_NO_POP(); + } + } + + op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + EXIT_IF(!PyFunction_Check(callable)); + PyFunctionObject *func = (PyFunctionObject *)callable; + EXIT_IF(func->func_version != func_version); + } + + macro(CALL_PY_GENERAL) = + unused/1 + // Skip over the counter + _CHECK_PEP_523 + + _CHECK_FUNCTION_VERSION + + _PY_FRAME_GENERAL + + _SAVE_RETURN_OFFSET + + _PUSH_FRAME; + + op(_CHECK_METHOD_VERSION, (func_version/2, callable, null, unused[oparg] -- callable, null, unused[oparg])) { + EXIT_IF(Py_TYPE(callable) != &PyMethod_Type); + PyObject *func = ((PyMethodObject *)callable)->im_func; + EXIT_IF(!PyFunction_Check(func)); + EXIT_IF(((PyFunctionObject *)func)->func_version != func_version); + EXIT_IF(null != NULL); + } + + op(_EXPAND_METHOD, (callable, null, unused[oparg] -- method, self, unused[oparg])) { + assert(null == NULL); + assert(Py_TYPE(callable) == &PyMethod_Type); + self = ((PyMethodObject *)callable)->im_self; + Py_INCREF(self); + stack_pointer[-1 - oparg] = self; // Patch stack as it is used by _PY_FRAME_GENERAL + method = ((PyMethodObject *)callable)->im_func; + assert(PyFunction_Check(method)); + Py_INCREF(method); + Py_DECREF(callable); + } + + macro(CALL_BOUND_METHOD_GENERAL) = + unused/1 + // Skip over the counter + _CHECK_PEP_523 + + _CHECK_METHOD_VERSION + + _EXPAND_METHOD + + _PY_FRAME_GENERAL + + _SAVE_RETURN_OFFSET + + _PUSH_FRAME; + + op(_CHECK_IS_NOT_PY_CALLABLE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + EXIT_IF(PyFunction_Check(callable)); + EXIT_IF(Py_TYPE(callable) == &PyMethod_Type); + } + + op(_CALL_NON_PY_GENERAL, (callable, self_or_null, args[oparg] -- res)) { +#if TIER_ONE + assert(opcode != INSTRUMENTED_CALL); +#endif + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + /* Callable is not a normal Python function */ + res = PyObject_Vectorcall( + callable, args, + total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + NULL); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(callable); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + ERROR_IF(res == NULL, error); + } + + macro(CALL_NON_PY_GENERAL) = + unused/1 + // Skip over the counter + unused/2 + + _CHECK_IS_NOT_PY_CALLABLE + + _CALL_NON_PY_GENERAL + + _CHECK_PERIODIC; + op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) { - DEOPT_IF(null != NULL); - DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type); + EXIT_IF(null != NULL); + EXIT_IF(Py_TYPE(callable) != &PyMethod_Type); } op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable, unused, unused[oparg] -- func, self, unused[oparg])) { @@ -3227,40 +3328,6 @@ dummy_func( _SAVE_RETURN_OFFSET + _PUSH_FRAME; - inst(CALL_PY_WITH_DEFAULTS, (unused/1, func_version/2, callable, self_or_null, args[oparg] -- unused)) { - DEOPT_IF(tstate->interp->eval_frame); - int argcount = oparg; - if (self_or_null != NULL) { - args--; - argcount++; - } - DEOPT_IF(!PyFunction_Check(callable)); - PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version); - PyCodeObject *code = (PyCodeObject *)func->func_code; - assert(func->func_defaults); - assert(PyTuple_CheckExact(func->func_defaults)); - int defcount = (int)PyTuple_GET_SIZE(func->func_defaults); - assert(defcount <= code->co_argcount); - int min_args = code->co_argcount - defcount; - DEOPT_IF(argcount > code->co_argcount); - DEOPT_IF(argcount < min_args); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); - STAT_INC(CALL, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); - for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = args[i]; - } - for (int i = argcount; i < code->co_argcount; i++) { - PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); - new_frame->localsplus[i] = Py_NewRef(def); - } - // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). - STACK_SHRINK(oparg + 2); - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); - } - inst(CALL_TYPE_1, (unused/1, unused/2, callable, null, arg -- res)) { assert(oparg == 1); DEOPT_IF(null != NULL); diff --git a/Python/ceval.c b/Python/ceval.c index 118746909902f3..3626ffbd02ff40 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -247,10 +247,6 @@ static PyObject * import_name(PyThreadState *, _PyInterpreterFrame *, static PyObject * import_from(PyThreadState *, PyObject *, PyObject *); static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg); static int get_exception_handler(PyCodeObject *, int, int*, int*, int*); -static _PyInterpreterFrame * -_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, - PyObject *locals, PyObject* const* args, - size_t argcount, PyObject *kwnames); static _PyInterpreterFrame * _PyEvalFramePushAndInit_Ex(PyThreadState *tstate, PyFunctionObject *func, PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs); @@ -1716,7 +1712,7 @@ _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame) } /* Consumes references to func, locals and all the args */ -static _PyInterpreterFrame * +_PyInterpreterFrame * _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames) @@ -1736,6 +1732,8 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, return frame; fail: /* Consume the references */ + Py_DECREF(func); + Py_XDECREF(locals); for (size_t i = 0; i < argcount; i++) { Py_DECREF(args[i]); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index d0b794c61ef4a8..5f15f67324292b 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -3032,6 +3032,153 @@ break; } + case _PY_FRAME_GENERAL: { + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + _PyInterpreterFrame *new_frame; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + // oparg counts all of the args, but *not* self: + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + assert(Py_TYPE(callable) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); + new_frame = _PyEvalFramePushAndInit( + tstate, (PyFunctionObject *)callable, locals, + args, total_args, NULL + ); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + stack_pointer += -2 - oparg; + if (new_frame == NULL) { + JUMP_TO_ERROR(); + } + stack_pointer[0] = (PyObject *)new_frame; + stack_pointer += 1; + break; + } + + case _CHECK_FUNCTION_VERSION: { + PyObject *callable; + oparg = CURRENT_OPARG(); + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = (uint32_t)CURRENT_OPERAND(); + if (!PyFunction_Check(callable)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + PyFunctionObject *func = (PyFunctionObject *)callable; + if (func->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + + case _CHECK_METHOD_VERSION: { + PyObject *null; + PyObject *callable; + oparg = CURRENT_OPARG(); + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = (uint32_t)CURRENT_OPERAND(); + if (Py_TYPE(callable) != &PyMethod_Type) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + PyObject *func = ((PyMethodObject *)callable)->im_func; + if (!PyFunction_Check(func)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + if (((PyFunctionObject *)func)->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + if (null != NULL) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + + case _EXPAND_METHOD: { + PyObject *null; + PyObject *callable; + PyObject *method; + PyObject *self; + oparg = CURRENT_OPARG(); + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(null == NULL); + assert(Py_TYPE(callable) == &PyMethod_Type); + self = ((PyMethodObject *)callable)->im_self; + Py_INCREF(self); + stack_pointer[-1 - oparg] = self; // Patch stack as it is used by _PY_FRAME_GENERAL + method = ((PyMethodObject *)callable)->im_func; + assert(PyFunction_Check(method)); + Py_INCREF(method); + Py_DECREF(callable); + stack_pointer[-2 - oparg] = method; + stack_pointer[-1 - oparg] = self; + break; + } + + case _CHECK_IS_NOT_PY_CALLABLE: { + PyObject *callable; + oparg = CURRENT_OPARG(); + callable = stack_pointer[-2 - oparg]; + if (PyFunction_Check(callable)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(callable) == &PyMethod_Type) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + + case _CALL_NON_PY_GENERAL: { + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + #if TIER_ONE + assert(opcode != INSTRUMENTED_CALL); + #endif + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + /* Callable is not a normal Python function */ + res = PyObject_Vectorcall( + callable, args, + total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + NULL); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(callable); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + if (res == NULL) JUMP_TO_ERROR(); + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + break; + } + case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { PyObject *null; PyObject *callable; @@ -3276,8 +3423,6 @@ break; } - /* _CALL_PY_WITH_DEFAULTS is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - case _CALL_TYPE_1: { PyObject *arg; PyObject *null; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 800d19229e3d6a..87098b0506522f 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1002,6 +1002,97 @@ DISPATCH(); } + TARGET(CALL_BOUND_METHOD_GENERAL) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BOUND_METHOD_GENERAL); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject *null; + PyObject *callable; + PyObject *method; + PyObject *self; + PyObject **args; + PyObject *self_or_null; + _PyInterpreterFrame *new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + DEOPT_IF(tstate->interp->eval_frame, CALL); + } + // _CHECK_METHOD_VERSION + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + { + uint32_t func_version = read_u32(&this_instr[2].cache); + DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); + PyObject *func = ((PyMethodObject *)callable)->im_func; + DEOPT_IF(!PyFunction_Check(func), CALL); + DEOPT_IF(((PyFunctionObject *)func)->func_version != func_version, CALL); + DEOPT_IF(null != NULL, CALL); + } + // _EXPAND_METHOD + { + assert(null == NULL); + assert(Py_TYPE(callable) == &PyMethod_Type); + self = ((PyMethodObject *)callable)->im_self; + Py_INCREF(self); + stack_pointer[-1 - oparg] = self; // Patch stack as it is used by _PY_FRAME_GENERAL + method = ((PyMethodObject *)callable)->im_func; + assert(PyFunction_Check(method)); + Py_INCREF(method); + Py_DECREF(callable); + } + // _PY_FRAME_GENERAL + args = &stack_pointer[-oparg]; + self_or_null = self; + callable = method; + { + // oparg counts all of the args, but *not* self: + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + assert(Py_TYPE(callable) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); + new_frame = _PyEvalFramePushAndInit( + tstate, (PyFunctionObject *)callable, locals, + args, total_args, NULL + ); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + stack_pointer += -2 - oparg; + if (new_frame == NULL) { + goto error; + } + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + // Write it out explicitly because it's subtly different. + // Eventually this should be the only occurrence of this code. + assert(tstate->interp->eval_frame == NULL); + _PyFrame_SetStackPointer(frame, stack_pointer); + new_frame->previous = frame; + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = new_frame; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + TARGET(CALL_BUILTIN_CLASS) { frame->instr_ptr = next_instr; next_instr += 4; @@ -1713,6 +1804,56 @@ DISPATCH(); } + TARGET(CALL_NON_PY_GENERAL) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_NON_PY_GENERAL); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject *callable; + PyObject **args; + PyObject *self_or_null; + PyObject *res; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _CHECK_IS_NOT_PY_CALLABLE + callable = stack_pointer[-2 - oparg]; + { + DEOPT_IF(PyFunction_Check(callable), CALL); + DEOPT_IF(Py_TYPE(callable) == &PyMethod_Type, CALL); + } + // _CALL_NON_PY_GENERAL + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + { + #if TIER_ONE + assert(opcode != INSTRUMENTED_CALL); + #endif + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + /* Callable is not a normal Python function */ + res = PyObject_Vectorcall( + callable, args, + total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + NULL); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(callable); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + } + // _CHECK_PERIODIC + { + } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); + DISPATCH(); + } + TARGET(CALL_PY_EXACT_ARGS) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 4; @@ -1786,50 +1927,76 @@ DISPATCH(); } - TARGET(CALL_PY_WITH_DEFAULTS) { + TARGET(CALL_PY_GENERAL) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 4; - INSTRUCTION_STATS(CALL_PY_WITH_DEFAULTS); + INSTRUCTION_STATS(CALL_PY_GENERAL); static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject *callable; PyObject **args; PyObject *self_or_null; - PyObject *callable; + _PyInterpreterFrame *new_frame; /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + DEOPT_IF(tstate->interp->eval_frame, CALL); + } + // _CHECK_FUNCTION_VERSION + callable = stack_pointer[-2 - oparg]; + { + uint32_t func_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyFunction_Check(callable), CALL); + PyFunctionObject *func = (PyFunctionObject *)callable; + DEOPT_IF(func->func_version != func_version, CALL); + } + // _PY_FRAME_GENERAL args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - uint32_t func_version = read_u32(&this_instr[2].cache); - DEOPT_IF(tstate->interp->eval_frame, CALL); - int argcount = oparg; - if (self_or_null != NULL) { - args--; - argcount++; - } - DEOPT_IF(!PyFunction_Check(callable), CALL); - PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version, CALL); - PyCodeObject *code = (PyCodeObject *)func->func_code; - assert(func->func_defaults); - assert(PyTuple_CheckExact(func->func_defaults)); - int defcount = (int)PyTuple_GET_SIZE(func->func_defaults); - assert(defcount <= code->co_argcount); - int min_args = code->co_argcount - defcount; - DEOPT_IF(argcount > code->co_argcount, CALL); - DEOPT_IF(argcount < min_args, CALL); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - STAT_INC(CALL, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); - for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = args[i]; + { + // oparg counts all of the args, but *not* self: + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + assert(Py_TYPE(callable) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); + new_frame = _PyEvalFramePushAndInit( + tstate, (PyFunctionObject *)callable, locals, + args, total_args, NULL + ); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + stack_pointer += -2 - oparg; + if (new_frame == NULL) { + goto error; + } } - for (int i = argcount; i < code->co_argcount; i++) { - PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); - new_frame->localsplus[i] = Py_NewRef(def); + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif } - // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). - STACK_SHRINK(oparg + 2); - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + // _PUSH_FRAME + { + // Write it out explicitly because it's subtly different. + // Eventually this should be the only occurrence of this code. + assert(tstate->interp->eval_frame == NULL); + _PyFrame_SetStackPointer(frame, stack_pointer); + new_frame->previous = frame; + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = new_frame; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); } TARGET(CALL_STR_1) { diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 4061ba33cea53e..fa4f1f8cbb475a 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -163,6 +163,7 @@ static void *opcode_targets[256] = { &&TARGET_BINARY_SUBSCR_TUPLE_INT, &&TARGET_CALL_ALLOC_AND_ENTER_INIT, &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS, + &&TARGET_CALL_BOUND_METHOD_GENERAL, &&TARGET_CALL_BUILTIN_CLASS, &&TARGET_CALL_BUILTIN_FAST, &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS, @@ -174,8 +175,9 @@ static void *opcode_targets[256] = { &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, &&TARGET_CALL_METHOD_DESCRIPTOR_NOARGS, &&TARGET_CALL_METHOD_DESCRIPTOR_O, + &&TARGET_CALL_NON_PY_GENERAL, &&TARGET_CALL_PY_EXACT_ARGS, - &&TARGET_CALL_PY_WITH_DEFAULTS, + &&TARGET_CALL_PY_GENERAL, &&TARGET_CALL_STR_1, &&TARGET_CALL_TUPLE_1, &&TARGET_CALL_TYPE_1, @@ -233,8 +235,6 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_INSTRUMENTED_RESUME, &&TARGET_INSTRUMENTED_END_FOR, &&TARGET_INSTRUMENTED_END_SEND, diff --git a/Python/optimizer.c b/Python/optimizer.c index c0e1be96353d3f..8be2c0ffbd78e9 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -987,6 +987,7 @@ static void make_exit(_PyUOpInstruction *inst, int opcode, int target) { inst->opcode = opcode; inst->oparg = 0; + inst->operand = 0; inst->format = UOP_FORMAT_TARGET; inst->target = target; } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 60763286178c71..928bc03382b8fb 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -629,6 +629,15 @@ dummy_func(void) { frame_new(ctx, co, localsplus_start, n_locals_already_filled, 0)); } + op(_PY_FRAME_GENERAL, (callable, self_or_null, args[oparg] -- new_frame: _Py_UOpsAbstractFrame *)) { + /* The _Py_UOpsAbstractFrame design assumes that we can copy arguments across directly */ + (void)callable; + (void)self_or_null; + (void)args; + first_valid_check_stack = NULL; + goto done; + } + op(_POP_FRAME, (retval -- res)) { SYNC_SP(); ctx->frame->stack_pointer = stack_pointer; @@ -718,7 +727,7 @@ dummy_func(void) { if (first_valid_check_stack == NULL) { first_valid_check_stack = corresponding_check_stack; } - else { + else if (corresponding_check_stack) { // delete all but the first valid _CHECK_STACK_SPACE corresponding_check_stack->opcode = _NOP; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index b602d663b08ba6..2a4efd73d794df 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1559,6 +1559,58 @@ break; } + case _PY_FRAME_GENERAL: { + _Py_UopsSymbol **args; + _Py_UopsSymbol *self_or_null; + _Py_UopsSymbol *callable; + _Py_UOpsAbstractFrame *new_frame; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* The _Py_UOpsAbstractFrame design assumes that we can copy arguments across directly */ + (void)callable; + (void)self_or_null; + (void)args; + first_valid_check_stack = NULL; + goto done; + stack_pointer[-2 - oparg] = (_Py_UopsSymbol *)new_frame; + stack_pointer += -1 - oparg; + break; + } + + case _CHECK_FUNCTION_VERSION: { + break; + } + + case _CHECK_METHOD_VERSION: { + break; + } + + case _EXPAND_METHOD: { + _Py_UopsSymbol *method; + _Py_UopsSymbol *self; + method = sym_new_not_null(ctx); + if (method == NULL) goto out_of_space; + self = sym_new_not_null(ctx); + if (self == NULL) goto out_of_space; + stack_pointer[-2 - oparg] = method; + stack_pointer[-1 - oparg] = self; + break; + } + + case _CHECK_IS_NOT_PY_CALLABLE: { + break; + } + + case _CALL_NON_PY_GENERAL: { + _Py_UopsSymbol *res; + res = sym_new_not_null(ctx); + if (res == NULL) goto out_of_space; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + break; + } + case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { _Py_UopsSymbol *null; _Py_UopsSymbol *callable; @@ -1692,7 +1744,7 @@ if (first_valid_check_stack == NULL) { first_valid_check_stack = corresponding_check_stack; } - else { + else if (corresponding_check_stack) { // delete all but the first valid _CHECK_STACK_SPACE corresponding_check_stack->opcode = _NOP; } @@ -1700,8 +1752,6 @@ break; } - /* _CALL_PY_WITH_DEFAULTS is not a viable micro-op for tier 2 */ - case _CALL_TYPE_1: { _Py_UopsSymbol *res; res = sym_new_not_null(ctx); diff --git a/Python/specialize.c b/Python/specialize.c index 72114f27f69c52..9ac428c3593f56 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1789,8 +1789,7 @@ specialize_class_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) return -1; } if (Py_TYPE(tp) != &PyType_Type) { - SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_METACLASS); - return -1; + goto generic; } if (tp->tp_new == PyBaseObject_Type.tp_new) { PyFunctionObject *init = get_init_for_simple_managed_python_class(tp); @@ -1807,58 +1806,11 @@ specialize_class_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) _Py_SET_OPCODE(*instr, CALL_ALLOC_AND_ENTER_INIT); return 0; } - return -1; - } - SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_CLASS_MUTABLE); - return -1; -} - -#ifdef Py_STATS -static int -builtin_call_fail_kind(int ml_flags) -{ - switch (ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | - METH_KEYWORDS | METH_METHOD)) { - case METH_VARARGS: - return SPEC_FAIL_CALL_CFUNC_VARARGS; - case METH_VARARGS | METH_KEYWORDS: - return SPEC_FAIL_CALL_CFUNC_VARARGS_KEYWORDS; - case METH_NOARGS: - return SPEC_FAIL_CALL_CFUNC_NOARGS; - case METH_METHOD | METH_FASTCALL | METH_KEYWORDS: - return SPEC_FAIL_CALL_CFUNC_METHOD_FASTCALL_KEYWORDS; - /* These cases should be optimized, but return "other" just in case */ - case METH_O: - case METH_FASTCALL: - case METH_FASTCALL | METH_KEYWORDS: - return SPEC_FAIL_OTHER; - default: - return SPEC_FAIL_CALL_BAD_CALL_FLAGS; - } -} - -static int -meth_descr_call_fail_kind(int ml_flags) -{ - switch (ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | - METH_KEYWORDS | METH_METHOD)) { - case METH_VARARGS: - return SPEC_FAIL_CALL_METH_DESCR_VARARGS; - case METH_VARARGS | METH_KEYWORDS: - return SPEC_FAIL_CALL_METH_DESCR_VARARGS_KEYWORDS; - case METH_METHOD | METH_FASTCALL | METH_KEYWORDS: - return SPEC_FAIL_CALL_METH_DESCR_METHOD_FASTCALL_KEYWORDS; - /* These cases should be optimized, but return "other" just in case */ - case METH_NOARGS: - case METH_O: - case METH_FASTCALL: - case METH_FASTCALL | METH_KEYWORDS: - return SPEC_FAIL_OTHER; - default: - return SPEC_FAIL_CALL_BAD_CALL_FLAGS; } +generic: + instr->op.code = CALL_NON_PY_GENERAL; + return 0; } -#endif // Py_STATS static int specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr, @@ -1901,8 +1853,8 @@ specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr, return 0; } } - SPECIALIZATION_FAIL(CALL, meth_descr_call_fail_kind(descr->d_method->ml_flags)); - return -1; + instr->op.code = CALL_NON_PY_GENERAL; + return 0; } static int @@ -1917,36 +1869,25 @@ specialize_py_call(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs, SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_PEP_523); return -1; } - if (kind != SIMPLE_FUNCTION) { - SPECIALIZATION_FAIL(CALL, kind); + int argcount = -1; + if (kind == SPEC_FAIL_CODE_NOT_OPTIMIZED) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CODE_NOT_OPTIMIZED); return -1; } - int argcount = code->co_argcount; - int defcount = func->func_defaults == NULL ? 0 : (int)PyTuple_GET_SIZE(func->func_defaults); - int min_args = argcount-defcount; - // GH-105840: min_args is negative when somebody sets too many __defaults__! - if (min_args < 0 || nargs > argcount || nargs < min_args) { - SPECIALIZATION_FAIL(CALL, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS); - return -1; + if (kind == SIMPLE_FUNCTION) { + argcount = code->co_argcount; } - assert(nargs <= argcount && nargs >= min_args); - assert(min_args >= 0 && defcount >= 0); - assert(defcount == 0 || func->func_defaults != NULL); int version = _PyFunction_GetVersionForCurrentState(func); if (version == 0) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_OUT_OF_VERSIONS); return -1; } write_u32(cache->func_version, version); - if (argcount == nargs) { + if (argcount == nargs + bound_method) { instr->op.code = bound_method ? CALL_BOUND_METHOD_EXACT_ARGS : CALL_PY_EXACT_ARGS; } - else if (bound_method) { - SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_BOUND_METHOD); - return -1; - } else { - instr->op.code = CALL_PY_WITH_DEFAULTS; + instr->op.code = bound_method ? CALL_BOUND_METHOD_GENERAL : CALL_PY_GENERAL; } return 0; } @@ -1955,6 +1896,7 @@ static int specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) { if (PyCFunction_GET_FUNCTION(callable) == NULL) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_OTHER); return 1; } switch (PyCFunction_GET_FLAGS(callable) & @@ -1991,38 +1933,10 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) return 0; } default: - SPECIALIZATION_FAIL(CALL, - builtin_call_fail_kind(PyCFunction_GET_FLAGS(callable))); - return 1; - } -} - -#ifdef Py_STATS -static int -call_fail_kind(PyObject *callable) -{ - assert(!PyCFunction_CheckExact(callable)); - assert(!PyFunction_Check(callable)); - assert(!PyType_Check(callable)); - assert(!Py_IS_TYPE(callable, &PyMethodDescr_Type)); - assert(!PyMethod_Check(callable)); - if (PyInstanceMethod_Check(callable)) { - return SPEC_FAIL_CALL_INSTANCE_METHOD; - } - // builtin method - else if (PyCMethod_Check(callable)) { - return SPEC_FAIL_CALL_CMETHOD; - } - else if (Py_TYPE(callable) == &PyWrapperDescr_Type) { - return SPEC_FAIL_CALL_OPERATOR_WRAPPER; - } - else if (Py_TYPE(callable) == &_PyMethodWrapper_Type) { - return SPEC_FAIL_CALL_METHOD_WRAPPER; + instr->op.code = CALL_NON_PY_GENERAL; + return 0; } - return SPEC_FAIL_OTHER; } -#endif // Py_STATS - void _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) @@ -2047,7 +1961,7 @@ _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) else if (PyMethod_Check(callable)) { PyObject *func = ((PyMethodObject *)callable)->im_func; if (PyFunction_Check(func)) { - fail = specialize_py_call((PyFunctionObject *)func, instr, nargs+1, true); + fail = specialize_py_call((PyFunctionObject *)func, instr, nargs, true); } else { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_BOUND_METHOD); @@ -2055,8 +1969,8 @@ _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) } } else { - SPECIALIZATION_FAIL(CALL, call_fail_kind(callable)); - fail = -1; + instr->op.code = CALL_NON_PY_GENERAL; + fail = 0; } if (fail) { STAT_INC(CALL, failure); From b034f14a4b6e9197d3926046721b8b4b4b4f5b3d Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Sat, 4 May 2024 04:12:10 -0700 Subject: [PATCH 206/217] gh-74929: Implement PEP 667 (GH-115153) --- Doc/data/stable_abi.dat | 3 + Include/ceval.h | 4 + Include/cpython/frameobject.h | 6 + Include/cpython/pyframe.h | 2 + Include/internal/pycore_frame.h | 13 +- Lib/test/test_frame.py | 186 +++- Lib/test/test_listcomps.py | 7 +- Lib/test/test_peepholer.py | 17 - Lib/test/test_stable_abi_ctypes.py | 3 + Lib/test/test_sys.py | 2 +- ...4-04-27-21-44-40.gh-issue-74929.C2nESp.rst | 1 + Misc/stable_abi.toml | 6 + Objects/frameobject.c | 860 ++++++++++++++---- Objects/object.c | 1 + PC/python3dll.c | 3 + Python/ceval.c | 48 +- Python/intrinsics.c | 7 +- Python/sysmodule.c | 8 - Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 + 19 files changed, 921 insertions(+), 257 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-27-21-44-40.gh-issue-74929.C2nESp.rst diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 8c8a378f52bd5d..76a035f194d911 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -188,6 +188,9 @@ function,PyEval_EvalFrame,3.2,, function,PyEval_EvalFrameEx,3.2,, function,PyEval_GetBuiltins,3.2,, function,PyEval_GetFrame,3.2,, +function,PyEval_GetFrameBuiltins,3.13,, +function,PyEval_GetFrameGlobals,3.13,, +function,PyEval_GetFrameLocals,3.13,, function,PyEval_GetFuncDesc,3.2,, function,PyEval_GetFuncName,3.2,, function,PyEval_GetGlobals,3.2,, diff --git a/Include/ceval.h b/Include/ceval.h index 8ea9da8d134ee0..1ec746c3708220 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -22,6 +22,10 @@ PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void); PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); PyAPI_FUNC(PyFrameObject *) PyEval_GetFrame(void); +PyAPI_FUNC(PyObject *) PyEval_GetFrameBuiltins(void); +PyAPI_FUNC(PyObject *) PyEval_GetFrameGlobals(void); +PyAPI_FUNC(PyObject *) PyEval_GetFrameLocals(void); + PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg); PyAPI_FUNC(int) Py_MakePendingCalls(void); diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 4e19535c656f2c..dbbfbb5105ba7a 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -27,3 +27,9 @@ PyAPI_FUNC(int) _PyFrame_IsEntryFrame(PyFrameObject *frame); PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f); PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); + + +typedef struct { + PyObject_HEAD + PyFrameObject* frame; +} PyFrameLocalsProxyObject; diff --git a/Include/cpython/pyframe.h b/Include/cpython/pyframe.h index c5adbbe4868f69..eeafbb17a56bad 100644 --- a/Include/cpython/pyframe.h +++ b/Include/cpython/pyframe.h @@ -3,8 +3,10 @@ #endif PyAPI_DATA(PyTypeObject) PyFrame_Type; +PyAPI_DATA(PyTypeObject) PyFrameLocalsProxy_Type; #define PyFrame_Check(op) Py_IS_TYPE((op), &PyFrame_Type) +#define PyFrameLocalsProxy_Check(op) Py_IS_TYPE((op), &PyFrameLocalsProxy_Type) PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); PyAPI_FUNC(PyObject *) PyFrame_GetLocals(PyFrameObject *frame); diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index e13fdd9bb2e01c..994900c007f4bd 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -25,7 +25,7 @@ struct _frame { int f_lineno; /* Current line number. Only valid if non-zero */ char f_trace_lines; /* Emit per-line trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */ - char f_fast_as_locals; /* Have the fast locals of this frame been converted to a dict? */ + PyObject *f_extra_locals; /* Dict for locals set by users using f_locals, could be NULL */ /* The frame data, if this frame object owns the frame */ PyObject *_f_frame_data[1]; }; @@ -245,14 +245,11 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame * frame); int _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg); -PyObject * -_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden); - -int -_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame); +bool +_PyFrame_HasHiddenLocals(_PyInterpreterFrame *frame); -void -_PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear); +PyObject * +_PyFrame_GetLocals(_PyInterpreterFrame *frame); static inline bool _PyThreadState_HasStackSpace(PyThreadState *tstate, int size) diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 8e744a1223e86f..93d0ea839d16eb 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -1,3 +1,4 @@ +import copy import gc import operator import re @@ -13,7 +14,7 @@ _testcapi = None from test import support -from test.support import threading_helper, Py_GIL_DISABLED +from test.support import import_helper, threading_helper, Py_GIL_DISABLED from test.support.script_helper import assert_python_ok @@ -198,14 +199,6 @@ def inner(): tb = tb.tb_next return frames - def test_locals(self): - f, outer, inner = self.make_frames() - outer_locals = outer.f_locals - self.assertIsInstance(outer_locals.pop('inner'), types.FunctionType) - self.assertEqual(outer_locals, {'x': 5, 'y': 6}) - inner_locals = inner.f_locals - self.assertEqual(inner_locals, {'x': 5, 'z': 7}) - def test_clear_locals(self): # Test f_locals after clear() (issue #21897) f, outer, inner = self.make_frames() @@ -217,8 +210,8 @@ def test_clear_locals(self): def test_locals_clear_locals(self): # Test f_locals before and after clear() (to exercise caching) f, outer, inner = self.make_frames() - outer.f_locals - inner.f_locals + self.assertNotEqual(outer.f_locals, {}) + self.assertNotEqual(inner.f_locals, {}) outer.clear() inner.clear() self.assertEqual(outer.f_locals, {}) @@ -269,6 +262,177 @@ def inner(): r"^$" % (file_repr, offset + 5)) +class TestFrameLocals(unittest.TestCase): + def test_scope(self): + class A: + x = 1 + sys._getframe().f_locals['x'] = 2 + sys._getframe().f_locals['y'] = 2 + + self.assertEqual(A.x, 2) + self.assertEqual(A.y, 2) + + def f(): + x = 1 + sys._getframe().f_locals['x'] = 2 + sys._getframe().f_locals['y'] = 2 + self.assertEqual(x, 2) + self.assertEqual(locals()['y'], 2) + f() + + def test_closure(self): + x = 1 + y = 2 + + def f(): + z = x + y + d = sys._getframe().f_locals + self.assertEqual(d['x'], 1) + self.assertEqual(d['y'], 2) + d['x'] = 2 + d['y'] = 3 + + f() + self.assertEqual(x, 2) + self.assertEqual(y, 3) + + def test_as_dict(self): + x = 1 + y = 2 + d = sys._getframe().f_locals + # self, x, y, d + self.assertEqual(len(d), 4) + self.assertIs(d['d'], d) + self.assertEqual(set(d.keys()), set(['x', 'y', 'd', 'self'])) + self.assertEqual(len(d.values()), 4) + self.assertIn(1, d.values()) + self.assertEqual(len(d.items()), 4) + self.assertIn(('x', 1), d.items()) + self.assertEqual(d.__getitem__('x'), 1) + d.__setitem__('x', 2) + self.assertEqual(d['x'], 2) + self.assertEqual(d.get('x'), 2) + self.assertIs(d.get('non_exist', None), None) + self.assertEqual(d.__len__(), 4) + self.assertEqual(set([key for key in d]), set(['x', 'y', 'd', 'self'])) + self.assertIn('x', d) + self.assertTrue(d.__contains__('x')) + + self.assertEqual(reversed(d), list(reversed(d.keys()))) + + d.update({'x': 3, 'z': 4}) + self.assertEqual(d['x'], 3) + self.assertEqual(d['z'], 4) + + with self.assertRaises(TypeError): + d.update([1, 2]) + + self.assertEqual(d.setdefault('x', 5), 3) + self.assertEqual(d.setdefault('new', 5), 5) + self.assertEqual(d['new'], 5) + + with self.assertRaises(KeyError): + d['non_exist'] + + def test_as_number(self): + x = 1 + y = 2 + d = sys._getframe().f_locals + self.assertIn('z', d | {'z': 3}) + d |= {'z': 3} + self.assertEqual(d['z'], 3) + d |= {'y': 3} + self.assertEqual(d['y'], 3) + with self.assertRaises(TypeError): + d |= 3 + with self.assertRaises(TypeError): + _ = d | [3] + + def test_non_string_key(self): + d = sys._getframe().f_locals + d[1] = 2 + self.assertEqual(d[1], 2) + + def test_write_with_hidden(self): + def f(): + f_locals = [sys._getframe().f_locals for b in [0]][0] + f_locals['b'] = 2 + f_locals['c'] = 3 + self.assertEqual(b, 2) + self.assertEqual(c, 3) + b = 0 + c = 0 + f() + + def test_repr(self): + x = 1 + # Introduce a reference cycle + frame = sys._getframe() + self.assertEqual(repr(frame.f_locals), repr(dict(frame.f_locals))) + + def test_delete(self): + x = 1 + d = sys._getframe().f_locals + with self.assertRaises(TypeError): + del d['x'] + + with self.assertRaises(AttributeError): + d.clear() + + with self.assertRaises(AttributeError): + d.pop('x') + + @support.cpython_only + def test_sizeof(self): + proxy = sys._getframe().f_locals + support.check_sizeof(self, proxy, support.calcobjsize("P")) + + def test_unsupport(self): + x = 1 + d = sys._getframe().f_locals + with self.assertRaises(AttributeError): + d.copy() + + with self.assertRaises(TypeError): + copy.copy(d) + + with self.assertRaises(TypeError): + copy.deepcopy(d) + + +class TestFrameCApi(unittest.TestCase): + def test_basic(self): + x = 1 + ctypes = import_helper.import_module('ctypes') + PyEval_GetFrameLocals = ctypes.pythonapi.PyEval_GetFrameLocals + PyEval_GetFrameLocals.restype = ctypes.py_object + frame_locals = PyEval_GetFrameLocals() + self.assertTrue(type(frame_locals), dict) + self.assertEqual(frame_locals['x'], 1) + frame_locals['x'] = 2 + self.assertEqual(x, 1) + + PyEval_GetFrameGlobals = ctypes.pythonapi.PyEval_GetFrameGlobals + PyEval_GetFrameGlobals.restype = ctypes.py_object + frame_globals = PyEval_GetFrameGlobals() + self.assertTrue(type(frame_globals), dict) + self.assertIs(frame_globals, globals()) + + PyEval_GetFrameBuiltins = ctypes.pythonapi.PyEval_GetFrameBuiltins + PyEval_GetFrameBuiltins.restype = ctypes.py_object + frame_builtins = PyEval_GetFrameBuiltins() + self.assertEqual(frame_builtins, __builtins__) + + PyFrame_GetLocals = ctypes.pythonapi.PyFrame_GetLocals + PyFrame_GetLocals.argtypes = [ctypes.py_object] + PyFrame_GetLocals.restype = ctypes.py_object + frame = sys._getframe() + f_locals = PyFrame_GetLocals(frame) + self.assertTrue(f_locals['x'], 1) + f_locals['x'] = 2 + self.assertEqual(x, 2) + + class TestIncompleteFrameAreInvisible(unittest.TestCase): def test_issue95818(self): diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py index df1debf35210ca..ec2aac81682db8 100644 --- a/Lib/test/test_listcomps.py +++ b/Lib/test/test_listcomps.py @@ -622,9 +622,14 @@ def test_exception_in_post_comp_call(self): def test_frame_locals(self): code = """ - val = [sys._getframe().f_locals for a in [0]][0]["a"] + val = "a" in [sys._getframe().f_locals for a in [0]][0] """ import sys + self._check_in_scopes(code, {"val": False}, ns={"sys": sys}) + + code = """ + val = [sys._getframe().f_locals["a"] for a in [0]][0] + """ self._check_in_scopes(code, {"val": 0}, ns={"sys": sys}) def _recursive_replace(self, maybe_code): diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 6989aafd293138..6c27ee4db97af3 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -933,23 +933,6 @@ def f(): self.assertNotInBytecode(f, "LOAD_FAST_CHECK") return f - def test_deleting_local_warns_and_assigns_none(self): - f = self.make_function_with_no_checks() - co_code = f.__code__.co_code - def trace(frame, event, arg): - if event == 'line' and frame.f_lineno == 4: - del frame.f_locals["x"] - sys.settrace(None) - return None - return trace - e = r"assigning None to unbound local 'x'" - with self.assertWarnsRegex(RuntimeWarning, e): - sys.settrace(trace) - f() - self.assertInBytecode(f, "LOAD_FAST") - self.assertNotInBytecode(f, "LOAD_FAST_CHECK") - self.assertEqual(f.__code__.co_code, co_code) - def test_modifying_local_does_not_add_check(self): f = self.make_function_with_no_checks() def trace(frame, event, arg): diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index d22698168615e2..c06c285c5013a6 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -227,6 +227,9 @@ def test_windows_feature_macros(self): "PyEval_EvalFrameEx", "PyEval_GetBuiltins", "PyEval_GetFrame", + "PyEval_GetFrameBuiltins", + "PyEval_GetFrameGlobals", + "PyEval_GetFrameLocals", "PyEval_GetFuncDesc", "PyEval_GetFuncName", "PyEval_GetGlobals", diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 73912767ae25b7..944e84e88c8a35 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1561,7 +1561,7 @@ class C(object): pass def func(): return sys._getframe() x = func() - check(x, size('3Pi3c7P2ic??2P')) + check(x, size('3Pi2cP7P2ic??2P')) # function def func(): pass check(func, size('15Pi')) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-27-21-44-40.gh-issue-74929.C2nESp.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-27-21-44-40.gh-issue-74929.C2nESp.rst new file mode 100644 index 00000000000000..46e628f7fa7dbe --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-27-21-44-40.gh-issue-74929.C2nESp.rst @@ -0,0 +1 @@ +Implement PEP 667 - converted ``frame.f_locals`` to a write through proxy diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 5c29e98705aeaf..77473662aaa76c 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2501,3 +2501,9 @@ added = '3.13' [function.PyType_GetModuleByDef] added = '3.13' +[function.PyEval_GetFrameBuiltins] + added = '3.13' +[function.PyEval_GetFrameGlobals] + added = '3.13' +[function.PyEval_GetFrameLocals] + added = '3.13' diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 36538b1f6d53fe..8030ecb6853674 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -16,12 +16,633 @@ #define OFF(x) offsetof(PyFrameObject, x) + +// Returns borrowed reference or NULL +static PyObject * +framelocalsproxy_getval(_PyInterpreterFrame *frame, PyCodeObject *co, int i) +{ + PyObject **fast = _PyFrame_GetLocalsArray(frame); + _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); + + PyObject *value = fast[i]; + PyObject *cell = NULL; + + if (value == NULL) { + return NULL; + } + + if (kind == CO_FAST_FREE || kind & CO_FAST_CELL) { + // The cell was set when the frame was created from + // the function's closure. + assert(PyCell_Check(value)); + cell = value; + } + + if (cell != NULL) { + value = PyCell_GET(cell); + } + + if (value == NULL) { + return NULL; + } + + return value; +} + +static int +framelocalsproxy_getkeyindex(PyFrameObject *frame, PyObject* key, bool read) +{ + /* + * Returns the fast locals index of the key + * - if read == true, returns the index if the value is not NULL + * - if read == false, returns the index if the value is not hidden + */ + + assert(PyUnicode_CheckExact(key)); + + PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); + int found_key = false; + + // We do 2 loops here because it's highly possible the key is interned + // and we can do a pointer comparison. + for (int i = 0; i < co->co_nlocalsplus; i++) { + PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); + if (name == key) { + found_key = true; + if (read) { + if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) { + return i; + } + } else { + if (!(_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_HIDDEN)) { + return i; + } + } + } + } + + if (!found_key) { + // This is unlikely, but we need to make sure. This means the key + // is not interned. + for (int i = 0; i < co->co_nlocalsplus; i++) { + PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); + if (_PyUnicode_EQ(name, key)) { + if (read) { + if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) { + return i; + } + } else { + if (!(_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_HIDDEN)) { + return i; + } + } + } + } + } + + return -1; +} + +static PyObject * +framelocalsproxy_getitem(PyObject *self, PyObject *key) +{ + PyFrameObject* frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyCodeObject* co = _PyFrame_GetCode(frame->f_frame); + + if (PyUnicode_CheckExact(key)) { + int i = framelocalsproxy_getkeyindex(frame, key, true); + if (i >= 0) { + PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i); + assert(value != NULL); + return Py_NewRef(value); + } + } + + // Okay not in the fast locals, try extra locals + + PyObject *extra = frame->f_extra_locals; + if (extra != NULL) { + PyObject *value = PyDict_GetItem(extra, key); + if (value != NULL) { + return Py_NewRef(value); + } + } + + PyErr_Format(PyExc_KeyError, "local variable '%R' is not defined", key); + return NULL; +} + +static int +framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value) +{ + /* Merge locals into fast locals */ + PyFrameObject* frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyObject** fast = _PyFrame_GetLocalsArray(frame->f_frame); + PyCodeObject* co = _PyFrame_GetCode(frame->f_frame); + + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "cannot remove variables from FrameLocalsProxy"); + return -1; + } + + if (PyUnicode_CheckExact(key)) { + int i = framelocalsproxy_getkeyindex(frame, key, false); + if (i >= 0) { + _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); + + PyObject *oldvalue = fast[i]; + PyObject *cell = NULL; + if (kind == CO_FAST_FREE) { + // The cell was set when the frame was created from + // the function's closure. + assert(oldvalue != NULL && PyCell_Check(oldvalue)); + cell = oldvalue; + } else if (kind & CO_FAST_CELL && oldvalue != NULL) { + if (PyCell_Check(oldvalue)) { + cell = oldvalue; + } + } + if (cell != NULL) { + oldvalue = PyCell_GET(cell); + if (value != oldvalue) { + PyCell_SET(cell, Py_XNewRef(value)); + Py_XDECREF(oldvalue); + } + } else if (value != oldvalue) { + Py_XSETREF(fast[i], Py_NewRef(value)); + } + Py_XDECREF(value); + return 0; + } + } + + // Okay not in the fast locals, try extra locals + + PyObject *extra = frame->f_extra_locals; + + if (extra == NULL) { + extra = PyDict_New(); + if (extra == NULL) { + return -1; + } + frame->f_extra_locals = extra; + } + + assert(PyDict_Check(extra)); + + return PyDict_SetItem(extra, key, value) < 0; +} + +static int +framelocalsproxy_merge(PyObject* self, PyObject* other) +{ + if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) { + return -1; + } + + PyObject *keys = PyMapping_Keys(other); + PyObject *iter = NULL; + PyObject *key = NULL; + PyObject *value = NULL; + + assert(keys != NULL); + + iter = PyObject_GetIter(keys); + Py_DECREF(keys); + + if (iter == NULL) { + return -1; + } + + while ((key = PyIter_Next(iter)) != NULL) { + value = PyObject_GetItem(other, key); + if (value == NULL) { + Py_DECREF(key); + Py_DECREF(iter); + return -1; + } + + if (framelocalsproxy_setitem(self, key, value) < 0) { + Py_DECREF(key); + Py_DECREF(value); + Py_DECREF(iter); + return -1; + } + + Py_DECREF(key); + Py_DECREF(value); + } + + return 0; +} + +static PyObject * +framelocalsproxy_keys(PyObject *self, PyObject *__unused) +{ + PyObject *names = PyList_New(0); + PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); + + for (int i = 0; i < co->co_nlocalsplus; i++) { + PyObject *val = framelocalsproxy_getval(frame->f_frame, co, i); + if (val) { + PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); + PyList_Append(names, name); + } + } + + // Iterate through the extra locals + Py_ssize_t i = 0; + PyObject *key = NULL; + PyObject *value = NULL; + + if (frame->f_extra_locals) { + assert(PyDict_Check(frame->f_extra_locals)); + while (PyDict_Next(frame->f_extra_locals, &i, &key, &value)) { + PyList_Append(names, key); + } + } + + return names; +} + +static void +framelocalsproxy_dealloc(PyObject *self) +{ + PyObject_GC_UnTrack(self); + Py_CLEAR(((PyFrameLocalsProxyObject*)self)->frame); + Py_TYPE(self)->tp_free(self); +} + +static PyObject * +framelocalsproxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyFrameLocalsProxyObject *self = (PyFrameLocalsProxyObject *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } + + PyFrameObject *frame = (PyFrameObject*)PyTuple_GET_ITEM(args, 0); + assert(PyFrame_Check(frame)); + + ((PyFrameLocalsProxyObject*)self)->frame = (PyFrameObject*)Py_NewRef(frame); + + return (PyObject *)self; +} + +static int +framelocalsproxy_tp_clear(PyObject *self) +{ + Py_CLEAR(((PyFrameLocalsProxyObject*)self)->frame); + return 0; +} + +static int +framelocalsproxy_visit(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(((PyFrameLocalsProxyObject*)self)->frame); + return 0; +} + +static PyObject * +framelocalsproxy_iter(PyObject *self) +{ + return PyObject_GetIter(framelocalsproxy_keys(self, NULL)); +} + +static PyObject * +framelocalsproxy_richcompare(PyObject *self, PyObject *other, int op) +{ + if (PyFrameLocalsProxy_Check(other)) { + bool result = ((PyFrameLocalsProxyObject*)self)->frame == ((PyFrameLocalsProxyObject*)other)->frame; + if (op == Py_EQ) { + return PyBool_FromLong(result); + } else if (op == Py_NE) { + return PyBool_FromLong(!result); + } + } else if (PyDict_Check(other)) { + PyObject *dct = PyDict_New(); + PyObject *result = NULL; + PyDict_Update(dct, self); + result = PyObject_RichCompare(dct, other, op); + Py_DECREF(dct); + return result; + } + + Py_RETURN_NOTIMPLEMENTED; +} + +static PyObject * +framelocalsproxy_repr(PyObject *self) +{ + int i = Py_ReprEnter(self); + if (i != 0) { + return i > 0 ? PyUnicode_FromString("{...}") : NULL; + } + + PyObject *dct = PyDict_New(); + PyObject *repr = NULL; + + if (PyDict_Update(dct, self) == 0) { + repr = PyObject_Repr(dct); + } + Py_ReprLeave(self); + + Py_DECREF(dct); + return repr; +} + +static PyObject* +framelocalsproxy_or(PyObject *self, PyObject *other) +{ + if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) { + Py_RETURN_NOTIMPLEMENTED; + } + + PyObject *result = PyDict_New(); + if (PyDict_Update(result, self) < 0) { + Py_DECREF(result); + return NULL; + } + + if (PyDict_Update(result, other) < 0) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +static PyObject* +framelocalsproxy_inplace_or(PyObject *self, PyObject *other) +{ + if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) { + Py_RETURN_NOTIMPLEMENTED; + } + + if (framelocalsproxy_merge(self, other) < 0) { + Py_RETURN_NOTIMPLEMENTED; + } + + return Py_NewRef(self); +} + +static PyObject* +framelocalsproxy_values(PyObject *self, PyObject *__unused) +{ + PyObject *values = PyList_New(0); + PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); + + for (int i = 0; i < co->co_nlocalsplus; i++) { + PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i); + if (value) { + PyList_Append(values, value); + } + } + + // Iterate through the extra locals + Py_ssize_t j = 0; + PyObject *key = NULL; + PyObject *value = NULL; + + if (frame->f_extra_locals) { + while (PyDict_Next(frame->f_extra_locals, &j, &key, &value)) { + PyList_Append(values, value); + } + } + + return values; +} + +static PyObject * +framelocalsproxy_items(PyObject *self, PyObject *__unused) +{ + PyObject *items = PyList_New(0); + PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); + + for (int i = 0; i < co->co_nlocalsplus; i++) { + PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); + PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i); + + if (value) { + PyObject *pair = PyTuple_Pack(2, name, value); + PyList_Append(items, pair); + Py_DECREF(pair); + } + } + + // Iterate through the extra locals + Py_ssize_t j = 0; + PyObject *key = NULL; + PyObject *value = NULL; + + if (frame->f_extra_locals) { + while (PyDict_Next(frame->f_extra_locals, &j, &key, &value)) { + PyObject *pair = PyTuple_Pack(2, key, value); + PyList_Append(items, pair); + Py_DECREF(pair); + } + } + + return items; +} + +static Py_ssize_t +framelocalsproxy_length(PyObject *self) +{ + PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); + Py_ssize_t size = 0; + + if (frame->f_extra_locals != NULL) { + assert(PyDict_Check(frame->f_extra_locals)); + size += PyDict_Size(frame->f_extra_locals); + } + + for (int i = 0; i < co->co_nlocalsplus; i++) { + if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) { + size++; + } + } + return size; +} + +static PyObject* +framelocalsproxy_contains(PyObject *self, PyObject *key) +{ + PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + + if (PyUnicode_CheckExact(key)) { + int i = framelocalsproxy_getkeyindex(frame, key, true); + if (i >= 0) { + Py_RETURN_TRUE; + } + } + + PyObject *extra = ((PyFrameObject*)frame)->f_extra_locals; + if (extra != NULL) { + int result = PyDict_Contains(extra, key); + if (result < 0) { + return NULL; + } else if (result > 0) { + Py_RETURN_TRUE; + } + } + + Py_RETURN_FALSE; +} + +static PyObject* +framelocalsproxy_update(PyObject *self, PyObject *other) +{ + if (framelocalsproxy_merge(self, other) < 0) { + PyErr_SetString(PyExc_TypeError, "update() argument must be dict or another FrameLocalsProxy"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject* +framelocalsproxy_get(PyObject* self, PyObject *const *args, Py_ssize_t nargs) +{ + if (nargs < 1 || nargs > 2) { + PyErr_SetString(PyExc_TypeError, "get expected 1 or 2 arguments"); + return NULL; + } + + PyObject *key = args[0]; + PyObject *default_value = Py_None; + + if (nargs == 2) { + default_value = args[1]; + } + + PyObject *result = framelocalsproxy_getitem(self, key); + + if (result == NULL) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + return Py_XNewRef(default_value); + } + return NULL; + } + + return result; +} + +static PyObject* +framelocalsproxy_setdefault(PyObject* self, PyObject *const *args, Py_ssize_t nargs) +{ + if (nargs < 1 || nargs > 2) { + PyErr_SetString(PyExc_TypeError, "setdefault expected 1 or 2 arguments"); + return NULL; + } + + PyObject *key = args[0]; + PyObject *default_value = Py_None; + + if (nargs == 2) { + default_value = args[1]; + } + + PyObject *result = framelocalsproxy_getitem(self, key); + + if (result == NULL) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + if (framelocalsproxy_setitem(self, key, default_value) < 0) { + return NULL; + } + return Py_XNewRef(default_value); + } + return NULL; + } + + return result; +} + +static PyObject* +framelocalsproxy_reversed(PyObject *self, PyObject *__unused) +{ + PyObject *result = framelocalsproxy_keys(self, NULL); + if (PyList_Reverse(result) < 0) { + Py_DECREF(result); + return NULL; + } + return result; +} + +static PyNumberMethods framelocalsproxy_as_number = { + .nb_or = framelocalsproxy_or, + .nb_inplace_or = framelocalsproxy_inplace_or, +}; + +static PyMappingMethods framelocalsproxy_as_mapping = { + framelocalsproxy_length, // mp_length + framelocalsproxy_getitem, // mp_subscript + framelocalsproxy_setitem, // mp_ass_subscript +}; + +static PyMethodDef framelocalsproxy_methods[] = { + {"__contains__", framelocalsproxy_contains, METH_O | METH_COEXIST, + NULL}, + {"__getitem__", framelocalsproxy_getitem, METH_O | METH_COEXIST, + NULL}, + {"__reversed__", framelocalsproxy_reversed, METH_NOARGS, + NULL}, + {"keys", framelocalsproxy_keys, METH_NOARGS, + NULL}, + {"values", framelocalsproxy_values, METH_NOARGS, + NULL}, + {"items", framelocalsproxy_items, METH_NOARGS, + NULL}, + {"update", framelocalsproxy_update, METH_O, + NULL}, + {"get", _PyCFunction_CAST(framelocalsproxy_get), METH_FASTCALL, + NULL}, + {"setdefault", _PyCFunction_CAST(framelocalsproxy_setdefault), METH_FASTCALL, + NULL}, + {NULL, NULL} /* sentinel */ +}; + +PyTypeObject PyFrameLocalsProxy_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "FrameLocalsProxy", + .tp_basicsize = sizeof(PyFrameLocalsProxyObject), + .tp_dealloc = (destructor)framelocalsproxy_dealloc, + .tp_repr = &framelocalsproxy_repr, + .tp_as_number = &framelocalsproxy_as_number, + .tp_as_mapping = &framelocalsproxy_as_mapping, + .tp_getattro = PyObject_GenericGetAttr, + .tp_setattro = PyObject_GenericSetAttr, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .tp_traverse = framelocalsproxy_visit, + .tp_clear = framelocalsproxy_tp_clear, + .tp_richcompare = framelocalsproxy_richcompare, + .tp_iter = framelocalsproxy_iter, + .tp_methods = framelocalsproxy_methods, + .tp_alloc = PyType_GenericAlloc, + .tp_new = framelocalsproxy_new, + .tp_free = PyObject_GC_Del, +}; + +PyObject * +_PyFrameLocalsProxy_New(PyFrameObject *frame) +{ + PyObject* args = PyTuple_Pack(1, frame); + PyObject* proxy = (PyObject*)framelocalsproxy_new(&PyFrameLocalsProxy_Type, args, NULL); + Py_DECREF(args); + return proxy; +} + static PyMemberDef frame_memberlist[] = { {"f_trace_lines", Py_T_BOOL, OFF(f_trace_lines), 0}, {NULL} /* Sentinel */ }; - static PyObject * frame_getlocals(PyFrameObject *f, void *closure) { @@ -30,11 +651,14 @@ frame_getlocals(PyFrameObject *f, void *closure) return NULL; } assert(!_PyFrame_IsIncomplete(f->f_frame)); - PyObject *locals = _PyFrame_GetLocals(f->f_frame, 1); - if (locals) { - f->f_fast_as_locals = 1; + + PyCodeObject *co = _PyFrame_GetCode(f->f_frame); + + if (!(co->co_flags & CO_OPTIMIZED) && !_PyFrame_HasHiddenLocals(f->f_frame)) { + return Py_NewRef(f->f_frame->f_locals); } - return locals; + + return _PyFrameLocalsProxy_New(f); } int @@ -595,20 +1219,6 @@ first_line_not_before(int *lines, int len, int line) return result; } -static bool -frame_is_cleared(PyFrameObject *frame) -{ - assert(!_PyFrame_IsIncomplete(frame->f_frame)); - if (frame->f_frame->stacktop == 0) { - return true; - } - if (frame->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { - PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame); - return gen->gi_frame_state == FRAME_CLEARED; - } - return false; -} - static bool frame_is_suspended(PyFrameObject *frame) { assert(!_PyFrame_IsIncomplete(frame->f_frame)); @@ -900,6 +1510,7 @@ frame_dealloc(PyFrameObject *f) } Py_CLEAR(f->f_back); Py_CLEAR(f->f_trace); + Py_CLEAR(f->f_extra_locals); PyObject_GC_Del(f); Py_XDECREF(co); Py_TRASHCAN_END; @@ -910,6 +1521,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { Py_VISIT(f->f_back); Py_VISIT(f->f_trace); + Py_VISIT(f->f_extra_locals); if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) { return 0; } @@ -921,6 +1533,7 @@ static int frame_tp_clear(PyFrameObject *f) { Py_CLEAR(f->f_trace); + Py_CLEAR(f->f_extra_locals); /* locals and stack */ PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); @@ -1056,8 +1669,8 @@ _PyFrame_New_NoTrack(PyCodeObject *code) f->f_trace = NULL; f->f_trace_lines = 1; f->f_trace_opcodes = 0; - f->f_fast_as_locals = 0; f->f_lineno = 0; + f->f_extra_locals = NULL; return f; } @@ -1204,103 +1817,45 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i, } -PyObject * -_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden) +bool +_PyFrame_HasHiddenLocals(_PyInterpreterFrame *frame) { - /* Merge fast locals into f->f_locals */ - PyObject *locals = frame->f_locals; - if (locals == NULL) { - locals = frame->f_locals = PyDict_New(); - if (locals == NULL) { - return NULL; - } - } - PyObject *hidden = NULL; - - /* If include_hidden, "hidden" fast locals (from inlined comprehensions in - module/class scopes) will be included in the returned dict, but not in - frame->f_locals; the returned dict will be a modified copy. Non-hidden - locals will still be updated in frame->f_locals. */ - if (include_hidden) { - hidden = PyDict_New(); - if (hidden == NULL) { - return NULL; - } - } - - frame_init_get_vars(frame); + /* + * This function returns if there are hidden locals introduced by PEP 709, + * which are the isolated fast locals for inline comprehensions + */ + PyCodeObject* co = _PyFrame_GetCode(frame); - PyCodeObject *co = _PyFrame_GetCode(frame); for (int i = 0; i < co->co_nlocalsplus; i++) { - PyObject *value; // borrowed reference - if (!frame_get_var(frame, co, i, &value)) { - continue; - } - - PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); + if (kind & CO_FAST_HIDDEN) { - if (include_hidden && value != NULL) { - if (PyObject_SetItem(hidden, name, value) != 0) { - goto error; - } - } - continue; - } - if (value == NULL) { - if (PyObject_DelItem(locals, name) != 0) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) { - PyErr_Clear(); - } - else { - goto error; - } - } - } - else { - if (PyObject_SetItem(locals, name, value) != 0) { - goto error; - } - } - } + PyObject* value = framelocalsproxy_getval(frame, co, i); - if (include_hidden && PyDict_Size(hidden)) { - PyObject *innerlocals = PyDict_New(); - if (innerlocals == NULL) { - goto error; - } - if (PyDict_Merge(innerlocals, locals, 1) != 0) { - Py_DECREF(innerlocals); - goto error; - } - if (PyDict_Merge(innerlocals, hidden, 1) != 0) { - Py_DECREF(innerlocals); - goto error; + if (value != NULL) { + return true; + } } - locals = innerlocals; - } - else { - Py_INCREF(locals); } - Py_CLEAR(hidden); - return locals; - - error: - Py_XDECREF(hidden); - return NULL; + return false; } -int -_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) +PyObject * +_PyFrame_GetLocals(_PyInterpreterFrame *frame) { - PyObject *locals = _PyFrame_GetLocals(frame, 0); - if (locals == NULL) { - return -1; + // We should try to avoid creating the FrameObject if possible. + // So we check if the frame is a module or class level scope + PyCodeObject *co = _PyFrame_GetCode(frame); + + if (!(co->co_flags & CO_OPTIMIZED) && !_PyFrame_HasHiddenLocals(frame)) { + return Py_NewRef(frame->f_locals); } - Py_DECREF(locals); - return 0; + + PyFrameObject* f = _PyFrame_GetFrameObject(frame); + + return _PyFrameLocalsProxy_New(f); } @@ -1354,112 +1909,19 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name) int PyFrame_FastToLocalsWithError(PyFrameObject *f) { - if (f == NULL) { - PyErr_BadInternalCall(); - return -1; - } - assert(!_PyFrame_IsIncomplete(f->f_frame)); - int err = _PyFrame_FastToLocalsWithError(f->f_frame); - if (err == 0) { - f->f_fast_as_locals = 1; - } - return err; + return 0; } void PyFrame_FastToLocals(PyFrameObject *f) { - int res; - assert(!_PyFrame_IsIncomplete(f->f_frame)); - assert(!PyErr_Occurred()); - - res = PyFrame_FastToLocalsWithError(f); - if (res < 0) - PyErr_Clear(); -} - -void -_PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) -{ - /* Merge locals into fast locals */ - PyObject *locals; - PyObject **fast; - PyCodeObject *co; - locals = frame->f_locals; - if (locals == NULL) { - return; - } - fast = _PyFrame_GetLocalsArray(frame); - co = _PyFrame_GetCode(frame); - - PyObject *exc = PyErr_GetRaisedException(); - for (int i = 0; i < co->co_nlocalsplus; i++) { - _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); - - /* Same test as in PyFrame_FastToLocals() above. */ - if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) { - continue; - } - PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); - PyObject *value = PyObject_GetItem(locals, name); - /* We only care about NULLs if clear is true. */ - if (value == NULL) { - PyErr_Clear(); - if (!clear) { - continue; - } - } - PyObject *oldvalue = fast[i]; - PyObject *cell = NULL; - if (kind == CO_FAST_FREE) { - // The cell was set when the frame was created from - // the function's closure. - assert(oldvalue != NULL && PyCell_Check(oldvalue)); - cell = oldvalue; - } - else if (kind & CO_FAST_CELL && oldvalue != NULL) { - /* Same test as in PyFrame_FastToLocals() above. */ - if (PyCell_Check(oldvalue) && - _PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) { - // (likely) MAKE_CELL must have executed already. - cell = oldvalue; - } - // (unlikely) Otherwise, it must have been set to some - // initial value by an earlier call to PyFrame_LocalsToFast(). - } - if (cell != NULL) { - oldvalue = PyCell_GET(cell); - if (value != oldvalue) { - PyCell_SET(cell, Py_XNewRef(value)); - Py_XDECREF(oldvalue); - } - } - else if (value != oldvalue) { - if (value == NULL) { - // Probably can't delete this, since the compiler's flow - // analysis may have already "proven" that it exists here: - const char *e = "assigning None to unbound local %R"; - if (PyErr_WarnFormat(PyExc_RuntimeWarning, 0, e, name)) { - // It's okay if frame_obj is NULL, just try anyways: - PyErr_WriteUnraisable((PyObject *)frame->frame_obj); - } - value = Py_NewRef(Py_None); - } - Py_XSETREF(fast[i], Py_NewRef(value)); - } - Py_XDECREF(value); - } - PyErr_SetRaisedException(exc); + return; } void PyFrame_LocalsToFast(PyFrameObject *f, int clear) { - assert(!_PyFrame_IsIncomplete(f->f_frame)); - if (f && f->f_fast_as_locals && !frame_is_cleared(f)) { - _PyFrame_LocalsToFast(f->f_frame, clear); - f->f_fast_as_locals = 0; - } + return; } int diff --git a/Objects/object.c b/Objects/object.c index 79e4fb4dbbf7c6..1bf0e65ec60ce4 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2235,6 +2235,7 @@ static PyTypeObject* static_types[] = { &PyFilter_Type, &PyFloat_Type, &PyFrame_Type, + &PyFrameLocalsProxy_Type, &PyFrozenSet_Type, &PyFunction_Type, &PyGen_Type, diff --git a/PC/python3dll.c b/PC/python3dll.c index c6fdc0bd73b9fe..86c888430891c9 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -253,6 +253,9 @@ EXPORT_FUNC(PyEval_EvalFrame) EXPORT_FUNC(PyEval_EvalFrameEx) EXPORT_FUNC(PyEval_GetBuiltins) EXPORT_FUNC(PyEval_GetFrame) +EXPORT_FUNC(PyEval_GetFrameBuiltins) +EXPORT_FUNC(PyEval_GetFrameGlobals) +EXPORT_FUNC(PyEval_GetFrameLocals) EXPORT_FUNC(PyEval_GetFuncDesc) EXPORT_FUNC(PyEval_GetFuncName) EXPORT_FUNC(PyEval_GetGlobals) diff --git a/Python/ceval.c b/Python/ceval.c index 3626ffbd02ff40..0d02a9887bef7a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2475,12 +2475,7 @@ PyEval_GetLocals(void) return NULL; } - if (_PyFrame_FastToLocalsWithError(current_frame) < 0) { - return NULL; - } - - PyObject *locals = current_frame->f_locals; - assert(locals != NULL); + PyObject *locals = _PyEval_GetFrameLocals(); return locals; } @@ -2494,7 +2489,24 @@ _PyEval_GetFrameLocals(void) return NULL; } - return _PyFrame_GetLocals(current_frame, 1); + PyObject *locals = _PyFrame_GetLocals(current_frame); + if (locals == NULL) { + return NULL; + } + + if (PyFrameLocalsProxy_Check(locals)) { + PyObject* ret = PyDict_New(); + if (PyDict_Update(ret, locals)) { + Py_DECREF(ret); + return NULL; + } + Py_DECREF(locals); + return ret; + } else if (PyMapping_Check(locals)) { + return locals; + } + + return NULL; } PyObject * @@ -2508,6 +2520,28 @@ PyEval_GetGlobals(void) return current_frame->f_globals; } +PyObject* +PyEval_GetFrameLocals(void) +{ + return _PyEval_GetFrameLocals(); +} + +PyObject* PyEval_GetFrameGlobals(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + _PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate); + if (current_frame == NULL) { + return NULL; + } + return Py_XNewRef(current_frame->f_globals); +} + +PyObject* PyEval_GetFrameBuiltins(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + return Py_XNewRef(_PyEval_GetBuiltins(tstate)); +} + int PyEval_MergeCompilerFlags(PyCompilerFlags *cf) { diff --git a/Python/intrinsics.c b/Python/intrinsics.c index 5b10c3ce7d5d77..a6b2c108b67175 100644 --- a/Python/intrinsics.c +++ b/Python/intrinsics.c @@ -123,18 +123,15 @@ static PyObject * import_star(PyThreadState* tstate, PyObject *from) { _PyInterpreterFrame *frame = tstate->current_frame; - if (_PyFrame_FastToLocalsWithError(frame) < 0) { - return NULL; - } - PyObject *locals = frame->f_locals; + PyObject *locals = _PyFrame_GetLocals(frame); if (locals == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, "no locals found during 'import *'"); return NULL; } int err = import_all_from(tstate, locals, from); - _PyFrame_LocalsToFast(frame, 0); + Py_DECREF(locals); if (err < 0) { return NULL; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 645b76fccf602c..bd7f821931da42 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1022,13 +1022,6 @@ static PyObject * call_trampoline(PyThreadState *tstate, PyObject* callback, PyFrameObject *frame, int what, PyObject *arg) { - /* Discard any previous modifications the frame's fast locals */ - if (frame->f_fast_as_locals) { - if (PyFrame_FastToLocalsWithError(frame) < 0) { - return NULL; - } - } - /* call the Python-level function */ if (arg == NULL) { arg = Py_None; @@ -1036,7 +1029,6 @@ call_trampoline(PyThreadState *tstate, PyObject* callback, PyObject *args[3] = {(PyObject *)frame, whatstrings[what], arg}; PyObject *result = _PyObject_VectorcallTstate(tstate, callback, args, 3, NULL); - PyFrame_LocalsToFast(frame, 1); return result; } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 285129fd361665..1b8cccf80872c8 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -43,6 +43,7 @@ Objects/enumobject.c - PyReversed_Type - Objects/fileobject.c - PyStdPrinter_Type - Objects/floatobject.c - PyFloat_Type - Objects/frameobject.c - PyFrame_Type - +Objects/frameobject.c - PyFrameLocalsProxy_Type - Objects/funcobject.c - PyClassMethod_Type - Objects/funcobject.c - PyFunction_Type - Objects/funcobject.c - PyStaticMethod_Type - From d5e6c7cb667465125ff7fe2c643ccb2abb40fcd0 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Sat, 4 May 2024 08:46:32 -0500 Subject: [PATCH 207/217] fix comment typo in importlib (#118567) --- Lib/importlib/_bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 6d6292f9559253..de5651f0a7fc36 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -1134,7 +1134,7 @@ def find_spec(cls, fullname, path=None, target=None): # part of the importer), instead of here (the finder part). # The loader is the usual place to get the data that will # be loaded into the module. (For example, see _LoaderBasics - # in _bootstra_external.py.) Most importantly, this importer + # in _bootstrap_external.py.) Most importantly, this importer # is simpler if we wait to get the data. # However, getting as much data in the finder as possible # to later load the module is okay, and sometimes important. From f6b5d3bdc83f8daca05e8b379190587a236585ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 14:47:48 +0100 Subject: [PATCH 208/217] build(deps): bump hypothesis from 6.100.0 to 6.100.2 in /Tools (#118462) Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.100.0 to 6.100.2. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.100.0...hypothesis-python-6.100.2) --- Tools/requirements-hypothesis.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/requirements-hypothesis.txt b/Tools/requirements-hypothesis.txt index 8cfa0df523dd16..9d5a18c881bf36 100644 --- a/Tools/requirements-hypothesis.txt +++ b/Tools/requirements-hypothesis.txt @@ -1,4 +1,4 @@ # Requirements file for hypothesis that # we use to run our property-based tests in CI. -hypothesis==6.100.0 +hypothesis==6.100.2 From f34e965e52b9bdf157b829371870edfde45b80bf Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Sat, 4 May 2024 07:44:49 -0700 Subject: [PATCH 209/217] GH-111744: Support opcode events in bdb (GH-111834) --- Lib/bdb.py | 72 +++++++++++++++---- Lib/test/test_bdb.py | 15 +++- Lib/test/test_pdb.py | 1 - ...-11-07-22-41-42.gh-issue-111744.TbLxF0.rst | 1 + 4 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-07-22-41-42.gh-issue-111744.TbLxF0.rst diff --git a/Lib/bdb.py b/Lib/bdb.py index 1acf7957f0d669..675c8ae51df4c3 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -32,8 +32,10 @@ def __init__(self, skip=None): self.skip = set(skip) if skip else None self.breaks = {} self.fncache = {} - self.frame_trace_lines = {} + self.frame_trace_lines_opcodes = {} self.frame_returning = None + self.trace_opcodes = False + self.enterframe = None self._load_breaks() @@ -85,6 +87,9 @@ def trace_dispatch(self, frame, event, arg): The arg parameter depends on the previous event. """ + + self.enterframe = frame + if self.quitting: return # None if event == 'line': @@ -101,6 +106,8 @@ def trace_dispatch(self, frame, event, arg): return self.trace_dispatch if event == 'c_return': return self.trace_dispatch + if event == 'opcode': + return self.dispatch_opcode(frame, arg) print('bdb.Bdb.dispatch: unknown debugging event:', repr(event)) return self.trace_dispatch @@ -187,6 +194,17 @@ def dispatch_exception(self, frame, arg): return self.trace_dispatch + def dispatch_opcode(self, frame, arg): + """Invoke user function and return trace function for opcode event. + If the debugger stops on the current opcode, invoke + self.user_opcode(). Raise BdbQuit if self.quitting is set. + Return self.trace_dispatch to continue tracing in this scope. + """ + if self.stop_here(frame) or self.break_here(frame): + self.user_opcode(frame) + if self.quitting: raise BdbQuit + return self.trace_dispatch + # Normally derived classes don't override the following # methods, but they may if they want to redefine the # definition of stopping and breakpoints. @@ -273,7 +291,21 @@ def user_exception(self, frame, exc_info): """Called when we stop on an exception.""" pass - def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): + def user_opcode(self, frame): + """Called when we are about to execute an opcode.""" + pass + + def _set_trace_opcodes(self, trace_opcodes): + if trace_opcodes != self.trace_opcodes: + self.trace_opcodes = trace_opcodes + frame = self.enterframe + while frame is not None: + frame.f_trace_opcodes = trace_opcodes + if frame is self.botframe: + break + frame = frame.f_back + + def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False): """Set the attributes for stopping. If stoplineno is greater than or equal to 0, then stop at line @@ -286,6 +318,17 @@ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): # stoplineno >= 0 means: stop at line >= the stoplineno # stoplineno -1 means: don't stop at all self.stoplineno = stoplineno + self._set_trace_opcodes(opcode) + + def _set_caller_tracefunc(self): + # Issue #13183: pdb skips frames after hitting a breakpoint and running + # step commands. + # Restore the trace function in the caller (that may not have been set + # for performance reasons) when returning from the current frame. + if self.frame_returning: + caller_frame = self.frame_returning.f_back + if caller_frame and not caller_frame.f_trace: + caller_frame.f_trace = self.trace_dispatch # Derived classes and clients can call the following methods # to affect the stepping state. @@ -300,16 +343,14 @@ def set_until(self, frame, lineno=None): def set_step(self): """Stop after one line of code.""" - # Issue #13183: pdb skips frames after hitting a breakpoint and running - # step commands. - # Restore the trace function in the caller (that may not have been set - # for performance reasons) when returning from the current frame. - if self.frame_returning: - caller_frame = self.frame_returning.f_back - if caller_frame and not caller_frame.f_trace: - caller_frame.f_trace = self.trace_dispatch + self._set_caller_tracefunc() self._set_stopinfo(None, None) + def set_stepinstr(self): + """Stop before the next instruction.""" + self._set_caller_tracefunc() + self._set_stopinfo(None, None, opcode=True) + def set_next(self, frame): """Stop on the next line in or below the given frame.""" self._set_stopinfo(frame, None) @@ -329,11 +370,12 @@ def set_trace(self, frame=None): if frame is None: frame = sys._getframe().f_back self.reset() + self.enterframe = frame while frame: frame.f_trace = self.trace_dispatch self.botframe = frame - # We need f_trace_liens == True for the debugger to work - self.frame_trace_lines[frame] = frame.f_trace_lines + self.frame_trace_lines_opcodes[frame] = (frame.f_trace_lines, frame.f_trace_opcodes) + # We need f_trace_lines == True for the debugger to work frame.f_trace_lines = True frame = frame.f_back self.set_step() @@ -353,9 +395,9 @@ def set_continue(self): while frame and frame is not self.botframe: del frame.f_trace frame = frame.f_back - for frame, prev_trace_lines in self.frame_trace_lines.items(): - frame.f_trace_lines = prev_trace_lines - self.frame_trace_lines = {} + for frame, (trace_lines, trace_opcodes) in self.frame_trace_lines_opcodes.items(): + frame.f_trace_lines, frame.f_trace_opcodes = trace_lines, trace_opcodes + self.frame_trace_lines_opcodes = {} def set_quit(self): """Set quitting attribute to True. diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py index 568c88e326c087..ed1a63daea1186 100644 --- a/Lib/test/test_bdb.py +++ b/Lib/test/test_bdb.py @@ -228,6 +228,10 @@ def user_exception(self, frame, exc_info): self.process_event('exception', frame) self.next_set_method() + def user_opcode(self, frame): + self.process_event('opcode', frame) + self.next_set_method() + def do_clear(self, arg): # The temporary breakpoints are deleted in user_line(). bp_list = [self.currentbp] @@ -366,7 +370,7 @@ def next_set_method(self): set_method = getattr(self, 'set_' + set_type) # The following set methods give back control to the tracer. - if set_type in ('step', 'continue', 'quit'): + if set_type in ('step', 'stepinstr', 'continue', 'quit'): set_method() return elif set_type in ('next', 'return'): @@ -610,6 +614,15 @@ def test_step_next_on_last_statement(self): with TracerRun(self) as tracer: tracer.runcall(tfunc_main) + def test_stepinstr(self): + self.expect_set = [ + ('line', 2, 'tfunc_main'), ('stepinstr', ), + ('opcode', 2, 'tfunc_main'), ('next', ), + ('line', 3, 'tfunc_main'), ('quit', ), + ] + with TracerRun(self) as tracer: + tracer.runcall(tfunc_main) + def test_next(self): self.expect_set = [ ('line', 2, 'tfunc_main'), ('step', ), diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index c9ea52717490b1..001562acae7458 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2456,7 +2456,6 @@ def test_pdb_issue_gh_108976(): ... 'continue' ... ]): ... test_function() - bdb.Bdb.dispatch: unknown debugging event: 'opcode' > (5)test_function() -> a = 1 (Pdb) continue diff --git a/Misc/NEWS.d/next/Library/2023-11-07-22-41-42.gh-issue-111744.TbLxF0.rst b/Misc/NEWS.d/next/Library/2023-11-07-22-41-42.gh-issue-111744.TbLxF0.rst new file mode 100644 index 00000000000000..ed856e7667a372 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-07-22-41-42.gh-issue-111744.TbLxF0.rst @@ -0,0 +1 @@ +Support opcode events in :mod:`bdb` From 5f547585fa56c94c5d836b5313a7200f4937ebc4 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 4 May 2024 18:08:38 +0300 Subject: [PATCH 210/217] gh-118569: Add a test for dynamic PEP695 classes (#118570) --- Lib/test/test_type_params.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index 4c5bf6bfd33c75..82f1007f9ac97b 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -776,6 +776,31 @@ class D[U](T): self.assertIn(int, C.D.__bases__) self.assertIs(C.D.x, str) + +class DynamicClassTest(unittest.TestCase): + def _set_type_params(self, ns, params): + ns['__type_params__'] = params + + def test_types_new_class_with_callback(self): + T = TypeVar('T', infer_variance=True) + Klass = types.new_class('Klass', (Generic[T],), {}, + lambda ns: self._set_type_params(ns, (T,))) + + self.assertEqual(Klass.__bases__, (Generic,)) + self.assertEqual(Klass.__orig_bases__, (Generic[T],)) + self.assertEqual(Klass.__type_params__, (T,)) + self.assertEqual(Klass.__parameters__, (T,)) + + def test_types_new_class_no_callback(self): + T = TypeVar('T', infer_variance=True) + Klass = types.new_class('Klass', (Generic[T],), {}) + + self.assertEqual(Klass.__bases__, (Generic,)) + self.assertEqual(Klass.__orig_bases__, (Generic[T],)) + self.assertEqual(Klass.__type_params__, ()) # must be explicitly set + self.assertEqual(Klass.__parameters__, (T,)) + + class TypeParamsManglingTest(unittest.TestCase): def test_mangling(self): class Foo[__T]: From 978fba58aef347de4a1376e525df2dacc7b2fff3 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Sat, 4 May 2024 23:45:49 +0800 Subject: [PATCH 211/217] gh-117139: Fix missing semicolon (GH-118573) --- Include/internal/pycore_stackref.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index fd929cd4873a8b..93898174789f7b 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -114,7 +114,7 @@ _Py_untag_stack_steal(PyObject **dst, const _PyStackRef *src, size_t length) #define PyStackRef_XSETREF(dst, src) \ do { \ - _PyStackRef *_tmp_dst_ptr = &(dst) \ + _PyStackRef *_tmp_dst_ptr = &(dst); \ _PyStackRef _tmp_old_dst = (*_tmp_dst_ptr); \ *_tmp_dst_ptr = (src); \ PyStackRef_XDECREF(_tmp_old_dst); \ From 291cfa454b9c5b677c955aaf53fab91f0186b6fa Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 4 May 2024 15:24:02 -0600 Subject: [PATCH 212/217] gh-117953: Track Extra Details in Global Extensions Cache (gh-118532) We have only been tracking each module's PyModuleDef. However, there are some problems with that. For example, in some cases we load single-phase init extension modules from def->m_base.m_init or def->m_base.m_copy, but if multiple modules share a def then we can end up with unexpected behavior. With this change, we track the following: * PyModuleDef (same as before) * for some modules, its init function or a copy of its __dict__, but specific to that module * whether it is a builtin/core module or a "dynamic" extension * the interpreter (ID) that owns the cached __dict__ (only if cached) This also makes it easier to remember the module's kind (e.g. single-phase init) and if loading it previously failed, which I'm doing separately. --- Include/internal/pycore_importdl.h | 12 +- Python/import.c | 680 +++++++++++++++++++++++------ Python/importdl.c | 24 +- 3 files changed, 578 insertions(+), 138 deletions(-) diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index b0af28da34e087..e5f222b371a113 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -22,6 +22,11 @@ typedef enum ext_module_kind { _Py_ext_module_kind_INVALID = 3, } _Py_ext_module_kind; +typedef enum ext_module_origin { + _Py_ext_module_origin_CORE = 1, + _Py_ext_module_origin_BUILTIN = 2, + _Py_ext_module_origin_DYNAMIC = 3, +} _Py_ext_module_origin; /* Input for loading an extension module. */ struct _Py_ext_module_loader_info { @@ -34,6 +39,7 @@ struct _Py_ext_module_loader_info { /* path is always a borrowed ref of name or filename, * depending on if it's builtin or not. */ PyObject *path; + _Py_ext_module_origin origin; const char *hook_prefix; const char *newcontext; }; @@ -42,7 +48,11 @@ extern void _Py_ext_module_loader_info_clear( extern int _Py_ext_module_loader_info_init( struct _Py_ext_module_loader_info *info, PyObject *name, - PyObject *filename); + PyObject *filename, + _Py_ext_module_origin origin); +extern int _Py_ext_module_loader_info_init_for_core( + struct _Py_ext_module_loader_info *p_info, + PyObject *name); extern int _Py_ext_module_loader_info_init_for_builtin( struct _Py_ext_module_loader_info *p_info, PyObject *name); diff --git a/Python/import.c b/Python/import.c index 4f91f03f091edd..fa0e548c9bf4c4 100644 --- a/Python/import.c +++ b/Python/import.c @@ -34,6 +34,17 @@ module _imp #include "clinic/import.c.h" +#ifndef NDEBUG +static bool +is_interpreter_isolated(PyInterpreterState *interp) +{ + return !_Py_IsMainInterpreter(interp) + && !(interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC) + && interp->ceval.own_gil; +} +#endif + + /*******************************/ /* process-global import state */ /*******************************/ @@ -435,6 +446,45 @@ _PyImport_GetNextModuleIndex(void) return _Py_atomic_add_ssize(&LAST_MODULE_INDEX, 1) + 1; } +#ifndef NDEBUG +struct extensions_cache_value; +static struct extensions_cache_value * _find_cached_def(PyModuleDef *); +static Py_ssize_t _get_cached_module_index(struct extensions_cache_value *); +#endif + +static Py_ssize_t +_get_module_index_from_def(PyModuleDef *def) +{ + Py_ssize_t index = def->m_base.m_index; + assert(index > 0); +#ifndef NDEBUG + struct extensions_cache_value *cached = _find_cached_def(def); + assert(cached == NULL || index == _get_cached_module_index(cached)); +#endif + return index; +} + +static void +_set_module_index(PyModuleDef *def, Py_ssize_t index) +{ + assert(index > 0); + if (index == def->m_base.m_index) { + /* There's nothing to do. */ + } + else if (def->m_base.m_index == 0) { + /* It should have been initialized by PyModuleDef_Init(). + * We assert here to catch this in dev, but keep going otherwise. */ + assert(def->m_base.m_index != 0); + def->m_base.m_index = index; + } + else { + /* It was already set for a different module. + * We replace the old value. */ + assert(def->m_base.m_index > 0); + def->m_base.m_index = index; + } +} + static const char * _modules_by_index_check(PyInterpreterState *interp, Py_ssize_t index) { @@ -451,9 +501,8 @@ _modules_by_index_check(PyInterpreterState *interp, Py_ssize_t index) } static PyObject * -_modules_by_index_get(PyInterpreterState *interp, PyModuleDef *def) +_modules_by_index_get(PyInterpreterState *interp, Py_ssize_t index) { - Py_ssize_t index = def->m_base.m_index; if (_modules_by_index_check(interp, index) != NULL) { return NULL; } @@ -463,11 +512,9 @@ _modules_by_index_get(PyInterpreterState *interp, PyModuleDef *def) static int _modules_by_index_set(PyInterpreterState *interp, - PyModuleDef *def, PyObject *module) + Py_ssize_t index, PyObject *module) { - assert(def != NULL); - assert(def->m_slots == NULL); - assert(def->m_base.m_index > 0); + assert(index > 0); if (MODULES_BY_INDEX(interp) == NULL) { MODULES_BY_INDEX(interp) = PyList_New(0); @@ -476,7 +523,6 @@ _modules_by_index_set(PyInterpreterState *interp, } } - Py_ssize_t index = def->m_base.m_index; while (PyList_GET_SIZE(MODULES_BY_INDEX(interp)) <= index) { if (PyList_Append(MODULES_BY_INDEX(interp), Py_None) < 0) { return -1; @@ -487,9 +533,8 @@ _modules_by_index_set(PyInterpreterState *interp, } static int -_modules_by_index_clear_one(PyInterpreterState *interp, PyModuleDef *def) +_modules_by_index_clear_one(PyInterpreterState *interp, Py_ssize_t index) { - Py_ssize_t index = def->m_base.m_index; const char *err = _modules_by_index_check(interp, index); if (err != NULL) { Py_FatalError(err); @@ -506,7 +551,8 @@ PyState_FindModule(PyModuleDef* module) if (module->m_slots) { return NULL; } - return _modules_by_index_get(interp, module); + Py_ssize_t index = _get_module_index_from_def(module); + return _modules_by_index_get(interp, index); } /* _PyState_AddModule() has been completely removed from the C-API @@ -526,7 +572,9 @@ _PyState_AddModule(PyThreadState *tstate, PyObject* module, PyModuleDef* def) "PyState_AddModule called on module with slots"); return -1; } - return _modules_by_index_set(tstate->interp, def, module); + assert(def->m_slots == NULL); + Py_ssize_t index = _get_module_index_from_def(def); + return _modules_by_index_set(tstate->interp, index, module); } int @@ -546,7 +594,7 @@ PyState_AddModule(PyObject* module, PyModuleDef* def) } PyInterpreterState *interp = tstate->interp; - Py_ssize_t index = def->m_base.m_index; + Py_ssize_t index = _get_module_index_from_def(def); if (MODULES_BY_INDEX(interp) && index < PyList_GET_SIZE(MODULES_BY_INDEX(interp)) && module == PyList_GET_ITEM(MODULES_BY_INDEX(interp), index)) @@ -555,7 +603,8 @@ PyState_AddModule(PyObject* module, PyModuleDef* def) return -1; } - return _modules_by_index_set(interp, def, module); + assert(def->m_slots == NULL); + return _modules_by_index_set(interp, index, module); } int @@ -568,7 +617,8 @@ PyState_RemoveModule(PyModuleDef* def) "PyState_RemoveModule called on module with slots"); return -1; } - return _modules_by_index_clear_one(tstate->interp, def); + Py_ssize_t index = _get_module_index_from_def(def); + return _modules_by_index_clear_one(tstate->interp, index); } @@ -587,6 +637,8 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) /* cleanup the saved copy of module dicts */ PyModuleDef *md = PyModule_GetDef(m); if (md) { + // XXX Do this more carefully. The dict might be owned + // by another interpreter. Py_CLEAR(md->m_base.m_copy); } } @@ -924,6 +976,7 @@ extensions_lock_release(void) PyMutex_Unlock(&_PyRuntime.imports.extensions.mutex); } + /* Magic for extension modules (built-in as well as dynamically loaded). To prevent initializing an extension module more than once, we keep a static dictionary 'extensions' keyed by the tuple @@ -940,6 +993,217 @@ extensions_lock_release(void) dictionary, to avoid loading shared libraries twice. */ +typedef struct cached_m_dict { + /* A shallow copy of the original module's __dict__. */ + PyObject *copied; + /* The interpreter that owns the copy. */ + int64_t interpid; +} *cached_m_dict_t; + +struct extensions_cache_value { + PyModuleDef *def; + + /* The function used to re-initialize the module. + This is only set for legacy (single-phase init) extension modules + and only used for those that support multiple initializations + (m_size >= 0). + It is set by update_global_state_for_extension(). */ + PyModInitFunction m_init; + + /* The module's index into its interpreter's modules_by_index cache. + This is set for all extension modules but only used for legacy ones. + (See PyInterpreterState.modules_by_index for more info.) */ + Py_ssize_t m_index; + + /* A copy of the module's __dict__ after the first time it was loaded. + This is only set/used for legacy modules that do not support + multiple initializations. + It is set exclusively by fixup_cached_def(). */ + cached_m_dict_t m_dict; + struct cached_m_dict _m_dict; + + _Py_ext_module_origin origin; +}; + +static struct extensions_cache_value * +alloc_extensions_cache_value(void) +{ + struct extensions_cache_value *value + = PyMem_RawMalloc(sizeof(struct extensions_cache_value)); + if (value == NULL) { + PyErr_NoMemory(); + return NULL; + } + *value = (struct extensions_cache_value){0}; + return value; +} + +static void +free_extensions_cache_value(struct extensions_cache_value *value) +{ + PyMem_RawFree(value); +} + +static Py_ssize_t +_get_cached_module_index(struct extensions_cache_value *cached) +{ + assert(cached->m_index > 0); + return cached->m_index; +} + +static void +fixup_cached_def(struct extensions_cache_value *value) +{ + /* For the moment, the values in the def's m_base may belong + * to another module, and we're replacing them here. This can + * cause problems later if the old module is reloaded. + * + * Also, we don't decref any old cached values first when we + * replace them here, in case we need to restore them in the + * near future. Instead, the caller is responsible for wrapping + * this up by calling cleanup_old_cached_def() or + * restore_old_cached_def() if there was an error. */ + PyModuleDef *def = value->def; + assert(def != NULL); + + /* We assume that all module defs are statically allocated + and will never be freed. Otherwise, we would incref here. */ + _Py_SetImmortalUntracked((PyObject *)def); + + def->m_base.m_init = value->m_init; + + assert(value->m_index > 0); + _set_module_index(def, value->m_index); + + /* Different modules can share the same def, so we can't just + * expect m_copy to be NULL. */ + assert(def->m_base.m_copy == NULL + || def->m_base.m_init == NULL + || value->m_dict != NULL); + if (value->m_dict != NULL) { + assert(value->m_dict->copied != NULL); + /* As noted above, we don't first decref the old value, if any. */ + def->m_base.m_copy = Py_NewRef(value->m_dict->copied); + } +} + +static void +restore_old_cached_def(PyModuleDef *def, PyModuleDef_Base *oldbase) +{ + def->m_base = *oldbase; +} + +static void +cleanup_old_cached_def(PyModuleDef_Base *oldbase) +{ + Py_XDECREF(oldbase->m_copy); +} + +static void +del_cached_def(struct extensions_cache_value *value) +{ + /* If we hadn't made the stored defs immortal, we would decref here. + However, this decref would be problematic if the module def were + dynamically allocated, it were the last ref, and this function + were called with an interpreter other than the def's owner. */ + assert(value->def == NULL || _Py_IsImmortal(value->def)); + + Py_XDECREF(value->def->m_base.m_copy); + value->def->m_base.m_copy = NULL; +} + +static int +init_cached_m_dict(struct extensions_cache_value *value, PyObject *m_dict) +{ + assert(value != NULL); + /* This should only have been called without an m_dict already set. */ + assert(value->m_dict == NULL); + if (m_dict == NULL) { + return 0; + } + assert(PyDict_Check(m_dict)); + assert(value->origin != _Py_ext_module_origin_CORE); + + PyInterpreterState *interp = _PyInterpreterState_GET(); + assert(!is_interpreter_isolated(interp)); + + /* XXX gh-88216: The copied dict is owned by the current + * interpreter. That's a problem if the interpreter has + * its own obmalloc state or if the module is successfully + * imported into such an interpreter. If the interpreter + * has its own GIL then there may be data races and + * PyImport_ClearModulesByIndex() can crash. Normally, + * a single-phase init module cannot be imported in an + * isolated interpreter, but there are ways around that. + * Hence, heere be dragons! Ideally we would instead do + * something like make a read-only, immortal copy of the + * dict using PyMem_RawMalloc() and store *that* in m_copy. + * Then we'd need to make sure to clear that when the + * runtime is finalized, rather than in + * PyImport_ClearModulesByIndex(). */ + PyObject *copied = PyDict_Copy(m_dict); + if (copied == NULL) { + /* We expect this can only be "out of memory". */ + return -1; + } + // XXX We may want to make the copy immortal. + // XXX This incref shouldn't be necessary. We are decref'ing + // one to many times somewhere. + Py_INCREF(copied); + + value->_m_dict = (struct cached_m_dict){ + .copied=copied, + .interpid=PyInterpreterState_GetID(interp), + }; + + value->m_dict = &value->_m_dict; + return 0; +} + +static void +del_cached_m_dict(struct extensions_cache_value *value) +{ + if (value->m_dict != NULL) { + assert(value->m_dict == &value->_m_dict); + assert(value->m_dict->copied != NULL); + /* In the future we can take advantage of m_dict->interpid + * to decref the dict using the owning interpreter. */ + Py_XDECREF(value->m_dict->copied); + value->m_dict = NULL; + } +} + +static PyObject * get_core_module_dict( + PyInterpreterState *interp, PyObject *name, PyObject *path); + +static PyObject * +get_cached_m_dict(struct extensions_cache_value *value, + PyObject *name, PyObject *path) +{ + assert(value != NULL); + PyInterpreterState *interp = _PyInterpreterState_GET(); + /* It might be a core module (e.g. sys & builtins), + for which we don't cache m_dict. */ + if (value->origin == _Py_ext_module_origin_CORE) { + return get_core_module_dict(interp, name, path); + } + assert(value->def != NULL); + // XXX Switch to value->m_dict. + PyObject *m_dict = value->def->m_base.m_copy; + Py_XINCREF(m_dict); + return m_dict; +} + +static void +del_extensions_cache_value(struct extensions_cache_value *value) +{ + if (value != NULL) { + del_cached_m_dict(value); + del_cached_def(value); + free_extensions_cache_value(value); + } +} + static void * hashtable_key_from_2_strings(PyObject *str1, PyObject *str2, const char sep) { @@ -953,6 +1217,7 @@ hashtable_key_from_2_strings(PyObject *str1, PyObject *str2, const char sep) assert(SIZE_MAX - str1_len - str2_len > 2); size_t size = str1_len + 1 + str2_len + 1; + // XXX Use a buffer if it's a temp value (every case but "set"). char *key = PyMem_RawMalloc(size); if (key == NULL) { PyErr_NoMemory(); @@ -984,6 +1249,41 @@ hashtable_destroy_str(void *ptr) PyMem_RawFree(ptr); } +#ifndef NDEBUG +struct hashtable_next_match_def_data { + PyModuleDef *def; + struct extensions_cache_value *matched; +}; + +static int +hashtable_next_match_def(_Py_hashtable_t *ht, + const void *key, const void *value, void *user_data) +{ + if (value == NULL) { + /* It was previously deleted. */ + return 0; + } + struct hashtable_next_match_def_data *data + = (struct hashtable_next_match_def_data *)user_data; + struct extensions_cache_value *cur + = (struct extensions_cache_value *)value; + if (cur->def == data->def) { + data->matched = cur; + return 1; + } + return 0; +} + +static struct extensions_cache_value * +_find_cached_def(PyModuleDef *def) +{ + struct hashtable_next_match_def_data data = {0}; + (void)_Py_hashtable_foreach( + EXTENSIONS.hashtable, hashtable_next_match_def, &data); + return data.matched; +} +#endif + #define HTSEP ':' static int @@ -994,8 +1294,7 @@ _extensions_cache_init(void) hashtable_hash_str, hashtable_compare_str, hashtable_destroy_str, // key - /* There's no need to decref the def since it's immortal. */ - NULL, // value + (_Py_hashtable_destroy_func)del_extensions_cache_value, // value &alloc ); if (EXTENSIONS.hashtable == NULL) { @@ -1027,10 +1326,11 @@ _extensions_cache_find_unlocked(PyObject *path, PyObject *name, return entry; } -static PyModuleDef * +/* This can only fail with "out of memory". */ +static struct extensions_cache_value * _extensions_cache_get(PyObject *path, PyObject *name) { - PyModuleDef *def = NULL; + struct extensions_cache_value *value = NULL; extensions_lock_acquire(); _Py_hashtable_entry_t *entry = @@ -1039,18 +1339,35 @@ _extensions_cache_get(PyObject *path, PyObject *name) /* It was never added. */ goto finally; } - def = (PyModuleDef *)entry->value; + value = (struct extensions_cache_value *)entry->value; finally: extensions_lock_release(); - return def; + return value; } -static int -_extensions_cache_set(PyObject *path, PyObject *name, PyModuleDef *def) +/* This can only fail with "out of memory". */ +static struct extensions_cache_value * +_extensions_cache_set(PyObject *path, PyObject *name, + PyModuleDef *def, PyModInitFunction m_init, + Py_ssize_t m_index, PyObject *m_dict, + _Py_ext_module_origin origin) { - int res = -1; + struct extensions_cache_value *value = NULL; + void *key = NULL; + struct extensions_cache_value *newvalue = NULL; + PyModuleDef_Base olddefbase = def->m_base; + assert(def != NULL); + assert(m_init == NULL || m_dict == NULL); + /* We expect the same symbol to be used and the shared object file + * to have remained loaded, so it must be the same pointer. */ + assert(def->m_base.m_init == NULL || def->m_base.m_init == m_init); + /* For now we don't worry about comparing value->m_copy. */ + assert(def->m_base.m_copy == NULL || m_dict != NULL); + assert((origin == _Py_ext_module_origin_DYNAMIC) == (name != path)); + assert(origin != _Py_ext_module_origin_CORE || m_dict == NULL); + extensions_lock_acquire(); if (EXTENSIONS.hashtable == NULL) { @@ -1059,43 +1376,82 @@ _extensions_cache_set(PyObject *path, PyObject *name, PyModuleDef *def) } } - int already_set = 0; - void *key = NULL; + /* Create a cached value to populate for the module. */ _Py_hashtable_entry_t *entry = _extensions_cache_find_unlocked(path, name, &key); + value = entry == NULL + ? NULL + : (struct extensions_cache_value *)entry->value; + /* We should never be updating an existing cache value. */ + assert(value == NULL); + if (value != NULL) { + PyErr_Format(PyExc_SystemError, + "extension module %R is already cached", name); + goto finally; + } + newvalue = alloc_extensions_cache_value(); + if (newvalue == NULL) { + goto finally; + } + + /* Populate the new cache value data. */ + *newvalue = (struct extensions_cache_value){ + .def=def, + .m_init=m_init, + .m_index=m_index, + /* m_dict is set by set_cached_m_dict(). */ + .origin=origin, + }; + if (init_cached_m_dict(newvalue, m_dict) < 0) { + goto finally; + } + fixup_cached_def(newvalue); + if (entry == NULL) { /* It was never added. */ - if (_Py_hashtable_set(EXTENSIONS.hashtable, key, def) < 0) { + if (_Py_hashtable_set(EXTENSIONS.hashtable, key, newvalue) < 0) { PyErr_NoMemory(); goto finally; } /* The hashtable owns the key now. */ key = NULL; } - else if (entry->value == NULL) { + else if (value == NULL) { /* It was previously deleted. */ - entry->value = def; + entry->value = newvalue; } else { - /* We expect it to be static, so it must be the same pointer. */ - assert((PyModuleDef *)entry->value == def); - /* It was already added. */ - already_set = 1; + /* We are updating the entry for an existing module. */ + /* We expect def to be static, so it must be the same pointer. */ + assert(value->def == def); + /* We expect the same symbol to be used and the shared object file + * to have remained loaded, so it must be the same pointer. */ + assert(value->m_init == m_init); + /* The same module can't switch between caching __dict__ and not. */ + assert((value->m_dict == NULL) == (m_dict == NULL)); + /* This shouldn't ever happen. */ + Py_UNREACHABLE(); } - if (!already_set) { - /* We assume that all module defs are statically allocated - and will never be freed. Otherwise, we would incref here. */ - _Py_SetImmortal((PyObject *)def); - } - res = 0; + value = newvalue; finally: + if (value == NULL) { + restore_old_cached_def(def, &olddefbase); + if (newvalue != NULL) { + del_extensions_cache_value(newvalue); + } + } + else { + cleanup_old_cached_def(&olddefbase); + } + extensions_lock_release(); if (key != NULL) { hashtable_destroy_str(key); } - return res; + + return value; } static void @@ -1118,13 +1474,11 @@ _extensions_cache_delete(PyObject *path, PyObject *name) /* It was already removed. */ goto finally; } - /* If we hadn't made the stored defs immortal, we would decref here. - However, this decref would be problematic if the module def were - dynamically allocated, it were the last ref, and this function - were called with an interpreter other than the def's owner. */ - assert(_Py_IsImmortal(entry->value)); + struct extensions_cache_value *value = entry->value; entry->value = NULL; + del_extensions_cache_value(value); + finally: extensions_lock_release(); } @@ -1180,11 +1534,11 @@ get_core_module_dict(PyInterpreterState *interp, if (path == name) { assert(!PyErr_Occurred()); if (PyUnicode_CompareWithASCIIString(name, "sys") == 0) { - return interp->sysdict_copy; + return Py_NewRef(interp->sysdict_copy); } assert(!PyErr_Occurred()); if (PyUnicode_CompareWithASCIIString(name, "builtins") == 0) { - return interp->builtins_copy; + return Py_NewRef(interp->builtins_copy); } assert(!PyErr_Occurred()); } @@ -1207,6 +1561,7 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) return 0; } + #ifndef NDEBUG static _Py_ext_module_kind _get_extension_kind(PyModuleDef *def, bool check_size) @@ -1252,29 +1607,39 @@ _get_extension_kind(PyModuleDef *def, bool check_size) || kind == _Py_ext_module_kind_UNKNOWN); \ } while (0) -#define assert_singlephase(def) \ - assert_singlephase_def(def) +#define assert_singlephase(cached) \ + do { \ + _Py_ext_module_kind kind = _get_extension_kind(cached->def, true); \ + assert(kind == _Py_ext_module_kind_SINGLEPHASE); \ + } while (0) #else /* defined(NDEBUG) */ #define assert_multiphase_def(def) #define assert_singlephase_def(def) -#define assert_singlephase(def) +#define assert_singlephase(cached) #endif struct singlephase_global_update { - PyObject *m_dict; PyModInitFunction m_init; + Py_ssize_t m_index; + PyObject *m_dict; + _Py_ext_module_origin origin; }; -static int +static struct extensions_cache_value * update_global_state_for_extension(PyThreadState *tstate, PyObject *path, PyObject *name, PyModuleDef *def, struct singlephase_global_update *singlephase) { - /* Copy the module's __dict__, if applicable. */ + struct extensions_cache_value *cached = NULL; + PyModInitFunction m_init = NULL; + PyObject *m_dict = NULL; + + /* Set up for _extensions_cache_set(). */ if (singlephase == NULL) { + assert(def->m_base.m_init == NULL); assert(def->m_base.m_copy == NULL); } else { @@ -1288,7 +1653,7 @@ update_global_state_for_extension(PyThreadState *tstate, // We should prevent this somehow. The simplest solution // is probably to store m_copy/m_init in the cache along // with the def, rather than within the def. - def->m_base.m_init = singlephase->m_init; + m_init = singlephase->m_init; } else if (singlephase->m_dict == NULL) { /* It must be a core builtin module. */ @@ -1305,32 +1670,7 @@ update_global_state_for_extension(PyThreadState *tstate, assert(!is_core_module(tstate->interp, name, path)); assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0); assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0); - /* XXX gh-88216: The copied dict is owned by the current - * interpreter. That's a problem if the interpreter has - * its own obmalloc state or if the module is successfully - * imported into such an interpreter. If the interpreter - * has its own GIL then there may be data races and - * PyImport_ClearModulesByIndex() can crash. Normally, - * a single-phase init module cannot be imported in an - * isolated interpreter, but there are ways around that. - * Hence, heere be dragons! Ideally we would instead do - * something like make a read-only, immortal copy of the - * dict using PyMem_RawMalloc() and store *that* in m_copy. - * Then we'd need to make sure to clear that when the - * runtime is finalized, rather than in - * PyImport_ClearModulesByIndex(). */ - if (def->m_base.m_copy) { - /* Somebody already imported the module, - likely under a different name. - XXX this should really not happen. */ - Py_CLEAR(def->m_base.m_copy); - } - def->m_base.m_copy = PyDict_Copy(singlephase->m_dict); - if (def->m_base.m_copy == NULL) { - // XXX Ignore this error? Doing so would effectively - // mark the module as not loadable. */ - return -1; - } + m_dict = singlephase->m_dict; } } @@ -1338,28 +1678,34 @@ update_global_state_for_extension(PyThreadState *tstate, // XXX Why special-case the main interpreter? if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) { #ifndef NDEBUG - PyModuleDef *cached = _extensions_cache_get(path, name); - assert(cached == NULL || cached == def); + cached = _extensions_cache_get(path, name); + assert(cached == NULL || cached->def == def); #endif - if (_extensions_cache_set(path, name, def) < 0) { - return -1; + cached = _extensions_cache_set( + path, name, def, m_init, singlephase->m_index, m_dict, + singlephase->origin); + if (cached == NULL) { + // XXX Ignore this error? Doing so would effectively + // mark the module as not loadable. + return NULL; } } - return 0; + return cached; } /* For multi-phase init modules, the module is finished * by PyModule_FromDefAndSpec(). */ static int -finish_singlephase_extension(PyThreadState *tstate, - PyObject *mod, PyModuleDef *def, +finish_singlephase_extension(PyThreadState *tstate, PyObject *mod, + struct extensions_cache_value *cached, PyObject *name, PyObject *modules) { assert(mod != NULL && PyModule_Check(mod)); - assert(def == _PyModule_GetDef(mod)); + assert(cached->def == _PyModule_GetDef(mod)); - if (_modules_by_index_set(tstate->interp, def, mod) < 0) { + Py_ssize_t index = _get_cached_module_index(cached); + if (_modules_by_index_set(tstate->interp, index, mod) < 0) { return -1; } @@ -1374,10 +1720,13 @@ finish_singlephase_extension(PyThreadState *tstate, static PyObject * -reload_singlephase_extension(PyThreadState *tstate, PyModuleDef *def, +reload_singlephase_extension(PyThreadState *tstate, + struct extensions_cache_value *cached, struct _Py_ext_module_loader_info *info) { - assert_singlephase(def); + PyModuleDef *def = cached->def; + assert(def != NULL); + assert_singlephase(cached); PyObject *mod = NULL; /* It may have been successfully imported previously @@ -1391,46 +1740,53 @@ reload_singlephase_extension(PyThreadState *tstate, PyModuleDef *def, PyObject *modules = get_modules_dict(tstate, true); if (def->m_size == -1) { - PyObject *m_copy = def->m_base.m_copy; /* Module does not support repeated initialization */ + assert(cached->m_init == NULL); + assert(def->m_base.m_init == NULL); + // XXX Copying the cached dict may break interpreter isolation. + // We could solve this by temporarily acquiring the original + // interpreter's GIL. + PyObject *m_copy = get_cached_m_dict(cached, info->name, info->path); if (m_copy == NULL) { - /* It might be a core module (e.g. sys & builtins), - for which we don't set m_copy. */ - m_copy = get_core_module_dict( - tstate->interp, info->name, info->path); - if (m_copy == NULL) { - assert(!PyErr_Occurred()); - return NULL; - } + assert(!PyErr_Occurred()); + return NULL; } mod = import_add_module(tstate, info->name); if (mod == NULL) { + Py_DECREF(m_copy); return NULL; } PyObject *mdict = PyModule_GetDict(mod); if (mdict == NULL) { + Py_DECREF(m_copy); Py_DECREF(mod); return NULL; } - if (PyDict_Update(mdict, m_copy)) { + int rc = PyDict_Update(mdict, m_copy); + Py_DECREF(m_copy); + if (rc < 0) { Py_DECREF(mod); return NULL; } /* We can't set mod->md_def if it's missing, * because _PyImport_ClearModulesByIndex() might break - * due to violating interpreter isolation. See the note - * in fix_up_extension_for_interpreter(). Until that - * is solved, we leave md_def set to NULL. */ + * due to violating interpreter isolation. + * See the note in set_cached_m_dict(). + * Until that is solved, we leave md_def set to NULL. */ assert(_PyModule_GetDef(mod) == NULL || _PyModule_GetDef(mod) == def); } else { - if (def->m_base.m_init == NULL) { + assert(cached->m_dict == NULL); + assert(def->m_base.m_copy == NULL); + // XXX Use cached->m_init. + PyModInitFunction p0 = def->m_base.m_init; + if (p0 == NULL) { assert(!PyErr_Occurred()); return NULL; } struct _Py_ext_module_loader_result res; - if (_PyImport_RunModInitFunc(def->m_base.m_init, info, &res) < 0) { + if (_PyImport_RunModInitFunc(p0, info, &res) < 0) { _Py_ext_module_loader_result_apply_error(&res, name_buf); return NULL; } @@ -1457,7 +1813,8 @@ reload_singlephase_extension(PyThreadState *tstate, PyModuleDef *def, } } - if (_modules_by_index_set(tstate->interp, def, mod) < 0) { + Py_ssize_t index = _get_cached_module_index(cached); + if (_modules_by_index_set(tstate->interp, index, mod) < 0) { PyMapping_DelItem(modules, info->name); Py_DECREF(mod); return NULL; @@ -1468,14 +1825,18 @@ reload_singlephase_extension(PyThreadState *tstate, PyModuleDef *def, static PyObject * import_find_extension(PyThreadState *tstate, - struct _Py_ext_module_loader_info *info) + struct _Py_ext_module_loader_info *info, + struct extensions_cache_value **p_cached) { /* Only single-phase init modules will be in the cache. */ - PyModuleDef *def = _extensions_cache_get(info->path, info->name); - if (def == NULL) { + struct extensions_cache_value *cached + = _extensions_cache_get(info->path, info->name); + if (cached == NULL) { return NULL; } - assert_singlephase(def); + assert(cached->def != NULL); + assert_singlephase(cached); + *p_cached = cached; /* It may have been successfully imported previously in an interpreter that allows legacy modules @@ -1486,7 +1847,7 @@ import_find_extension(PyThreadState *tstate, return NULL; } - PyObject *mod = reload_singlephase_extension(tstate, def, info); + PyObject *mod = reload_singlephase_extension(tstate, cached, info); if (mod == NULL) { return NULL; } @@ -1496,6 +1857,7 @@ import_find_extension(PyThreadState *tstate, PySys_FormatStderr("import %U # previously loaded (%R)\n", info->name, info->path); } + return mod; } @@ -1509,6 +1871,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, PyObject *mod = NULL; PyModuleDef *def = NULL; + struct extensions_cache_value *cached = NULL; const char *name_buf = PyBytes_AS_STRING(info->name_encoded); struct _Py_ext_module_loader_result res; @@ -1551,7 +1914,14 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, } /* Update global import state. */ - struct singlephase_global_update singlephase = {0}; + assert(def->m_base.m_index != 0); + struct singlephase_global_update singlephase = { + // XXX Modules that share a def should each get their own index, + // whereas currently they share (which means the per-interpreter + // cache is less reliable than it should be). + .m_index=def->m_base.m_index, + .origin=info->origin, + }; // gh-88216: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. if (def->m_size == -1) { @@ -1563,18 +1933,19 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, else { /* We will reload via the init function. */ assert(def->m_size >= 0); + assert(def->m_base.m_copy == NULL); singlephase.m_init = p0; } - if (update_global_state_for_extension( - tstate, info->path, info->name, def, &singlephase) < 0) - { + cached = update_global_state_for_extension( + tstate, info->path, info->name, def, &singlephase); + if (cached == NULL) { goto error; } /* Update per-interpreter import state. */ PyObject *modules = get_modules_dict(tstate, true); if (finish_singlephase_extension( - tstate, mod, def, info->name, modules) < 0) + tstate, mod, cached, info->name, modules) < 0) { goto error; } @@ -1594,13 +1965,14 @@ static int clear_singlephase_extension(PyInterpreterState *interp, PyObject *name, PyObject *path) { - PyModuleDef *def = _extensions_cache_get(path, name); - if (def == NULL) { + struct extensions_cache_value *cached = _extensions_cache_get(path, name); + if (cached == NULL) { if (PyErr_Occurred()) { return -1; } return 0; } + PyModuleDef *def = cached->def; /* Clear data set when the module was initially loaded. */ def->m_base.m_init = NULL; @@ -1608,8 +1980,9 @@ clear_singlephase_extension(PyInterpreterState *interp, // We leave m_index alone since there's no reason to reset it. /* Clear the PyState_*Module() cache entry. */ - if (_modules_by_index_check(interp, def->m_base.m_index) == NULL) { - if (_modules_by_index_clear_one(interp, def) < 0) { + Py_ssize_t index = _get_cached_module_index(cached); + if (_modules_by_index_check(interp, index) == NULL) { + if (_modules_by_index_clear_one(interp, index) < 0) { return -1; } } @@ -1652,18 +2025,29 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, assert_singlephase_def(def); assert(def->m_size == -1); assert(def->m_base.m_copy == NULL); - - struct singlephase_global_update singlephase = { - /* We don't want def->m_base.m_copy populated. */ - .m_dict=NULL, - }; - if (update_global_state_for_extension( - tstate, nameobj, nameobj, def, &singlephase) < 0) - { - goto finally; + assert(def->m_base.m_index >= 0); + + /* We aren't using import_find_extension() for core modules, + * so we have to do the extra check to make sure the module + * isn't already in the global cache before calling + * update_global_state_for_extension(). */ + struct extensions_cache_value *cached + = _extensions_cache_get(nameobj, nameobj); + if (cached == NULL) { + struct singlephase_global_update singlephase = { + .m_index=def->m_base.m_index, + /* We don't want def->m_base.m_copy populated. */ + .m_dict=NULL, + .origin=_Py_ext_module_origin_CORE, + }; + cached = update_global_state_for_extension( + tstate, nameobj, nameobj, def, &singlephase); + if (cached == NULL) { + goto finally; + } } - if (finish_singlephase_extension(tstate, mod, def, nameobj, modules) < 0) { + if (finish_singlephase_extension(tstate, mod, cached, nameobj, modules) < 0) { goto finally; } @@ -1700,16 +2084,30 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) return NULL; } - PyObject *mod = import_find_extension(tstate, &info); + struct extensions_cache_value *cached = NULL; + PyObject *mod = import_find_extension(tstate, &info, &cached); if (mod != NULL) { assert(!_PyErr_Occurred(tstate)); - assert_singlephase(_PyModule_GetDef(mod)); + assert(cached != NULL); + /* The module might not have md_def set in certain reload cases. */ + assert(_PyModule_GetDef(mod) == NULL + || cached->def == _PyModule_GetDef(mod)); + assert_singlephase(cached); goto finally; } else if (_PyErr_Occurred(tstate)) { goto finally; } + /* If the module was added to the global cache + * but def->m_base.m_copy was cleared (e.g. subinterp fini) + * then we have to do a little dance here. */ + if (cached != NULL) { + assert(cached->def->m_base.m_copy == NULL); + /* For now we clear the cache and move on. */ + _extensions_cache_delete(info.path, info.name); + } + struct _inittab *found = NULL; for (struct _inittab *p = INITTAB; p->name != NULL; p++) { if (_PyUnicode_EqualToASCIIString(info.name, p->name)) { @@ -4054,10 +4452,15 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) return NULL; } - mod = import_find_extension(tstate, &info); + struct extensions_cache_value *cached = NULL; + mod = import_find_extension(tstate, &info, &cached); if (mod != NULL) { assert(!_PyErr_Occurred(tstate)); - assert_singlephase(_PyModule_GetDef(mod)); + assert(cached != NULL); + /* The module might not have md_def set in certain reload cases. */ + assert(_PyModule_GetDef(mod) == NULL + || cached->def == _PyModule_GetDef(mod)); + assert_singlephase(cached); goto finally; } else if (_PyErr_Occurred(tstate)) { @@ -4065,6 +4468,15 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } /* Otherwise it must be multi-phase init or the first time it's loaded. */ + /* If the module was added to the global cache + * but def->m_base.m_copy was cleared (e.g. subinterp fini) + * then we have to do a little dance here. */ + if (cached != NULL) { + assert(cached->def->m_base.m_copy == NULL); + /* For now we clear the cache and move on. */ + _extensions_cache_delete(info.path, info.name); + } + if (PySys_Audit("import", "OOOOO", info.name, info.filename, Py_None, Py_None, Py_None) < 0) { diff --git a/Python/importdl.c b/Python/importdl.c index a3091946bc94bf..38f56db4b4cc96 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -111,9 +111,12 @@ _Py_ext_module_loader_info_clear(struct _Py_ext_module_loader_info *info) int _Py_ext_module_loader_info_init(struct _Py_ext_module_loader_info *p_info, - PyObject *name, PyObject *filename) + PyObject *name, PyObject *filename, + _Py_ext_module_origin origin) { - struct _Py_ext_module_loader_info info = {0}; + struct _Py_ext_module_loader_info info = { + .origin=origin, + }; assert(name != NULL); if (!PyUnicode_Check(name)) { @@ -183,12 +186,25 @@ _Py_ext_module_loader_info_init_for_builtin( .name_encoded=name_encoded, /* We won't need filename. */ .path=name, + .origin=_Py_ext_module_origin_BUILTIN, .hook_prefix=ascii_only_prefix, .newcontext=NULL, }; return 0; } +int +_Py_ext_module_loader_info_init_for_core( + struct _Py_ext_module_loader_info *info, + PyObject *name) +{ + if (_Py_ext_module_loader_info_init_for_builtin(info, name) < 0) { + return -1; + } + info->origin = _Py_ext_module_origin_CORE; + return 0; +} + int _Py_ext_module_loader_info_init_from_spec( struct _Py_ext_module_loader_info *p_info, @@ -203,7 +219,9 @@ _Py_ext_module_loader_info_init_from_spec( Py_DECREF(name); return -1; } - int err = _Py_ext_module_loader_info_init(p_info, name, filename); + /* We could also accommodate builtin modules here without much trouble. */ + _Py_ext_module_origin origin = _Py_ext_module_origin_DYNAMIC; + int err = _Py_ext_module_loader_info_init(p_info, name, filename, origin); Py_DECREF(name); Py_DECREF(filename); return err; From 08d169f14a715ceaae3d563ced2ff1633d009359 Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Sat, 4 May 2024 23:41:47 +0200 Subject: [PATCH 213/217] gh-109617: fix ncurses incompatibility on macOS with Xcode 15 (#111258) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Łukasz Langa --- Include/py_curses.h | 19 +++++-- ...-10-24-12-39-04.gh-issue-109617.YoI8TV.rst | 2 + Modules/_cursesmodule.c | 17 ++++-- configure | 55 +++++++++++++++---- configure.ac | 5 +- 5 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst diff --git a/Include/py_curses.h b/Include/py_curses.h index e46b08e9cc414e..a51d9980eee401 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -23,10 +23,16 @@ # endif #endif -#if !defined(HAVE_CURSES_IS_PAD) && defined(WINDOW_HAS_FLAGS) -/* The following definition is necessary for ncurses 5.7; without it, - some of [n]curses.h set NCURSES_OPAQUE to 1, and then Python - can't get at the WINDOW flags field. */ +#if defined(WINDOW_HAS_FLAGS) && defined(__APPLE__) +/* gh-109617, gh-115383: we can rely on the default value for NCURSES_OPAQUE on + most platforms, but not on macOS. This is because, starting with Xcode 15, + Apple-provided ncurses.h comes from ncurses 6 (which defaults to opaque + structs) but can still be linked to older versions of ncurses dynamic + libraries which don't provide functions such as is_pad() to deal with opaque + structs. Setting NCURSES_OPAQUE to 0 is harmless in all ncurses releases to + this date (provided that a thread-safe implementation is not required), but + this might change in the future. This fix might become irrelevant once + support for macOS 13 or earlier is dropped. */ #define NCURSES_OPAQUE 0 #endif @@ -39,7 +45,10 @@ #ifdef HAVE_NCURSES_H /* configure was checking , but we will use , which has some or all these features. */ -#if !defined(WINDOW_HAS_FLAGS) && !(NCURSES_OPAQUE+0) +#if !defined(WINDOW_HAS_FLAGS) && \ + (NCURSES_VERSION_PATCH+0 < 20070303 || !(NCURSES_OPAQUE+0)) +/* the WINDOW flags field was always accessible in ncurses prior to 20070303; + after that, it depends on the value of NCURSES_OPAQUE. */ #define WINDOW_HAS_FLAGS 1 #endif #if !defined(HAVE_CURSES_IS_PAD) && NCURSES_VERSION_PATCH+0 >= 20090906 diff --git a/Misc/NEWS.d/next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst b/Misc/NEWS.d/next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst new file mode 100644 index 00000000000000..4fda69d332d50a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst @@ -0,0 +1,2 @@ +:mod:`ncurses`: fixed a crash that could occur on macOS 13 or earlier when +Python was built with Apple Xcode 15's SDK. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 8bf6824b6d83ff..69b97042b89dc7 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1156,8 +1156,10 @@ int py_mvwdelch(WINDOW *w, int y, int x) #endif #if defined(HAVE_CURSES_IS_PAD) +// is_pad() is defined, either as a macro or as a function #define py_is_pad(win) is_pad(win) #elif defined(WINDOW_HAS_FLAGS) +// is_pad() is not defined, but we can inspect WINDOW structure members #define py_is_pad(win) ((win) ? ((win)->_flags & _ISPAD) != 0 : FALSE) #endif @@ -4586,7 +4588,14 @@ make_ncurses_version(PyTypeObject *type) if (ncurses_version == NULL) { return NULL; } - + const char *str = curses_version(); + unsigned long major = 0, minor = 0, patch = 0; + if (!str || sscanf(str, "%*[^0-9]%lu.%lu.%lu", &major, &minor, &patch) < 3) { + // Fallback to header version, which cannot be that wrong + major = NCURSES_VERSION_MAJOR; + minor = NCURSES_VERSION_MINOR; + patch = NCURSES_VERSION_PATCH; + } #define SetIntItem(flag) \ PyStructSequence_SET_ITEM(ncurses_version, pos++, PyLong_FromLong(flag)); \ if (PyErr_Occurred()) { \ @@ -4594,9 +4603,9 @@ make_ncurses_version(PyTypeObject *type) return NULL; \ } - SetIntItem(NCURSES_VERSION_MAJOR) - SetIntItem(NCURSES_VERSION_MINOR) - SetIntItem(NCURSES_VERSION_PATCH) + SetIntItem(major) + SetIntItem(minor) + SetIntItem(patch) #undef SetIntItem return ncurses_version; diff --git a/configure b/configure index cc85aed2aa51c2..0cd137198d0bb7 100755 --- a/configure +++ b/configure @@ -26781,7 +26781,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26824,7 +26827,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26867,7 +26873,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26910,7 +26919,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26953,7 +26965,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26996,7 +27011,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -27039,7 +27057,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -27082,7 +27103,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -27125,7 +27149,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -27168,7 +27195,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -27211,7 +27241,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { diff --git a/configure.ac b/configure.ac index c55e33add20fde..ae594517df5365 100644 --- a/configure.ac +++ b/configure.ac @@ -6713,7 +6713,10 @@ AC_DEFUN([PY_CHECK_CURSES_FUNC], [py_var], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( - [@%:@include ], [ + [ + #define NCURSES_OPAQUE 0 + #include + ], [ #ifndef $1 void *x=$1 #endif From 999f0c512281995fb61a0d9eda075fd846e8c505 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sat, 4 May 2024 18:22:33 -0500 Subject: [PATCH 214/217] gh-118164: str(10**10000) hangs if the C _decimal module is missing (#118503) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial stab. * Test the tentative fix. Hangs "forever" without this change. * Move the new test to a better spot. * New comment to explain why _convert_to_str allows any poewr of 10. * Fixed a comment, and fleshed out an existing test that appeared unfinished. * Added temporary asserts. Or maybe permanent ;-) * Update Lib/_pydecimal.py Co-authored-by: Serhiy Storchaka * Remove the new _convert_to_str(). Serhiy and I independently concluded that exact powers of 10 aren't possible in these contexts, so just checking the string length is sufficient. * At least for now, add the asserts to the other block too. * 📜🤖 Added by blurb_it. --------- Co-authored-by: Serhiy Storchaka Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/_pydecimal.py | 19 +++++++++++--- Lib/test/test_decimal.py | 25 ++++++++++++++++++- ...-05-04-20-22-59.gh-issue-118164.9D02MQ.rst | 1 + 3 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-05-04-20-22-59.gh-issue-118164.9D02MQ.rst diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index de4561a5ee050b..613123ec7b4329 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -2131,10 +2131,16 @@ def _power_exact(self, other, p): else: return None - if xc >= 10**p: + # An exact power of 10 is representable, but can convert to a + # string of any length. But an exact power of 10 shouldn't be + # possible at this point. + assert xc > 1, self + assert xc % 10 != 0, self + strxc = str(xc) + if len(strxc) > p: return None xe = -e-xe - return _dec_from_triple(0, str(xc), xe) + return _dec_from_triple(0, strxc, xe) # now y is positive; find m and n such that y = m/n if ye >= 0: @@ -2184,13 +2190,18 @@ def _power_exact(self, other, p): return None xc = xc**m xe *= m - if xc > 10**p: + # An exact power of 10 is representable, but can convert to a string + # of any length. But an exact power of 10 shouldn't be possible at + # this point. + assert xc > 1, self + assert xc % 10 != 0, self + str_xc = str(xc) + if len(str_xc) > p: return None # by this point the result *is* exactly representable # adjust the exponent to get as close as possible to the ideal # exponent, if necessary - str_xc = str(xc) if other._isinteger() and other._sign == 0: ideal_exponent = self._exp*int(other) zeros = min(xe-ideal_exponent, p-len(str_xc)) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 7010c34792e093..e927e24b582a5d 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -4716,9 +4716,33 @@ def test_py_exact_power(self): c.prec = 1 x = Decimal("152587890625") ** Decimal('-0.5') + self.assertEqual(x, Decimal('3e-6')) + c.prec = 2 + x = Decimal("152587890625") ** Decimal('-0.5') + self.assertEqual(x, Decimal('2.6e-6')) + c.prec = 3 + x = Decimal("152587890625") ** Decimal('-0.5') + self.assertEqual(x, Decimal('2.56e-6')) + c.prec = 28 + x = Decimal("152587890625") ** Decimal('-0.5') + self.assertEqual(x, Decimal('2.56e-6')) + c.prec = 201 x = Decimal(2**578) ** Decimal("-0.5") + # See https://github.com/python/cpython/issues/118027 + # Testing for an exact power could appear to hang, in the Python + # version, as it attempted to compute 10**(MAX_EMAX + 1). + # Fixed via https://github.com/python/cpython/pull/118503. + c.prec = P.MAX_PREC + c.Emax = P.MAX_EMAX + c.Emin = P.MIN_EMIN + c.traps[P.Inexact] = 1 + D2 = Decimal(2) + # If the bug is still present, the next statement won't complete. + res = D2 ** 117 + self.assertEqual(res, 1 << 117) + def test_py_immutability_operations(self): # Do operations and check that it didn't change internal objects. Decimal = P.Decimal @@ -5705,7 +5729,6 @@ def test_format_fallback_rounding(self): with C.localcontext(rounding=C.ROUND_DOWN): self.assertEqual(format(y, '#.1f'), '6.0') - @requires_docstrings @requires_cdecimal class SignatureTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-05-04-20-22-59.gh-issue-118164.9D02MQ.rst b/Misc/NEWS.d/next/Library/2024-05-04-20-22-59.gh-issue-118164.9D02MQ.rst new file mode 100644 index 00000000000000..80dc868540418f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-04-20-22-59.gh-issue-118164.9D02MQ.rst @@ -0,0 +1 @@ +The Python implementation of the ``decimal`` module could appear to hang in relatively small power cases (like ``2**117``) if context precision was set to a very high value. A different method to check for exactly representable results is used now that doesn't rely on computing ``10**precision`` (which could be effectively too large to compute). From 1b22d801b86ed314c4804b19a1fc4b13484e3cea Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Sun, 5 May 2024 03:07:29 +0200 Subject: [PATCH 215/217] gh-118518: Allow perf to work without frame pointers (#112254) --- Doc/c-api/init_config.rst | 5 +- Doc/howto/perf_profiling.rst | 33 + Doc/using/cmdline.rst | 24 + Doc/whatsnew/3.13.rst | 5 + Include/internal/pycore_ceval.h | 1 + Include/internal/pycore_ceval_state.h | 2 + Lib/test/test_perf_profiler.py | 146 ++++- Makefile.pre.in | 1 + ...-05-02-20-32-42.gh-issue-118518.m-JbTi.rst | 4 + PCbuild/_freeze_module.vcxproj | 1 + PCbuild/_freeze_module.vcxproj.filters | 3 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 + Python/initconfig.c | 14 + Python/perf_jit_trampoline.c | 615 ++++++++++++++++++ Python/perf_trampoline.c | 52 +- Python/pylifecycle.c | 9 +- Python/sysmodule.c | 10 + Tools/c-analyzer/cpython/ignored.tsv | 2 + 19 files changed, 892 insertions(+), 39 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-05-02-20-32-42.gh-issue-118518.m-JbTi.rst create mode 100644 Python/perf_jit_trampoline.c diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 47a8fbb2cd9c97..63ec25b8e60bad 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -1251,7 +1251,10 @@ PyConfig for more information. Set by :option:`-X perf <-X>` command line option and by the - :envvar:`PYTHONPERFSUPPORT` environment variable. + :envvar:`PYTHONPERFSUPPORT` environment variable for perf support + with stack pointers and :option:`-X perfjit <-X>` command line option + and by the :envvar:`PYTHONPERFJITSUPPORT` environment variable for perf + support with DWARF JIT information. Default: ``-1``. diff --git a/Doc/howto/perf_profiling.rst b/Doc/howto/perf_profiling.rst index bb1c00e0aa51d5..0ce66f31274a5f 100644 --- a/Doc/howto/perf_profiling.rst +++ b/Doc/howto/perf_profiling.rst @@ -205,3 +205,36 @@ You can check if your system has been compiled with this flag by running:: If you don't see any output it means that your interpreter has not been compiled with frame pointers and therefore it may not be able to show Python functions in the output of ``perf``. + + +How to work without frame pointers +---------------------------------- + +If you are working with a Python interpreter that has been compiled without frame pointers +you can still use the ``perf`` profiler but the overhead will be a bit higher because Python +needs to generate unwinding information for every Python function call on the fly. Additionally, +``perf`` will take more time to process the data because it will need to use the DWARF debugging +information to unwind the stack and this is a slow process. + +To enable this mode, you can use the environment variable :envvar:`PYTHONPERFJITSUPPORT` or the +:option:`-X perfjit <-X>` option, which will enable the JIT mode for the ``perf`` profiler. + +When using the perf JIT mode, you need an extra step before you can run ``perf report``. You need to +call the ``perf inject`` command to inject the JIT information into the ``perf.data`` file. + + $ perf record -F 9999 -g --call-graph dwarf -o perf.data python -Xperfjit my_script.py + $ perf inject -i perf.data --jit + $ perf report -g -i perf.data + +or using the environment variable:: + + $ PYTHONPERFJITSUPPORT=1 perf record -F 9999 -g --call-graph dwarf -o perf.data python my_script.py + $ perf inject -i perf.data --jit + $ perf report -g -i perf.data + +Notice that when using ``--call-graph dwarf`` the ``perf`` tool will take snapshots of the stack of +the process being profiled and save the information in the ``perf.data`` file. By default the size of +the stack dump is 8192 bytes but the user can change the size by passing the size after comma like +``--call-graph dwarf,4096``. The size of the stack dump is important because if the size is too small +``perf`` will not be able to unwind the stack and the output will be incomplete. + diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 1f5d02d1ea54a2..051dbf94a95366 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -586,6 +586,15 @@ Miscellaneous options .. versionadded:: 3.12 + * ``-X perfjit`` enables support for the Linux ``perf`` profiler with DWARF + support. When this option is provided, the ``perf`` profiler will be able + to report Python calls using DWARF ifnormation. This option is only available on + some platforms and will do nothing if is not supported on the current + system. The default value is "off". See also :envvar:`PYTHONPERFJITSUPPORT` + and :ref:`perf_profiling`. + + .. versionadded:: 3.13 + * :samp:`-X cpu_count={n}` overrides :func:`os.cpu_count`, :func:`os.process_cpu_count`, and :func:`multiprocessing.cpu_count`. *n* must be greater than or equal to 1. @@ -1127,6 +1136,21 @@ conflict. .. versionadded:: 3.12 +.. envvar:: PYTHONPERFJITSUPPORT + + If this variable is set to a nonzero value, it enables support for + the Linux ``perf`` profiler so Python calls can be detected by it + using DWARF information. + + If set to ``0``, disable Linux ``perf`` profiler support. + + See also the :option:`-X perfjit <-X>` command-line option + and :ref:`perf_profiling`. + + .. versionadded:: 3.13 + + + .. envvar:: PYTHON_CPU_COUNT If this variable is set to a positive integer, it overrides the return diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 269a7cc985ad19..a5f5ba41ebf66e 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -231,6 +231,11 @@ Other Language Changes equivalent of the :option:`-X frozen_modules <-X>` command-line option. (Contributed by Yilei Yang in :gh:`111374`.) +* Add :ref:`support for the perf profiler ` working without + frame pointers through the new environment variable + :envvar:`PYTHONPERFJITSUPPORT` and command-line option :option:`-X perfjit + <-X>` (Contributed by Pablo Galindo in :gh:`118518`.) + * The new :envvar:`PYTHON_HISTORY` environment variable can be used to change the location of a ``.python_history`` file. (Contributed by Levi Sabah, Zackery Spytz and Hugo van Kemenade in diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index cfb88c3f4c8e15..904fbd1a389f41 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -108,6 +108,7 @@ extern int _PyIsPerfTrampolineActive(void); extern PyStatus _PyPerfTrampoline_AfterFork_Child(void); #ifdef PY_HAVE_PERF_TRAMPOLINE extern _PyPerf_Callbacks _Py_perfmap_callbacks; +extern _PyPerf_Callbacks _Py_perfmap_jit_callbacks; #endif static inline PyObject* diff --git a/Include/internal/pycore_ceval_state.h b/Include/internal/pycore_ceval_state.h index 376d96ad5d334c..009a1ea41eb985 100644 --- a/Include/internal/pycore_ceval_state.h +++ b/Include/internal/pycore_ceval_state.h @@ -75,6 +75,7 @@ struct trampoline_api_st { unsigned int code_size, PyCodeObject* code); int (*free_state)(void* state); void *state; + Py_ssize_t code_padding; }; #endif @@ -83,6 +84,7 @@ struct _ceval_runtime_state { struct { #ifdef PY_HAVE_PERF_TRAMPOLINE perf_status_t status; + int perf_trampoline_type; Py_ssize_t extra_code_index; struct code_arena_st *code_arena; struct trampoline_api_st trampoline_api; diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py index e7c03b99086013..9f72a46dc91c35 100644 --- a/Lib/test/test_perf_profiler.py +++ b/Lib/test/test_perf_profiler.py @@ -5,6 +5,7 @@ import sysconfig import os import pathlib +import shutil from test import support from test.support.script_helper import ( make_script, @@ -76,14 +77,27 @@ def baz(): perf_file = pathlib.Path(f"/tmp/perf-{process.pid}.map") self.assertTrue(perf_file.exists()) perf_file_contents = perf_file.read_text() - perf_lines = perf_file_contents.splitlines(); - expected_symbols = [f"py::foo:{script}", f"py::bar:{script}", f"py::baz:{script}"] + perf_lines = perf_file_contents.splitlines() + expected_symbols = [ + f"py::foo:{script}", + f"py::bar:{script}", + f"py::baz:{script}", + ] for expected_symbol in expected_symbols: - perf_line = next((line for line in perf_lines if expected_symbol in line), None) - self.assertIsNotNone(perf_line, f"Could not find {expected_symbol} in perf file") + perf_line = next( + (line for line in perf_lines if expected_symbol in line), None + ) + self.assertIsNotNone( + perf_line, f"Could not find {expected_symbol} in perf file" + ) perf_addr = perf_line.split(" ")[0] - self.assertFalse(perf_addr.startswith("0x"), "Address should not be prefixed with 0x") - self.assertTrue(set(perf_addr).issubset(string.hexdigits), "Address should contain only hex characters") + self.assertFalse( + perf_addr.startswith("0x"), "Address should not be prefixed with 0x" + ) + self.assertTrue( + set(perf_addr).issubset(string.hexdigits), + "Address should contain only hex characters", + ) def test_trampoline_works_with_forks(self): code = """if 1: @@ -212,7 +226,7 @@ def test_sys_api_get_status(self): assert_python_ok("-c", code) -def is_unwinding_reliable(): +def is_unwinding_reliable_with_frame_pointers(): cflags = sysconfig.get_config_var("PY_CORE_CFLAGS") if not cflags: return False @@ -259,14 +273,27 @@ def perf_command_works(): return True -def run_perf(cwd, *args, **env_vars): +def run_perf(cwd, *args, use_jit=False, **env_vars): if env_vars: env = os.environ.copy() env.update(env_vars) else: env = None output_file = cwd + "/perf_output.perf" - base_cmd = ("perf", "record", "-g", "--call-graph=fp", "-o", output_file, "--") + if not use_jit: + base_cmd = ("perf", "record", "-g", "--call-graph=fp", "-o", output_file, "--") + else: + base_cmd = ( + "perf", + "record", + "-g", + "--call-graph=dwarf,65528", + "-F99", + "-k1", + "-o", + output_file, + "--", + ) proc = subprocess.run( base_cmd + args, stdout=subprocess.PIPE, @@ -274,9 +301,21 @@ def run_perf(cwd, *args, **env_vars): env=env, ) if proc.returncode: - print(proc.stderr) + print(proc.stderr, file=sys.stderr) raise ValueError(f"Perf failed with return code {proc.returncode}") + if use_jit: + jit_output_file = cwd + "/jit_output.dump" + command = ("perf", "inject", "-j", "-i", output_file, "-o", jit_output_file) + proc = subprocess.run( + command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=env + ) + if proc.returncode: + print(proc.stderr) + raise ValueError(f"Perf failed with return code {proc.returncode}") + # Copy the jit_output_file to the output_file + os.rename(jit_output_file, output_file) + base_cmd = ("perf", "script") proc = subprocess.run( ("perf", "script", "-i", output_file), @@ -290,20 +329,9 @@ def run_perf(cwd, *args, **env_vars): ) -@unittest.skipUnless(perf_command_works(), "perf command doesn't work") -@unittest.skipUnless(is_unwinding_reliable(), "Unwinding is unreliable") -class TestPerfProfiler(unittest.TestCase): - def setUp(self): - super().setUp() - self.perf_files = set(pathlib.Path("/tmp/").glob("perf-*.map")) - - def tearDown(self) -> None: - super().tearDown() - files_to_delete = ( - set(pathlib.Path("/tmp/").glob("perf-*.map")) - self.perf_files - ) - for file in files_to_delete: - file.unlink() +class TestPerfProfilerMixin: + def run_perf(self, script_dir, perf_mode, script): + raise NotImplementedError() def test_python_calls_appear_in_the_stack_if_perf_activated(self): with temp_dir() as script_dir: @@ -322,14 +350,14 @@ def baz(n): baz(10000000) """ script = make_script(script_dir, "perftest", code) - stdout, stderr = run_perf(script_dir, sys.executable, "-Xperf", script) + stdout, stderr = self.run_perf(script_dir, script) self.assertEqual(stderr, "") self.assertIn(f"py::foo:{script}", stdout) self.assertIn(f"py::bar:{script}", stdout) self.assertIn(f"py::baz:{script}", stdout) - def test_python_calls_do_not_appear_in_the_stack_if_perf_activated(self): + def test_python_calls_do_not_appear_in_the_stack_if_perf_deactivated(self): with temp_dir() as script_dir: code = """if 1: def foo(n): @@ -346,13 +374,38 @@ def baz(n): baz(10000000) """ script = make_script(script_dir, "perftest", code) - stdout, stderr = run_perf(script_dir, sys.executable, script) + stdout, stderr = self.run_perf( + script_dir, script, activate_trampoline=False + ) self.assertEqual(stderr, "") self.assertNotIn(f"py::foo:{script}", stdout) self.assertNotIn(f"py::bar:{script}", stdout) self.assertNotIn(f"py::baz:{script}", stdout) +@unittest.skipUnless(perf_command_works(), "perf command doesn't work") +@unittest.skipUnless( + is_unwinding_reliable_with_frame_pointers(), + "Unwinding is unreliable with frame pointers", +) +class TestPerfProfiler(unittest.TestCase, TestPerfProfilerMixin): + def run_perf(self, script_dir, script, activate_trampoline=True): + if activate_trampoline: + return run_perf(script_dir, sys.executable, "-Xperf", script) + return run_perf(script_dir, sys.executable, script) + + def setUp(self): + super().setUp() + self.perf_files = set(pathlib.Path("/tmp/").glob("perf-*.map")) + + def tearDown(self) -> None: + super().tearDown() + files_to_delete = ( + set(pathlib.Path("/tmp/").glob("perf-*.map")) - self.perf_files + ) + for file in files_to_delete: + file.unlink() + def test_pre_fork_compile(self): code = """if 1: import sys @@ -370,7 +423,7 @@ def bar_fork(): foo_fork() def foo(): - pass + import time; time.sleep(1) def bar(): foo() @@ -423,12 +476,41 @@ def compile_trampolines_for_all_functions(): # identical in both the parent and child perf-map files. perf_file_lines = perf_file_contents.split("\n") for line in perf_file_lines: - if ( - f"py::foo_fork:{script}" in line - or f"py::bar_fork:{script}" in line - ): + if f"py::foo_fork:{script}" in line or f"py::bar_fork:{script}" in line: self.assertIn(line, child_perf_file_contents) +def _is_kernel_version_at_least(major, minor): + try: + with open("/proc/version") as f: + version = f.readline().split()[2] + except FileNotFoundError: + return False + version = version.split(".") + return int(version[0]) > major or (int(version[0]) == major and int(version[1]) >= minor) + +@unittest.skipUnless(perf_command_works(), "perf command doesn't work") +@unittest.skipUnless(_is_kernel_version_at_least(6, 6), "perf command may not work due to a perf bug") +class TestPerfProfilerWithDwarf(unittest.TestCase, TestPerfProfilerMixin): + def run_perf(self, script_dir, script, activate_trampoline=True): + if activate_trampoline: + return run_perf( + script_dir, sys.executable, "-Xperfjit", script, use_jit=True + ) + return run_perf(script_dir, sys.executable, script, use_jit=True) + + def setUp(self): + super().setUp() + self.perf_files = set(pathlib.Path("/tmp/").glob("jit*.dump")) + self.perf_files |= set(pathlib.Path("/tmp/").glob("jitted-*.so")) + + def tearDown(self) -> None: + super().tearDown() + files_to_delete = set(pathlib.Path("/tmp/").glob("jit*.dump")) + files_to_delete |= set(pathlib.Path("/tmp/").glob("jitted-*.so")) + files_to_delete = files_to_delete - self.perf_files + for file in files_to_delete: + file.unlink() + if __name__ == "__main__": unittest.main() diff --git a/Makefile.pre.in b/Makefile.pre.in index bd17debf309f0e..924ed1f5621255 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -488,6 +488,7 @@ PYTHON_OBJS= \ Python/fileutils.o \ Python/suggestions.o \ Python/perf_trampoline.o \ + Python/perf_jit_trampoline.o \ Python/$(DYNLOADFILE) \ $(LIBOBJS) \ $(MACHDEP_OBJS) \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-02-20-32-42.gh-issue-118518.m-JbTi.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-20-32-42.gh-issue-118518.m-JbTi.rst new file mode 100644 index 00000000000000..7d4c003019bdef --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-20-32-42.gh-issue-118518.m-JbTi.rst @@ -0,0 +1,4 @@ +Allow the Linux perf support to work without frame pointers using perf's +advanced JIT support. The feature is activated when using the +``PYTHONPERFJITSUPPORT`` environment variable or when running Python with +``-Xperfjit``. Patch by Pablo Galindo diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj index 9717d89b54d828..e5e18de60ec349 100644 --- a/PCbuild/_freeze_module.vcxproj +++ b/PCbuild/_freeze_module.vcxproj @@ -240,6 +240,7 @@ + diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters index 9b106bea601e34..9630f54ae4ea29 100644 --- a/PCbuild/_freeze_module.vcxproj.filters +++ b/PCbuild/_freeze_module.vcxproj.filters @@ -94,6 +94,9 @@ Source Files + + Source Files + Source Files diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index a24667dc74064a..b17e782a21421e 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -609,6 +609,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 0b858cf1eb46a8..cf9bc0f4bc1c70 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -1403,6 +1403,9 @@ Python + + Python + Python diff --git a/Python/initconfig.c b/Python/initconfig.c index d91a8199b544dc..1880a28fea81f6 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -1703,6 +1703,20 @@ config_init_perf_profiling(PyConfig *config) if (xoption) { config->perf_profiling = 1; } + env = config_get_env(config, "PYTHONPERFJITSUPPORT"); + if (env) { + if (_Py_str_to_int(env, &active) != 0) { + active = 0; + } + if (active) { + config->perf_profiling = 2; + } + } + xoption = config_get_xoption(config, L"perfjit"); + if (xoption) { + config->perf_profiling = 2; + } + return _PyStatus_OK(); } diff --git a/Python/perf_jit_trampoline.c b/Python/perf_jit_trampoline.c new file mode 100644 index 00000000000000..fdce0da3eded72 --- /dev/null +++ b/Python/perf_jit_trampoline.c @@ -0,0 +1,615 @@ +#include "Python.h" +#include "pycore_ceval.h" // _PyPerf_Callbacks +#include "pycore_frame.h" +#include "pycore_interp.h" + + +#ifdef PY_HAVE_PERF_TRAMPOLINE + +#include +#include +#include +#include // mmap() +#include +#include // sysconf() +#include // gettimeofday() + +// ---------------------------------- +// Perf jitdump API +// ---------------------------------- + +typedef struct { + FILE* perf_map; + PyThread_type_lock map_lock; + void* mapped_buffer; + size_t mapped_size; + int code_id; +} PerfMapJitState; + +static PerfMapJitState perf_jit_map_state; + +/* +Usually the binary and libraries are mapped in separate region like below: + + address -> + --+---------------------+--//--+---------------------+-- + | .text | .data | ... | | .text | .data | ... | + --+---------------------+--//--+---------------------+-- + myprog libc.so + +So it'd be easy and straight-forward to find a mapped binary or library from an +address. + +But for JIT code, the code arena only cares about the code section. But the +resulting DSOs (which is generated by perf inject -j) contain ELF headers and +unwind info too. Then it'd generate following address space with synthesized +MMAP events. Let's say it has a sample between address B and C. + + sample + | + address -> A B v C + --------------------------------------------------------------------------------------------------- + /tmp/jitted-PID-0.so | (headers) | .text | unwind info | + /tmp/jitted-PID-1.so | (headers) | .text | unwind info | + /tmp/jitted-PID-2.so | (headers) | .text | unwind info | + ... + --------------------------------------------------------------------------------------------------- + +If it only maps the .text section, it'd find the jitted-PID-1.so but cannot see +the unwind info. If it maps both .text section and unwind sections, the sample +could be mapped to either jitted-PID-0.so or jitted-PID-1.so and it's confusing +which one is right. So to make perf happy we have non-overlapping ranges for each +DSO: + + address -> + ------------------------------------------------------------------------------------------------------- + /tmp/jitted-PID-0.so | (headers) | .text | unwind info | + /tmp/jitted-PID-1.so | (headers) | .text | unwind info | + /tmp/jitted-PID-2.so | (headers) | .text | unwind info | + ... + ------------------------------------------------------------------------------------------------------- + +As the trampolines are constant, we add a constant padding but in general the padding needs to have the +size of the unwind info rounded to 16 bytes. In general, for our trampolines this is 0x50 + */ + +#define PERF_JIT_CODE_PADDING 0x100 +#define trampoline_api _PyRuntime.ceval.perf.trampoline_api + +typedef uint64_t uword; +typedef const char* CodeComments; + +#define Pd "d" +#define MB (1024 * 1024) + +#define EM_386 3 +#define EM_X86_64 62 +#define EM_ARM 40 +#define EM_AARCH64 183 +#define EM_RISCV 243 + +#define TARGET_ARCH_IA32 0 +#define TARGET_ARCH_X64 0 +#define TARGET_ARCH_ARM 0 +#define TARGET_ARCH_ARM64 0 +#define TARGET_ARCH_RISCV32 0 +#define TARGET_ARCH_RISCV64 0 + +#define FLAG_generate_perf_jitdump 0 +#define FLAG_write_protect_code 0 +#define FLAG_write_protect_vm_isolate 0 +#define FLAG_code_comments 0 + +#define UNREACHABLE() + +static uword GetElfMachineArchitecture(void) { +#if TARGET_ARCH_IA32 + return EM_386; +#elif TARGET_ARCH_X64 + return EM_X86_64; +#elif TARGET_ARCH_ARM + return EM_ARM; +#elif TARGET_ARCH_ARM64 + return EM_AARCH64; +#elif TARGET_ARCH_RISCV32 || TARGET_ARCH_RISCV64 + return EM_RISCV; +#else + UNREACHABLE(); + return 0; +#endif +} + +typedef struct { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t elf_mach_target; + uint32_t reserved; + uint32_t process_id; + uint64_t time_stamp; + uint64_t flags; +} Header; + + enum PerfEvent { + PerfLoad = 0, + PerfMove = 1, + PerfDebugInfo = 2, + PerfClose = 3, + PerfUnwindingInfo = 4 +}; + +struct BaseEvent { + uint32_t event; + uint32_t size; + uint64_t time_stamp; + }; + +typedef struct { + struct BaseEvent base; + uint32_t process_id; + uint32_t thread_id; + uint64_t vma; + uint64_t code_address; + uint64_t code_size; + uint64_t code_id; +} CodeLoadEvent; + +typedef struct { + struct BaseEvent base; + uint64_t unwind_data_size; + uint64_t eh_frame_hdr_size; + uint64_t mapped_size; +} CodeUnwindingInfoEvent; + +static const intptr_t nanoseconds_per_second = 1000000000; + +// Dwarf encoding constants + +static const uint8_t DwarfUData4 = 0x03; +static const uint8_t DwarfSData4 = 0x0b; +static const uint8_t DwarfPcRel = 0x10; +static const uint8_t DwarfDataRel = 0x30; +// static uint8_t DwarfOmit = 0xff; +typedef struct { + unsigned char version; + unsigned char eh_frame_ptr_enc; + unsigned char fde_count_enc; + unsigned char table_enc; + int32_t eh_frame_ptr; + int32_t eh_fde_count; + int32_t from; + int32_t to; +} EhFrameHeader; + +static int64_t get_current_monotonic_ticks(void) { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + UNREACHABLE(); + return 0; + } + // Convert to nanoseconds. + int64_t result = ts.tv_sec; + result *= nanoseconds_per_second; + result += ts.tv_nsec; + return result; +} + +static int64_t get_current_time_microseconds(void) { + // gettimeofday has microsecond resolution. + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) { + UNREACHABLE(); + return 0; + } + return ((int64_t)(tv.tv_sec) * 1000000) + tv.tv_usec; +} + + +static size_t round_up(int64_t value, int64_t multiple) { + if (multiple == 0) { + // Avoid division by zero + return value; + } + + int64_t remainder = value % multiple; + if (remainder == 0) { + // Value is already a multiple of 'multiple' + return value; + } + + // Calculate the difference to the next multiple + int64_t difference = multiple - remainder; + + // Add the difference to the value + int64_t rounded_up_value = value + difference; + + return rounded_up_value; +} + + +static void perf_map_jit_write_fully(const void* buffer, size_t size) { + FILE* out_file = perf_jit_map_state.perf_map; + const char* ptr = (const char*)(buffer); + while (size > 0) { + const size_t written = fwrite(ptr, 1, size, out_file); + if (written == 0) { + UNREACHABLE(); + break; + } + size -= written; + ptr += written; + } +} + +static void perf_map_jit_write_header(int pid, FILE* out_file) { + Header header; + header.magic = 0x4A695444; + header.version = 1; + header.size = sizeof(Header); + header.elf_mach_target = GetElfMachineArchitecture(); + header.process_id = pid; + header.time_stamp = get_current_time_microseconds(); + header.flags = 0; + perf_map_jit_write_fully(&header, sizeof(header)); +} + +static void* perf_map_jit_init(void) { + char filename[100]; + int pid = getpid(); + snprintf(filename, sizeof(filename) - 1, "/tmp/jit-%d.dump", pid); + const int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666); + if (fd == -1) { + return NULL; + } + + const long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int) + if (page_size == -1) { + close(fd); + return NULL; + } + + // The perf jit interface forces us to map the first page of the file + // to signal that we are using the interface. + perf_jit_map_state.mapped_buffer = mmap(NULL, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); + if (perf_jit_map_state.mapped_buffer == NULL) { + close(fd); + return NULL; + } + perf_jit_map_state.mapped_size = page_size; + perf_jit_map_state.perf_map = fdopen(fd, "w+"); + if (perf_jit_map_state.perf_map == NULL) { + close(fd); + return NULL; + } + setvbuf(perf_jit_map_state.perf_map, NULL, _IOFBF, 2 * MB); + perf_map_jit_write_header(pid, perf_jit_map_state.perf_map); + + perf_jit_map_state.map_lock = PyThread_allocate_lock(); + if (perf_jit_map_state.map_lock == NULL) { + fclose(perf_jit_map_state.perf_map); + return NULL; + } + perf_jit_map_state.code_id = 0; + + // trampoline_api.code_padding = PERF_JIT_CODE_PADDING; + return &perf_jit_map_state; +} + +/* DWARF definitions. */ + +#define DWRF_CIE_VERSION 1 + +enum { + DWRF_CFA_nop = 0x0, + DWRF_CFA_offset_extended = 0x5, + DWRF_CFA_def_cfa = 0xc, + DWRF_CFA_def_cfa_offset = 0xe, + DWRF_CFA_offset_extended_sf = 0x11, + DWRF_CFA_advance_loc = 0x40, + DWRF_CFA_offset = 0x80 +}; + +enum + { + DWRF_EH_PE_absptr = 0x00, + DWRF_EH_PE_omit = 0xff, + + /* FDE data encoding. */ + DWRF_EH_PE_uleb128 = 0x01, + DWRF_EH_PE_udata2 = 0x02, + DWRF_EH_PE_udata4 = 0x03, + DWRF_EH_PE_udata8 = 0x04, + DWRF_EH_PE_sleb128 = 0x09, + DWRF_EH_PE_sdata2 = 0x0a, + DWRF_EH_PE_sdata4 = 0x0b, + DWRF_EH_PE_sdata8 = 0x0c, + DWRF_EH_PE_signed = 0x08, + + /* FDE flags. */ + DWRF_EH_PE_pcrel = 0x10, + DWRF_EH_PE_textrel = 0x20, + DWRF_EH_PE_datarel = 0x30, + DWRF_EH_PE_funcrel = 0x40, + DWRF_EH_PE_aligned = 0x50, + + DWRF_EH_PE_indirect = 0x80 + }; + +enum { DWRF_TAG_compile_unit = 0x11 }; + +enum { DWRF_children_no = 0, DWRF_children_yes = 1 }; + +enum { DWRF_AT_name = 0x03, DWRF_AT_stmt_list = 0x10, DWRF_AT_low_pc = 0x11, DWRF_AT_high_pc = 0x12 }; + +enum { DWRF_FORM_addr = 0x01, DWRF_FORM_data4 = 0x06, DWRF_FORM_string = 0x08 }; + +enum { DWRF_LNS_extended_op = 0, DWRF_LNS_copy = 1, DWRF_LNS_advance_pc = 2, DWRF_LNS_advance_line = 3 }; + +enum { DWRF_LNE_end_sequence = 1, DWRF_LNE_set_address = 2 }; + +enum { +#ifdef __x86_64__ + /* Yes, the order is strange, but correct. */ + DWRF_REG_AX, + DWRF_REG_DX, + DWRF_REG_CX, + DWRF_REG_BX, + DWRF_REG_SI, + DWRF_REG_DI, + DWRF_REG_BP, + DWRF_REG_SP, + DWRF_REG_8, + DWRF_REG_9, + DWRF_REG_10, + DWRF_REG_11, + DWRF_REG_12, + DWRF_REG_13, + DWRF_REG_14, + DWRF_REG_15, + DWRF_REG_RA, +#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) + DWRF_REG_SP = 31, + DWRF_REG_RA = 30, +#else +# error "Unsupported target architecture" +#endif +}; + +typedef struct ELFObjectContext +{ + uint8_t* p; /* Pointer to next address in obj.space. */ + uint8_t* startp; /* Pointer to start address in obj.space. */ + uint8_t* eh_frame_p; /* Pointer to start address in obj.space. */ + uint32_t code_size; /* Size of machine code. */ +} ELFObjectContext; + +/* Append a null-terminated string. */ +static uint32_t +elfctx_append_string(ELFObjectContext* ctx, const char* str) +{ + uint8_t* p = ctx->p; + uint32_t ofs = (uint32_t)(p - ctx->startp); + do { + *p++ = (uint8_t)*str; + } while (*str++); + ctx->p = p; + return ofs; +} + +/* Append a SLEB128 value. */ +static void +elfctx_append_sleb128(ELFObjectContext* ctx, int32_t v) +{ + uint8_t* p = ctx->p; + for (; (uint32_t)(v + 0x40) >= 0x80; v >>= 7) { + *p++ = (uint8_t)((v & 0x7f) | 0x80); + } + *p++ = (uint8_t)(v & 0x7f); + ctx->p = p; +} + +/* Append a ULEB128 to buffer. */ +static void +elfctx_append_uleb128(ELFObjectContext* ctx, uint32_t v) +{ + uint8_t* p = ctx->p; + for (; v >= 0x80; v >>= 7) { + *p++ = (char)((v & 0x7f) | 0x80); + } + *p++ = (char)v; + ctx->p = p; +} + +/* Shortcuts to generate DWARF structures. */ +#define DWRF_U8(x) (*p++ = (x)) +#define DWRF_I8(x) (*(int8_t*)p = (x), p++) +#define DWRF_U16(x) (*(uint16_t*)p = (x), p += 2) +#define DWRF_U32(x) (*(uint32_t*)p = (x), p += 4) +#define DWRF_ADDR(x) (*(uintptr_t*)p = (x), p += sizeof(uintptr_t)) +#define DWRF_UV(x) (ctx->p = p, elfctx_append_uleb128(ctx, (x)), p = ctx->p) +#define DWRF_SV(x) (ctx->p = p, elfctx_append_sleb128(ctx, (x)), p = ctx->p) +#define DWRF_STR(str) (ctx->p = p, elfctx_append_string(ctx, (str)), p = ctx->p) +#define DWRF_ALIGNNOP(s) \ + while ((uintptr_t)p & ((s)-1)) { \ + *p++ = DWRF_CFA_nop; \ + } +#define DWRF_SECTION(name, stmt) \ + { \ + uint32_t* szp_##name = (uint32_t*)p; \ + p += 4; \ + stmt; \ + *szp_##name = (uint32_t)((p - (uint8_t*)szp_##name) - 4); \ + } + +/* Initialize .eh_frame section. */ +static void +elf_init_ehframe(ELFObjectContext* ctx) +{ + uint8_t* p = ctx->p; + uint8_t* framep = p; + + /* Emit DWARF EH CIE. */ + DWRF_SECTION(CIE, DWRF_U32(0); /* Offset to CIE itself. */ + DWRF_U8(DWRF_CIE_VERSION); + DWRF_STR("zR"); /* Augmentation. */ + DWRF_UV(1); /* Code alignment factor. */ + DWRF_SV(-(int64_t)sizeof(uintptr_t)); /* Data alignment factor. */ + DWRF_U8(DWRF_REG_RA); /* Return address register. */ + DWRF_UV(1); + DWRF_U8(DWRF_EH_PE_pcrel | DWRF_EH_PE_sdata4); /* Augmentation data. */ + DWRF_U8(DWRF_CFA_def_cfa); DWRF_UV(DWRF_REG_SP); DWRF_UV(sizeof(uintptr_t)); + DWRF_U8(DWRF_CFA_offset|DWRF_REG_RA); DWRF_UV(1); + DWRF_ALIGNNOP(sizeof(uintptr_t)); + ) + + ctx->eh_frame_p = p; + + /* Emit DWARF EH FDE. */ + DWRF_SECTION(FDE, DWRF_U32((uint32_t)(p - framep)); /* Offset to CIE. */ + DWRF_U32(-0x30); /* Machine code offset relative to .text. */ + DWRF_U32(ctx->code_size); /* Machine code length. */ + DWRF_U8(0); /* Augmentation data. */ + /* Registers saved in CFRAME. */ +#ifdef __x86_64__ + DWRF_U8(DWRF_CFA_advance_loc | 4); + DWRF_U8(DWRF_CFA_def_cfa_offset); DWRF_UV(16); + DWRF_U8(DWRF_CFA_advance_loc | 6); + DWRF_U8(DWRF_CFA_def_cfa_offset); DWRF_UV(8); + /* Extra registers saved for JIT-compiled code. */ +#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) + DWRF_U8(DWRF_CFA_advance_loc | 1); + DWRF_U8(DWRF_CFA_def_cfa_offset); DWRF_UV(16); + DWRF_U8(DWRF_CFA_offset | 29); DWRF_UV(2); + DWRF_U8(DWRF_CFA_offset | 30); DWRF_UV(1); + DWRF_U8(DWRF_CFA_advance_loc | 3); + DWRF_U8(DWRF_CFA_offset | -(64 - 29)); + DWRF_U8(DWRF_CFA_offset | -(64 - 30)); + DWRF_U8(DWRF_CFA_def_cfa_offset); + DWRF_UV(0); +#else +# error "Unsupported target architecture" +#endif + DWRF_ALIGNNOP(sizeof(uintptr_t));) + + ctx->p = p; +} + +static void perf_map_jit_write_entry(void *state, const void *code_addr, + unsigned int code_size, PyCodeObject *co) +{ + + if (perf_jit_map_state.perf_map == NULL) { + void* ret = perf_map_jit_init(); + if(ret == NULL){ + return; + } + } + + const char *entry = ""; + if (co->co_qualname != NULL) { + entry = PyUnicode_AsUTF8(co->co_qualname); + } + const char *filename = ""; + if (co->co_filename != NULL) { + filename = PyUnicode_AsUTF8(co->co_filename); + } + + + size_t perf_map_entry_size = snprintf(NULL, 0, "py::%s:%s", entry, filename) + 1; + char* perf_map_entry = (char*) PyMem_RawMalloc(perf_map_entry_size); + if (perf_map_entry == NULL) { + return; + } + snprintf(perf_map_entry, perf_map_entry_size, "py::%s:%s", entry, filename); + + const size_t name_length = strlen(perf_map_entry); + uword base = (uword)code_addr; + uword size = code_size; + + // Write the code unwinding info event. + + // Create unwinding information (eh frame) + ELFObjectContext ctx; + char buffer[1024]; + ctx.code_size = code_size; + ctx.startp = ctx.p = (uint8_t*)buffer; + elf_init_ehframe(&ctx); + int eh_frame_size = ctx.p - ctx.startp; + + // Populate the unwind info event for perf + CodeUnwindingInfoEvent ev2; + ev2.base.event = PerfUnwindingInfo; + ev2.base.time_stamp = get_current_monotonic_ticks(); + ev2.unwind_data_size = sizeof(EhFrameHeader) + eh_frame_size; + // Ensure we have enough space between DSOs when perf maps them + assert(ev2.unwind_data_size <= PERF_JIT_CODE_PADDING); + ev2.eh_frame_hdr_size = sizeof(EhFrameHeader); + ev2.mapped_size = round_up(ev2.unwind_data_size, 16); + int content_size = sizeof(ev2) + sizeof(EhFrameHeader) + eh_frame_size; + int padding_size = round_up(content_size, 8) - content_size; + ev2.base.size = content_size + padding_size; + perf_map_jit_write_fully(&ev2, sizeof(ev2)); + + + // Populate the eh Frame header + EhFrameHeader f; + f.version = 1; + f.eh_frame_ptr_enc = DwarfSData4 | DwarfPcRel; + f.fde_count_enc = DwarfUData4; + f.table_enc = DwarfSData4 | DwarfDataRel; + f.eh_frame_ptr = -(eh_frame_size + 4 * sizeof(unsigned char)); + f.eh_fde_count = 1; + f.from = -(round_up(code_size, 8) + eh_frame_size); + int cie_size = ctx.eh_frame_p - ctx.startp; + f.to = -(eh_frame_size - cie_size); + + perf_map_jit_write_fully(ctx.startp, eh_frame_size); + perf_map_jit_write_fully(&f, sizeof(f)); + + char padding_bytes[] = "\0\0\0\0\0\0\0\0"; + perf_map_jit_write_fully(&padding_bytes, padding_size); + + // Write the code load event. + CodeLoadEvent ev; + ev.base.event = PerfLoad; + ev.base.size = sizeof(ev) + (name_length+1) + size; + ev.base.time_stamp = get_current_monotonic_ticks(); + ev.process_id = getpid(); + ev.thread_id = gettid(); + ev.vma = base; + ev.code_address = base; + ev.code_size = size; + perf_jit_map_state.code_id += 1; + ev.code_id = perf_jit_map_state.code_id; + + perf_map_jit_write_fully(&ev, sizeof(ev)); + perf_map_jit_write_fully(perf_map_entry, name_length+1); + perf_map_jit_write_fully((void*)(base), size); + return; +} + +static int perf_map_jit_fini(void* state) { + if (perf_jit_map_state.perf_map != NULL) { + // close the file + PyThread_acquire_lock(perf_jit_map_state.map_lock, 1); + fclose(perf_jit_map_state.perf_map); + PyThread_release_lock(perf_jit_map_state.map_lock); + + // clean up the lock and state + PyThread_free_lock(perf_jit_map_state.map_lock); + perf_jit_map_state.perf_map = NULL; + } + if (perf_jit_map_state.mapped_buffer != NULL) { + munmap(perf_jit_map_state.mapped_buffer, perf_jit_map_state.mapped_size); + } + trampoline_api.state = NULL; + return 0; +} + +_PyPerf_Callbacks _Py_perfmap_jit_callbacks = { + &perf_map_jit_init, + &perf_map_jit_write_entry, + &perf_map_jit_fini, +}; + +#endif diff --git a/Python/perf_trampoline.c b/Python/perf_trampoline.c index 750ba18d3510ed..f144f7d436fe68 100644 --- a/Python/perf_trampoline.c +++ b/Python/perf_trampoline.c @@ -143,6 +143,8 @@ any DWARF information available for them). #include // mmap() #include #include // sysconf() +#include // gettimeofday() + #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) #define PY_HAVE_INVALIDATE_ICACHE @@ -187,12 +189,19 @@ struct code_arena_st { typedef struct code_arena_st code_arena_t; typedef struct trampoline_api_st trampoline_api_t; +enum perf_trampoline_type { + PERF_TRAMPOLINE_UNSET = 0, + PERF_TRAMPOLINE_TYPE_MAP = 1, + PERF_TRAMPOLINE_TYPE_JITDUMP = 2, +}; + #define perf_status _PyRuntime.ceval.perf.status #define extra_code_index _PyRuntime.ceval.perf.extra_code_index #define perf_code_arena _PyRuntime.ceval.perf.code_arena #define trampoline_api _PyRuntime.ceval.perf.trampoline_api #define perf_map_file _PyRuntime.ceval.perf.map_file #define persist_after_fork _PyRuntime.ceval.perf.persist_after_fork +#define perf_trampoline_type _PyRuntime.ceval.perf.perf_trampoline_type static void perf_map_write_entry(void *state, const void *code_addr, @@ -220,6 +229,8 @@ static void* perf_map_init_state(void) { PyUnstable_PerfMapState_Init(); + trampoline_api.code_padding = 0; + perf_trampoline_type = PERF_TRAMPOLINE_TYPE_MAP; return NULL; } @@ -236,6 +247,30 @@ _PyPerf_Callbacks _Py_perfmap_callbacks = { &perf_map_free_state, }; + +static size_t round_up(int64_t value, int64_t multiple) { + if (multiple == 0) { + // Avoid division by zero + return value; + } + + int64_t remainder = value % multiple; + if (remainder == 0) { + // Value is already a multiple of 'multiple' + return value; + } + + // Calculate the difference to the next multiple + int64_t difference = multiple - remainder; + + // Add the difference to the value + int64_t rounded_up_value = value + difference; + + return rounded_up_value; +} + +// TRAMPOLINE MANAGEMENT API + static int new_code_arena(void) { @@ -256,6 +291,7 @@ new_code_arena(void) void *start = &_Py_trampoline_func_start; void *end = &_Py_trampoline_func_end; size_t code_size = end - start; + size_t chunk_size = round_up(code_size + trampoline_api.code_padding, 16); // TODO: Check the effect of alignment of the code chunks. Initial investigation // showed that this has no effect on performance in x86-64 or aarch64 and the current // version has the advantage that the unwinder in GDB can unwind across JIT-ed code. @@ -264,9 +300,9 @@ new_code_arena(void) // measurable performance improvement by rounding trampolines up to 32-bit // or 64-bit alignment. - size_t n_copies = mem_size / code_size; + size_t n_copies = mem_size / chunk_size; for (size_t i = 0; i < n_copies; i++) { - memcpy(memory + i * code_size, start, code_size * sizeof(char)); + memcpy(memory + i * chunk_size, start, code_size * sizeof(char)); } // Some systems may prevent us from creating executable code on the fly. int res = mprotect(memory, mem_size, PROT_READ | PROT_EXEC); @@ -320,16 +356,18 @@ static inline py_trampoline code_arena_new_code(code_arena_t *code_arena) { py_trampoline trampoline = (py_trampoline)code_arena->current_addr; - code_arena->size_left -= code_arena->code_size; - code_arena->current_addr += code_arena->code_size; + size_t total_code_size = round_up(code_arena->code_size + trampoline_api.code_padding, 16); + code_arena->size_left -= total_code_size; + code_arena->current_addr += total_code_size; return trampoline; } static inline py_trampoline compile_trampoline(void) { + size_t total_code_size = round_up(perf_code_arena->code_size + trampoline_api.code_padding, 16); if ((perf_code_arena == NULL) || - (perf_code_arena->size_left <= perf_code_arena->code_size)) { + (perf_code_arena->size_left <= total_code_size)) { if (new_code_arena() < 0) { return NULL; } @@ -480,6 +518,7 @@ _PyPerfTrampoline_Fini(void) } if (perf_status == PERF_STATUS_OK) { trampoline_api.free_state(trampoline_api.state); + perf_trampoline_type = PERF_TRAMPOLINE_UNSET; } extra_code_index = -1; perf_status = PERF_STATUS_NO_INIT; @@ -508,6 +547,9 @@ _PyPerfTrampoline_AfterFork_Child(void) { #ifdef PY_HAVE_PERF_TRAMPOLINE if (persist_after_fork) { + if (perf_trampoline_type != PERF_TRAMPOLINE_TYPE_MAP) { + return PyStatus_Error("Failed to copy perf map file as perf trampoline type is not type map."); + } _PyPerfTrampoline_Fini(); char filename[256]; pid_t parent_pid = getppid(); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 9dc6e3f31128c1..f24b0482c2bc38 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1210,7 +1210,14 @@ init_interp_main(PyThreadState *tstate) #ifdef PY_HAVE_PERF_TRAMPOLINE if (config->perf_profiling) { - if (_PyPerfTrampoline_SetCallbacks(&_Py_perfmap_callbacks) < 0 || + _PyPerf_Callbacks *cur_cb; + if (config->perf_profiling == 1) { + cur_cb = &_Py_perfmap_callbacks; + } + else { + cur_cb = &_Py_perfmap_jit_callbacks; + } + if (_PyPerfTrampoline_SetCallbacks(cur_cb) < 0 || _PyPerfTrampoline_Init(config->perf_profiling) < 0) { return _PyStatus_ERR("can't initialize the perf trampoline"); } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index bd7f821931da42..17c4a5f46c09df 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2282,6 +2282,16 @@ sys_activate_stack_trampoline_impl(PyObject *module, const char *backend) return NULL; } } + else if (strcmp(backend, "perfjit") == 0) { + _PyPerf_Callbacks cur_cb; + _PyPerfTrampoline_GetCallbacks(&cur_cb); + if (cur_cb.write_state != _Py_perfmap_jit_callbacks.write_state) { + if (_PyPerfTrampoline_SetCallbacks(&_Py_perfmap_jit_callbacks) < 0 ) { + PyErr_SetString(PyExc_ValueError, "can't activate perf jit trampoline"); + return NULL; + } + } + } } else { PyErr_Format(PyExc_ValueError, "invalid backend: %s", backend); diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index c4dbdcfe80bb4a..a1c1553d8ddc10 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -365,6 +365,8 @@ Python/intrinsics.c - _PyIntrinsics_BinaryFunctions - Python/lock.c - TIME_TO_BE_FAIR_NS - Python/opcode_targets.h - opcode_targets - Python/perf_trampoline.c - _Py_perfmap_callbacks - +Python/perf_jit_trampoline.c - _Py_perfmap_jit_callbacks - +Python/perf_jit_trampoline.c - perf_jit_map_state - Python/pyhash.c - PyHash_Func - Python/pylifecycle.c - _C_LOCALE_WARNING - Python/pylifecycle.c - _PyOS_mystrnicmp_hack - From 5dd36732c850084ce262b7869ed90d73a281296a Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Sat, 4 May 2024 20:06:42 -0700 Subject: [PATCH 216/217] gh-74929: Remove undesirable DECREF in PEP 667 implementation (#118583) With tests. --- Lib/test/test_frame.py | 15 +++++++++++++++ Objects/frameobject.c | 1 - 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 93d0ea839d16eb..212255374bddd1 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -364,6 +364,21 @@ def f(): c = 0 f() + def test_local_objects(self): + o = object() + k = '.'.join(['a', 'b', 'c']) + f_locals = sys._getframe().f_locals + f_locals['o'] = f_locals['k'] + self.assertEqual(o, 'a.b.c') + + def test_update_with_self(self): + def f(): + f_locals = sys._getframe().f_locals + f_locals.update(f_locals) + f_locals.update(f_locals) + f_locals.update(f_locals) + f() + def test_repr(self): x = 1 # Introduce a reference cycle diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 8030ecb6853674..1cb00e318d9163 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -171,7 +171,6 @@ framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value) } else if (value != oldvalue) { Py_XSETREF(fast[i], Py_NewRef(value)); } - Py_XDECREF(value); return 0; } } From 711c80bfca5dd17cb7c6ec26f0e44848b33aec04 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 5 May 2024 08:20:06 +0300 Subject: [PATCH 217/217] gh-118164: Break a loop between _pydecimal and _pylong and optimize int to str conversion (GH-118483) For converting large ints to strings, CPython invokes a function in _pylong.py, which uses the decimal module to implement an asymptotically waaaaay sub-quadratic algorithm. But if the C decimal module isn't available, CPython uses _pydecimal.py instead. Which in turn frequently does str(int). If the int is very large, _pylong ends up doing the work, which in turn asks decimal to do "big" arithmetic, which in turn calls str(big_int), which in turn ... it can become infinite mutual recursion. This change introduces a different int->str function that doesn't use decimal. It's asymptotically worse, "Karatsuba time" instead of quadratic time, so still a huge improvement. _pylong switches to that when the C decimal isn't available. It is also used for not too large integers (less than 450_000 bits), where it is faster (up to 2 times for 30_000 bits) than the asymptotically better implementation that uses the C decimal. Co-authored-by: Tim Peters --- Lib/_pylong.py | 46 ++++++++++++++++++- Lib/test/test_int.py | 31 +++++++++---- ...-05-02-15-57-07.gh-issue-118164.AF6kwI.rst | 4 ++ 3 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-05-02-15-57-07.gh-issue-118164.AF6kwI.rst diff --git a/Lib/_pylong.py b/Lib/_pylong.py index 936346e187ff69..30bee6fc9ef54c 100644 --- a/Lib/_pylong.py +++ b/Lib/_pylong.py @@ -14,6 +14,10 @@ import re import decimal +try: + import _decimal +except ImportError: + _decimal = None def int_to_decimal(n): @@ -82,7 +86,47 @@ def inner(n, w): def int_to_decimal_string(n): """Asymptotically fast conversion of an 'int' to a decimal string.""" - return str(int_to_decimal(n)) + w = n.bit_length() + if w > 450_000 and _decimal is not None: + # It is only usable with the C decimal implementation. + # _pydecimal.py calls str() on very large integers, which in its + # turn calls int_to_decimal_string(), causing very deep recursion. + return str(int_to_decimal(n)) + + # Fallback algorithm for the case when the C decimal module isn't + # available. This algorithm is asymptotically worse than the algorithm + # using the decimal module, but better than the quadratic time + # implementation in longobject.c. + def inner(n, w): + if w <= 1000: + return str(n) + w2 = w >> 1 + d = pow10_cache.get(w2) + if d is None: + d = pow10_cache[w2] = 5**w2 << w2 # 10**i = (5*2)**i = 5**i * 2**i + hi, lo = divmod(n, d) + return inner(hi, w - w2) + inner(lo, w2).zfill(w2) + + # The estimation of the number of decimal digits. + # There is no harm in small error. If we guess too large, there may + # be leading 0's that need to be stripped. If we guess too small, we + # may need to call str() recursively for the remaining highest digits, + # which can still potentially be a large integer. This is manifested + # only if the number has way more than 10**15 digits, that exceeds + # the 52-bit physical address limit in both Intel64 and AMD64. + w = int(w * 0.3010299956639812 + 1) # log10(2) + pow10_cache = {} + if n < 0: + n = -n + sign = '-' + else: + sign = '' + s = inner(n, w) + if s[0] == '0' and n: + # If our guess of w is too large, there may be leading 0's that + # need to be stripped. + s = s.lstrip('0') + return sign + s def _str_to_int_inner(s): diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 47fc50a0e20349..c8626398b35b89 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -829,17 +829,28 @@ def tearDown(self): sys.set_int_max_str_digits(self._previous_limit) super().tearDown() - def test_pylong_int_to_decimal(self): - n = (1 << 100_000) - 1 - suffix = '9883109375' + def _test_pylong_int_to_decimal(self, n, suffix): s = str(n) - assert s[-10:] == suffix - s = str(-n) - assert s[-10:] == suffix - s = '%d' % n - assert s[-10:] == suffix - s = b'%d' % n - assert s[-10:] == suffix.encode('ascii') + self.assertEqual(s[-10:], suffix) + s2 = str(-n) + self.assertEqual(s2, '-' + s) + s3 = '%d' % n + self.assertEqual(s3, s) + s4 = b'%d' % n + self.assertEqual(s4, s.encode('ascii')) + + def test_pylong_int_to_decimal(self): + self._test_pylong_int_to_decimal((1 << 100_000), '9883109376') + self._test_pylong_int_to_decimal((1 << 100_000) - 1, '9883109375') + self._test_pylong_int_to_decimal(10**30_000, '0000000000') + self._test_pylong_int_to_decimal(10**30_000 - 1, '9999999999') + self._test_pylong_int_to_decimal(3**60_000, '9313200001') + + @support.requires_resource('cpu') + def test_pylong_int_to_decimal_2(self): + self._test_pylong_int_to_decimal(2**1_000_000, '2747109376') + self._test_pylong_int_to_decimal(10**300_000, '0000000000') + self._test_pylong_int_to_decimal(3**600_000, '3132000001') def test_pylong_int_divmod(self): n = (1 << 100_000) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-02-15-57-07.gh-issue-118164.AF6kwI.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-15-57-07.gh-issue-118164.AF6kwI.rst new file mode 100644 index 00000000000000..5eb3b6f5009bc4 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-15-57-07.gh-issue-118164.AF6kwI.rst @@ -0,0 +1,4 @@ +Break a loop between the Python implementation of the :mod:`decimal` module +and the Python code for integer to string conversion. Also optimize integer +to string conversion for values in the range from 9_000 to 135_000 decimal +digits.