diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 9561c40776f01c..0ccc7a2b4482ec 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -304,6 +304,12 @@ clocks to track time. custom :class:`contextvars.Context` for the *callback* to run in. The current context is used when no *context* is provided. + .. note:: + + For performance, callbacks scheduled with :meth:`loop.call_later` + may run up to one clock-resolution early (see + ``time.get_clock_info('monotonic').resolution``). + .. versionchanged:: 3.7 The *context* keyword-only parameter was added. See :pep:`567` for more details. @@ -324,6 +330,12 @@ clocks to track time. An instance of :class:`asyncio.TimerHandle` is returned which can be used to cancel the callback. + .. note:: + + For performance, callbacks scheduled with :meth:`loop.call_at` + may run up to one clock-resolution early (see + ``time.get_clock_info('monotonic').resolution``). + .. versionchanged:: 3.7 The *context* keyword-only parameter was added. See :pep:`567` for more details. diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index 90c90862ca1ed3..e1568ae330b70f 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -321,6 +321,9 @@ StreamWriter stream.write(data) await stream.drain() + .. note:: + The *data* buffer should be a C contiguous one-dimensional :term:`bytes-like object `. + .. method:: writelines(data) The method writes a list (or any iterable) of bytes to the underlying socket diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 10ddfa02b43156..0b99a832405549 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -573,7 +573,7 @@ Decimal objects >>> Decimal(321).exp() Decimal('2.561702493119680037517373933E+139') - .. classmethod:: from_float(f) + .. classmethod:: from_float(f, /) Alternative constructor that only accepts instances of :class:`float` or :class:`int`. @@ -600,7 +600,7 @@ Decimal objects .. versionadded:: 3.1 - .. classmethod:: from_number(number) + .. classmethod:: from_number(number, /) Alternative constructor that only accepts instances of :class:`float`, :class:`int` or :class:`Decimal`, but not strings @@ -991,7 +991,7 @@ Each thread has its own current context which is accessed or changed using the Return the current context for the active thread. -.. function:: setcontext(c) +.. function:: setcontext(c, /) Set the current context for the active thread to *c*. @@ -1168,11 +1168,11 @@ In addition to the three supplied contexts, new contexts can be created with the Return a duplicate of the context. - .. method:: copy_decimal(num) + .. method:: copy_decimal(num, /) Return a copy of the Decimal instance num. - .. method:: create_decimal(num) + .. method:: create_decimal(num='0', /) Creates a new Decimal instance from *num* but using *self* as context. Unlike the :class:`Decimal` constructor, the context precision, @@ -1196,7 +1196,7 @@ In addition to the three supplied contexts, new contexts can be created with the If the argument is a string, no leading or trailing whitespace or underscores are permitted. - .. method:: create_decimal_from_float(f) + .. method:: create_decimal_from_float(f, /) Creates a new Decimal instance from a float *f* but rounding using *self* as the context. Unlike the :meth:`Decimal.from_float` class method, @@ -1234,222 +1234,222 @@ In addition to the three supplied contexts, new contexts can be created with the recounted here. - .. method:: abs(x) + .. method:: abs(x, /) Returns the absolute value of *x*. - .. method:: add(x, y) + .. method:: add(x, y, /) Return the sum of *x* and *y*. - .. method:: canonical(x) + .. method:: canonical(x, /) Returns the same Decimal object *x*. - .. method:: compare(x, y) + .. method:: compare(x, y, /) Compares *x* and *y* numerically. - .. method:: compare_signal(x, y) + .. method:: compare_signal(x, y, /) Compares the values of the two operands numerically. - .. method:: compare_total(x, y) + .. method:: compare_total(x, y, /) Compares two operands using their abstract representation. - .. method:: compare_total_mag(x, y) + .. method:: compare_total_mag(x, y, /) Compares two operands using their abstract representation, ignoring sign. - .. method:: copy_abs(x) + .. method:: copy_abs(x, /) Returns a copy of *x* with the sign set to 0. - .. method:: copy_negate(x) + .. method:: copy_negate(x, /) Returns a copy of *x* with the sign inverted. - .. method:: copy_sign(x, y) + .. method:: copy_sign(x, y, /) Copies the sign from *y* to *x*. - .. method:: divide(x, y) + .. method:: divide(x, y, /) Return *x* divided by *y*. - .. method:: divide_int(x, y) + .. method:: divide_int(x, y, /) Return *x* divided by *y*, truncated to an integer. - .. method:: divmod(x, y) + .. method:: divmod(x, y, /) Divides two numbers and returns the integer part of the result. - .. method:: exp(x) + .. method:: exp(x, /) Returns ``e ** x``. - .. method:: fma(x, y, z) + .. method:: fma(x, y, z, /) Returns *x* multiplied by *y*, plus *z*. - .. method:: is_canonical(x) + .. method:: is_canonical(x, /) Returns ``True`` if *x* is canonical; otherwise returns ``False``. - .. method:: is_finite(x) + .. method:: is_finite(x, /) Returns ``True`` if *x* is finite; otherwise returns ``False``. - .. method:: is_infinite(x) + .. method:: is_infinite(x, /) Returns ``True`` if *x* is infinite; otherwise returns ``False``. - .. method:: is_nan(x) + .. method:: is_nan(x, /) Returns ``True`` if *x* is a qNaN or sNaN; otherwise returns ``False``. - .. method:: is_normal(x) + .. method:: is_normal(x, /) Returns ``True`` if *x* is a normal number; otherwise returns ``False``. - .. method:: is_qnan(x) + .. method:: is_qnan(x, /) Returns ``True`` if *x* is a quiet NaN; otherwise returns ``False``. - .. method:: is_signed(x) + .. method:: is_signed(x, /) Returns ``True`` if *x* is negative; otherwise returns ``False``. - .. method:: is_snan(x) + .. method:: is_snan(x, /) Returns ``True`` if *x* is a signaling NaN; otherwise returns ``False``. - .. method:: is_subnormal(x) + .. method:: is_subnormal(x, /) Returns ``True`` if *x* is subnormal; otherwise returns ``False``. - .. method:: is_zero(x) + .. method:: is_zero(x, /) Returns ``True`` if *x* is a zero; otherwise returns ``False``. - .. method:: ln(x) + .. method:: ln(x, /) Returns the natural (base e) logarithm of *x*. - .. method:: log10(x) + .. method:: log10(x, /) Returns the base 10 logarithm of *x*. - .. method:: logb(x) + .. method:: logb(x, /) Returns the exponent of the magnitude of the operand's MSD. - .. method:: logical_and(x, y) + .. method:: logical_and(x, y, /) Applies the logical operation *and* between each operand's digits. - .. method:: logical_invert(x) + .. method:: logical_invert(x, /) Invert all the digits in *x*. - .. method:: logical_or(x, y) + .. method:: logical_or(x, y, /) Applies the logical operation *or* between each operand's digits. - .. method:: logical_xor(x, y) + .. method:: logical_xor(x, y, /) Applies the logical operation *xor* between each operand's digits. - .. method:: max(x, y) + .. method:: max(x, y, /) Compares two values numerically and returns the maximum. - .. method:: max_mag(x, y) + .. method:: max_mag(x, y, /) Compares the values numerically with their sign ignored. - .. method:: min(x, y) + .. method:: min(x, y, /) Compares two values numerically and returns the minimum. - .. method:: min_mag(x, y) + .. method:: min_mag(x, y, /) Compares the values numerically with their sign ignored. - .. method:: minus(x) + .. method:: minus(x, /) Minus corresponds to the unary prefix minus operator in Python. - .. method:: multiply(x, y) + .. method:: multiply(x, y, /) Return the product of *x* and *y*. - .. method:: next_minus(x) + .. method:: next_minus(x, /) Returns the largest representable number smaller than *x*. - .. method:: next_plus(x) + .. method:: next_plus(x, /) Returns the smallest representable number larger than *x*. - .. method:: next_toward(x, y) + .. method:: next_toward(x, y, /) Returns the number closest to *x*, in direction towards *y*. - .. method:: normalize(x) + .. method:: normalize(x, /) Reduces *x* to its simplest form. - .. method:: number_class(x) + .. method:: number_class(x, /) Returns an indication of the class of *x*. - .. method:: plus(x) + .. method:: plus(x, /) Plus corresponds to the unary prefix plus operator in Python. This operation applies the context precision and rounding, so it is *not* an @@ -1490,7 +1490,7 @@ In addition to the three supplied contexts, new contexts can be created with the always exact. - .. method:: quantize(x, y) + .. method:: quantize(x, y, /) Returns a value equal to *x* (rounded), having the exponent of *y*. @@ -1500,7 +1500,7 @@ In addition to the three supplied contexts, new contexts can be created with the Just returns 10, as this is Decimal, :) - .. method:: remainder(x, y) + .. method:: remainder(x, y, /) Returns the remainder from integer division. @@ -1508,43 +1508,43 @@ In addition to the three supplied contexts, new contexts can be created with the dividend. - .. method:: remainder_near(x, y) + .. method:: remainder_near(x, y, /) Returns ``x - y * n``, where *n* is the integer nearest the exact value of ``x / y`` (if the result is 0 then its sign will be the sign of *x*). - .. method:: rotate(x, y) + .. method:: rotate(x, y, /) Returns a rotated copy of *x*, *y* times. - .. method:: same_quantum(x, y) + .. method:: same_quantum(x, y, /) Returns ``True`` if the two operands have the same exponent. - .. method:: scaleb (x, y) + .. method:: scaleb (x, y, /) Returns the first operand after adding the second value its exp. - .. method:: shift(x, y) + .. method:: shift(x, y, /) Returns a shifted copy of *x*, *y* times. - .. method:: sqrt(x) + .. method:: sqrt(x, /) Square root of a non-negative number to context precision. - .. method:: subtract(x, y) + .. method:: subtract(x, y, /) Return the difference between *x* and *y*. - .. method:: to_eng_string(x) + .. method:: to_eng_string(x, /) Convert to a string, using engineering notation if an exponent is needed. @@ -1553,12 +1553,12 @@ In addition to the three supplied contexts, new contexts can be created with the require the addition of either one or two trailing zeros. - .. method:: to_integral_exact(x) + .. method:: to_integral_exact(x, /) Rounds to an integer. - .. method:: to_sci_string(x) + .. method:: to_sci_string(x, /) Converts a number to a string using scientific notation. diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index 5a2c6bdd27c386..02b73ccd3f3d19 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -350,6 +350,13 @@ searches them recursively for docstrings, which are then scanned for tests. Any classes found are recursively searched similarly, to test docstrings in their contained methods and nested classes. +.. note:: + + ``doctest`` can only automatically discover classes and functions that are + defined at the module level or inside other classes. + + Since nested classes and functions only exist when an outer function + is called, they cannot be discovered. Define them outside to make them visible. .. _doctest-finding-examples: diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst index 6698e6d3f43c43..aa4f1bf940bc5c 100644 --- a/Doc/library/uuid.rst +++ b/Doc/library/uuid.rst @@ -411,7 +411,7 @@ Here are some examples of typical usage of the :mod:`uuid` module:: >>> import uuid >>> # make a UUID based on the host ID and current time - >>> uuid.uuid1() + >>> uuid.uuid1() # doctest: +SKIP UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') >>> # make a UUID using an MD5 hash of a namespace UUID and a name @@ -449,15 +449,24 @@ Here are some examples of typical usage of the :mod:`uuid` module:: >>> uuid.MAX UUID('ffffffff-ffff-ffff-ffff-ffffffffffff') + >>> # same as UUIDv1 but with fields reordered to improve DB locality + >>> uuid.uuid6() # doctest: +SKIP + UUID('1f0799c0-98b9-62db-92c6-a0d365b91053') + >>> # get UUIDv7 creation (local) time as a timestamp in milliseconds >>> u = uuid.uuid7() >>> u.time # doctest: +SKIP 1743936859822 + >>> # get UUIDv7 creation (local) time as a datetime object >>> import datetime as dt >>> dt.datetime.fromtimestamp(u.time / 1000) # doctest: +SKIP datetime.datetime(...) + >>> # make a UUID with custom blocks + >>> uuid.uuid8(0x12345678, 0x9abcdef0, 0x11223344) + UUID('00001234-5678-8ef0-8000-000011223344') + .. _uuid-cli-example: diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 407606da961c16..81aa12184ed35c 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -209,6 +209,13 @@ Other language changes as keyword arguments at construction time. (Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir in :gh:`74185`.) +* The :attr:`~object.__dict__` and :attr:`!__weakref__` descriptors now use a + single descriptor instance per interpreter, shared across all types that + need them. + This speeds up class creation, and helps avoid reference cycles. + (Contributed by Petr Viktorin in :gh:`135228`.) + + New modules =========== diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 27102588076cc2..c5a08e05e70287 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -566,6 +566,8 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(type_params)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(utf_8)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(CANCELLED)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(Emax)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(Emin)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(FINISHED)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(False)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(JSONDecodeError)); @@ -848,11 +850,13 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cancel)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capath)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capitals)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(category)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cb_type)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(certfile)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(chain)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(check_same_thread)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(clamp)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(clear)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(close)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(closed)); @@ -892,6 +896,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(coro)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(count)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(covariant)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ctx)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cwd)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(d_parameter_type)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(data)); @@ -1113,6 +1118,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(module)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(module_globals)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(modules)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(modulo)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(month)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mro)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(msg)); @@ -1181,6 +1187,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(pos1)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(pos2)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(posix)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(prec)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(preserve_exc)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(print_file_and_line)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(priority)); @@ -1303,6 +1310,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(traceback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(trailers)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(translate)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(traps)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(true)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(truncate)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(twice)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 2d6130c216993f..9e49e4497750a0 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -57,6 +57,8 @@ struct _Py_global_strings { struct { STRUCT_FOR_ID(CANCELLED) + STRUCT_FOR_ID(Emax) + STRUCT_FOR_ID(Emin) STRUCT_FOR_ID(FINISHED) STRUCT_FOR_ID(False) STRUCT_FOR_ID(JSONDecodeError) @@ -339,11 +341,13 @@ struct _Py_global_strings { STRUCT_FOR_ID(callback) STRUCT_FOR_ID(cancel) STRUCT_FOR_ID(capath) + STRUCT_FOR_ID(capitals) STRUCT_FOR_ID(category) STRUCT_FOR_ID(cb_type) STRUCT_FOR_ID(certfile) STRUCT_FOR_ID(chain) STRUCT_FOR_ID(check_same_thread) + STRUCT_FOR_ID(clamp) STRUCT_FOR_ID(clear) STRUCT_FOR_ID(close) STRUCT_FOR_ID(closed) @@ -383,6 +387,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(coro) STRUCT_FOR_ID(count) STRUCT_FOR_ID(covariant) + STRUCT_FOR_ID(ctx) STRUCT_FOR_ID(cwd) STRUCT_FOR_ID(d_parameter_type) STRUCT_FOR_ID(data) @@ -604,6 +609,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(module) STRUCT_FOR_ID(module_globals) STRUCT_FOR_ID(modules) + STRUCT_FOR_ID(modulo) STRUCT_FOR_ID(month) STRUCT_FOR_ID(mro) STRUCT_FOR_ID(msg) @@ -672,6 +678,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(pos1) STRUCT_FOR_ID(pos2) STRUCT_FOR_ID(posix) + STRUCT_FOR_ID(prec) STRUCT_FOR_ID(preserve_exc) STRUCT_FOR_ID(print_file_and_line) STRUCT_FOR_ID(priority) @@ -794,6 +801,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(traceback) STRUCT_FOR_ID(trailers) STRUCT_FOR_ID(translate) + STRUCT_FOR_ID(traps) STRUCT_FOR_ID(true) STRUCT_FOR_ID(truncate) STRUCT_FOR_ID(twice) diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index e300732e9e58c3..2cb1b104681300 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -691,6 +691,13 @@ struct _Py_interp_cached_objects { PyTypeObject *paramspecargs_type; PyTypeObject *paramspeckwargs_type; PyTypeObject *constevaluator_type; + + /* Descriptors for __dict__ and __weakref__ */ +#ifdef Py_GIL_DISABLED + PyMutex descriptor_mutex; +#endif + PyObject *dict_descriptor; + PyObject *weakref_descriptor; }; struct _Py_interp_static_objects { diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 9e72c7df77b945..5c65cb218dedbb 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -564,6 +564,8 @@ extern "C" { #define _Py_str_identifiers_INIT { \ INIT_ID(CANCELLED), \ + INIT_ID(Emax), \ + INIT_ID(Emin), \ INIT_ID(FINISHED), \ INIT_ID(False), \ INIT_ID(JSONDecodeError), \ @@ -846,11 +848,13 @@ extern "C" { INIT_ID(callback), \ INIT_ID(cancel), \ INIT_ID(capath), \ + INIT_ID(capitals), \ INIT_ID(category), \ INIT_ID(cb_type), \ INIT_ID(certfile), \ INIT_ID(chain), \ INIT_ID(check_same_thread), \ + INIT_ID(clamp), \ INIT_ID(clear), \ INIT_ID(close), \ INIT_ID(closed), \ @@ -890,6 +894,7 @@ extern "C" { INIT_ID(coro), \ INIT_ID(count), \ INIT_ID(covariant), \ + INIT_ID(ctx), \ INIT_ID(cwd), \ INIT_ID(d_parameter_type), \ INIT_ID(data), \ @@ -1111,6 +1116,7 @@ extern "C" { INIT_ID(module), \ INIT_ID(module_globals), \ INIT_ID(modules), \ + INIT_ID(modulo), \ INIT_ID(month), \ INIT_ID(mro), \ INIT_ID(msg), \ @@ -1179,6 +1185,7 @@ extern "C" { INIT_ID(pos1), \ INIT_ID(pos2), \ INIT_ID(posix), \ + INIT_ID(prec), \ INIT_ID(preserve_exc), \ INIT_ID(print_file_and_line), \ INIT_ID(priority), \ @@ -1301,6 +1308,7 @@ extern "C" { INIT_ID(traceback), \ INIT_ID(trailers), \ INIT_ID(translate), \ + INIT_ID(traps), \ INIT_ID(true), \ INIT_ID(truncate), \ INIT_ID(twice), \ diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 0ee7d555c56cdd..24df69aa93fda2 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -40,6 +40,7 @@ extern void _PyTypes_FiniTypes(PyInterpreterState *); extern void _PyTypes_FiniExtTypes(PyInterpreterState *interp); extern void _PyTypes_Fini(PyInterpreterState *); extern void _PyTypes_AfterFork(void); +extern void _PyTypes_FiniCachedDescriptors(PyInterpreterState *); static inline PyObject ** _PyStaticType_GET_WEAKREFS_LISTPTR(managed_static_type_state *state) diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 02c62f68ff0801..4905007b95f8fa 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -16,6 +16,14 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(Emax); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(Emin); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(FINISHED); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1144,6 +1152,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(capitals); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(category); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1164,6 +1176,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(clamp); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(clear); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1320,6 +1336,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(ctx); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(cwd); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2204,6 +2224,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(modulo); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(month); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2476,6 +2500,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(prec); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(preserve_exc); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2964,6 +2992,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(traps); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(true); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index d29f1615f276d2..b98f21dcbe9220 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1283,10 +1283,6 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields): if '__slots__' in cls.__dict__: raise TypeError(f'{cls.__name__} already specifies __slots__') - # gh-102069: Remove existing __weakref__ descriptor. - # gh-135228: Make sure the original class can be garbage collected. - sys._clear_type_descriptors(cls) - # Create a new dict for our new class. cls_dict = dict(cls.__dict__) field_names = tuple(f.name for f in fields(cls)) @@ -1304,6 +1300,11 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields): # available in _MARKER. cls_dict.pop(field_name, None) + # Remove __dict__ and `__weakref__` descriptors. + # They'll be added back if applicable. + cls_dict.pop('__dict__', None) + cls_dict.pop('__weakref__', None) # gh-102069 + # And finally create the class. qualname = getattr(cls, '__qualname__', None) newcls = type(cls)(cls.__name__, cls.__bases__, cls_dict) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 8bcd741c446bd2..8a1437a2cc5d1e 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -297,7 +297,8 @@ def cache_from_source(path, debug_override=None, *, optimization=None): # Strip initial drive from a Windows path. We know we have an absolute # path here, so the second part of the check rules out a POSIX path that # happens to contain a colon at the second character. - if head[1] == ':' and head[0] not in path_separators: + # Slicing avoids issues with an empty (or short) `head`. + if head[1:2] == ':' and head[0:1] not in path_separators: head = head[2:] # Strip initial path separator from `head` to complete the conversion diff --git a/Lib/inspect.py b/Lib/inspect.py index 183e67fabf966e..d7814bfeb2b885 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1698,7 +1698,8 @@ def _shadowed_dict_from_weakref_mro_tuple(*weakref_mro): class_dict = dunder_dict['__dict__'] if not (type(class_dict) is types.GetSetDescriptorType and class_dict.__name__ == "__dict__" and - class_dict.__objclass__ is entry): + (class_dict.__objclass__ is object or + class_dict.__objclass__ is entry)): return class_dict return _sentinel diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 8da6647c3f71fc..9dfeeccb81b34d 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -6013,5 +6013,69 @@ class A(metaclass=M): pass +class TestGenericDescriptors(unittest.TestCase): + def test___dict__(self): + class CustomClass: + pass + class SlotClass: + __slots__ = ['foo'] + class SlotSubClass(SlotClass): + pass + class IntSubclass(int): + pass + + dict_descriptor = CustomClass.__dict__['__dict__'] + self.assertEqual(dict_descriptor.__objclass__, object) + + for cls in CustomClass, SlotSubClass, IntSubclass: + with self.subTest(cls=cls): + self.assertIs(cls.__dict__['__dict__'], dict_descriptor) + instance = cls() + instance.attr = 123 + self.assertEqual( + dict_descriptor.__get__(instance, cls), + {'attr': 123}, + ) + with self.assertRaises(AttributeError): + print(dict_descriptor.__get__(True, bool)) + with self.assertRaises(AttributeError): + print(dict_descriptor.__get__(SlotClass(), SlotClass)) + + # delegation to type.__dict__ + self.assertIsInstance( + dict_descriptor.__get__(type, type), + types.MappingProxyType, + ) + + def test___weakref__(self): + class CustomClass: + pass + class SlotClass: + __slots__ = ['foo'] + class SlotSubClass(SlotClass): + pass + class IntSubclass(int): + pass + + weakref_descriptor = CustomClass.__dict__['__weakref__'] + self.assertEqual(weakref_descriptor.__objclass__, object) + + for cls in CustomClass, SlotSubClass: + with self.subTest(cls=cls): + self.assertIs(cls.__dict__['__weakref__'], weakref_descriptor) + instance = cls() + instance.attr = 123 + self.assertEqual( + weakref_descriptor.__get__(instance, cls), + None, + ) + with self.assertRaises(AttributeError): + weakref_descriptor.__get__(True, bool) + with self.assertRaises(AttributeError): + weakref_descriptor.__get__(SlotClass(), SlotClass) + with self.assertRaises(AttributeError): + weakref_descriptor.__get__(IntSubclass(), IntSubclass) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 6d6d5f96aab4a8..a77ce234deec58 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -580,6 +580,18 @@ def test_cache_from_source_respects_pycache_prefix_relative(self): self.util.cache_from_source(path, optimization=''), os.path.normpath(expect)) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') + def test_cache_from_source_in_root_with_pycache_prefix(self): + # Regression test for gh-82916 + pycache_prefix = os.path.join(os.path.sep, 'tmp', 'bytecode') + path = 'qux.py' + expect = os.path.join(os.path.sep, 'tmp', 'bytecode', + f'qux.{self.tag}.pyc') + with util.temporary_pycache_prefix(pycache_prefix): + with os_helper.change_cwd('/'): + self.assertEqual(self.util.cache_from_source(path), expect) + @unittest.skipIf(sys.implementation.cache_tag is None, 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_inside_pycache_prefix(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-05-10-22-15.gh-issue-136966.J5lrE0.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-05-10-22-15.gh-issue-136966.J5lrE0.rst new file mode 100644 index 00000000000000..aafd9ca4db4cd3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-05-10-22-15.gh-issue-136966.J5lrE0.rst @@ -0,0 +1,4 @@ +The :attr:`object.__dict__` and :attr:`!__weakref__` descriptors now use a +single descriptor instance per interpreter, shared across all types that +need them. +This speeds up class creation, and helps avoid reference cycles. diff --git a/Misc/NEWS.d/next/Library/2022-01-07-16-56-57.bpo-38735.NFfJX6.rst b/Misc/NEWS.d/next/Library/2022-01-07-16-56-57.bpo-38735.NFfJX6.rst new file mode 100644 index 00000000000000..7f4ea04284e10b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-01-07-16-56-57.bpo-38735.NFfJX6.rst @@ -0,0 +1,2 @@ +Fix failure when importing a module from the root directory on unix-like +platforms with sys.pycache_prefix set. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 46cedf83df1f00..52ed5a532efd27 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -63,8 +63,9 @@ /*[clinic input] module _decimal class _decimal.Decimal "PyObject *" "&dec_spec" +class _decimal.Context "PyObject *" "&ctx_spec" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e0e1f68f1f413f5f]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=8c3aa7cfde934d7b]*/ struct PyDecContextObject; struct DecCondMap; @@ -1566,17 +1567,26 @@ init_extended_context(PyObject *v) } /* Factory function for creating IEEE interchange format contexts */ + +/*[clinic input] +_decimal.IEEEContext + + bits: Py_ssize_t + / + +Return a context, initialized as one of the IEEE interchange formats. + +The argument must be a multiple of 32 and less than +IEEE_CONTEXT_MAX_BITS. +[clinic start generated code]*/ + static PyObject * -ieee_context(PyObject *module, PyObject *v) +_decimal_IEEEContext_impl(PyObject *module, Py_ssize_t bits) +/*[clinic end generated code: output=19a35f320fe19789 input=5cff864d899eb2d7]*/ { PyObject *context; - mpd_ssize_t bits; mpd_context_t ctx; - bits = PyLong_AsSsize_t(v); - if (bits == -1 && PyErr_Occurred()) { - return NULL; - } if (bits <= 0 || bits > INT_MAX) { goto error; } @@ -1775,7 +1785,7 @@ current_context(decimal_state *modstate) /* Return a new reference to the current context */ static PyObject * -PyDec_GetCurrentContext(PyObject *self, PyObject *Py_UNUSED(dummy)) +PyDec_GetCurrentContext(PyObject *self) { PyObject *context; decimal_state *state = get_module_state(self); @@ -1871,7 +1881,7 @@ current_context(decimal_state *state) /* Return a new reference to the current context */ static PyObject * -PyDec_GetCurrentContext(PyObject *self, PyObject *Py_UNUSED(dummy)) +PyDec_GetCurrentContext(PyObject *self) { decimal_state *state = get_module_state(self); return current_context(state); @@ -1910,36 +1920,73 @@ PyDec_SetCurrentContext(PyObject *self, PyObject *v) } #endif +/*[clinic input] +_decimal.getcontext + +Get the current default context. +[clinic start generated code]*/ + +static PyObject * +_decimal_getcontext_impl(PyObject *module) +/*[clinic end generated code: output=5982062c4d39e3dd input=7ac316fe42a1b6f5]*/ +{ + return PyDec_GetCurrentContext(module); +} + +/*[clinic input] +_decimal.setcontext + + context: object + / + +Set a new default context. +[clinic start generated code]*/ + +static PyObject * +_decimal_setcontext(PyObject *module, PyObject *context) +/*[clinic end generated code: output=8065f870be2852ce input=b57d7ee786b022a6]*/ +{ + return PyDec_SetCurrentContext(module, context); +} + /* Context manager object for the 'with' statement. The manager * owns one reference to the global (outer) context and one * to the local (inner) context. */ + +/*[clinic input] +@text_signature "($module, /, ctx=None, **kwargs)" +_decimal.localcontext + + ctx as local: object = None + * + prec: object = None + rounding: object = None + Emin: object = None + Emax: object = None + capitals: object = None + clamp: object = None + flags: object = None + traps: object = None + +Return a context manager for a copy of the supplied context. + +That will set the default context to a copy of ctx on entry to the +with-statement and restore the previous default context when exiting +the with-statement. If no context is specified, a copy of the current +default context is used. +[clinic start generated code]*/ + static PyObject * -ctxmanager_new(PyObject *m, PyObject *args, PyObject *kwds) +_decimal_localcontext_impl(PyObject *module, PyObject *local, PyObject *prec, + PyObject *rounding, PyObject *Emin, + PyObject *Emax, PyObject *capitals, + PyObject *clamp, PyObject *flags, PyObject *traps) +/*[clinic end generated code: output=9bf4e47742a809b0 input=490307b9689c3856]*/ { - static char *kwlist[] = { - "ctx", "prec", "rounding", - "Emin", "Emax", "capitals", - "clamp", "flags", "traps", - NULL - }; - PyObject *local = Py_None; PyObject *global; - PyObject *prec = Py_None; - PyObject *rounding = Py_None; - PyObject *Emin = Py_None; - PyObject *Emax = Py_None; - PyObject *capitals = Py_None; - PyObject *clamp = Py_None; - PyObject *flags = Py_None; - PyObject *traps = Py_None; - - decimal_state *state = get_module_state(m); + decimal_state *state = get_module_state(module); CURRENT_CONTEXT(state, global); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOOOOO", kwlist, &local, - &prec, &rounding, &Emin, &Emax, &capitals, &clamp, &flags, &traps)) { - return NULL; - } if (local == Py_None) { local = global; } @@ -3569,15 +3616,27 @@ pydec_format(PyObject *dec, PyObject *context, PyObject *fmt, decimal_state *sta } /* Formatted representation of a PyDecObject. */ + +/*[clinic input] +_decimal.Decimal.__format__ + + self as dec: self + format_spec as fmtarg: unicode + override: object = NULL + / + +Formats the Decimal according to format_spec. +[clinic start generated code]*/ + static PyObject * -dec_format(PyObject *dec, PyObject *args) +_decimal_Decimal___format___impl(PyObject *dec, PyObject *fmtarg, + PyObject *override) +/*[clinic end generated code: output=4b3640b7f0c8b6a5 input=e53488e49a0fff00]*/ { PyObject *result = NULL; - PyObject *override = NULL; PyObject *dot = NULL; PyObject *sep = NULL; PyObject *grouping = NULL; - PyObject *fmtarg; PyObject *context; mpd_spec_t spec; char *fmt; @@ -3585,42 +3644,29 @@ dec_format(PyObject *dec, PyObject *args) uint32_t status = 0; int replace_fillchar = 0; Py_ssize_t size; - - decimal_state *state = get_module_state_by_def(Py_TYPE(dec)); CURRENT_CONTEXT(state, context); - if (!PyArg_ParseTuple(args, "O|O", &fmtarg, &override)) { + fmt = (char *)PyUnicode_AsUTF8AndSize(fmtarg, &size); + if (fmt == NULL) { return NULL; } - if (PyUnicode_Check(fmtarg)) { - fmt = (char *)PyUnicode_AsUTF8AndSize(fmtarg, &size); - if (fmt == NULL) { + if (size > 0 && fmt[size-1] == 'N') { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Format specifier 'N' is deprecated", 1) < 0) { return NULL; } + } - if (size > 0 && fmt[size-1] == 'N') { - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "Format specifier 'N' is deprecated", 1) < 0) { - return NULL; - } - } - - if (size > 0 && fmt[0] == '\0') { - /* NUL fill character: must be replaced with a valid UTF-8 char - before calling mpd_parse_fmt_str(). */ - replace_fillchar = 1; - fmt = dec_strdup(fmt, size); - if (fmt == NULL) { - return NULL; - } - fmt[0] = '_'; + if (size > 0 && fmt[0] == '\0') { + /* NUL fill character: must be replaced with a valid UTF-8 char + before calling mpd_parse_fmt_str(). */ + replace_fillchar = 1; + fmt = dec_strdup(fmt, size); + if (fmt == NULL) { + return NULL; } - } - else { - PyErr_SetString(PyExc_TypeError, - "format arg must be str"); - return NULL; + fmt[0] = '_'; } if (!mpd_parse_fmt_str(&spec, fmt, CtxCaps(context))) { @@ -5389,7 +5435,7 @@ static PyMethodDef dec_methods [] = /* Special methods */ { "__copy__", dec_copy, METH_NOARGS, NULL }, { "__deepcopy__", dec_copy, METH_O, NULL }, - { "__format__", dec_format, METH_VARARGS, NULL }, + _DECIMAL_DECIMAL___FORMAT___METHODDEF { "__reduce__", dec_reduce, METH_NOARGS, NULL }, { "__round__", PyDec_Round, METH_VARARGS, NULL }, { "__ceil__", dec_ceil, METH_NOARGS, NULL }, @@ -5686,20 +5732,41 @@ ctx_mpd_qdivmod(PyObject *context, PyObject *args) } /* Binary or ternary arithmetic functions */ + +/*[clinic input] +_decimal.Context.power + + self as context: self + a as base: object + b as exp: object + modulo as mod: object = None + +Compute a**b. + +If 'a' is negative, then 'b' must be integral. The result will be +inexact unless 'a' is integral and the result is finite and can be +expressed exactly in 'precision' digits. In the Python version the +result is always correctly rounded, in the C version the result is +almost always correctly rounded. + +If modulo is given, compute (a**b) % modulo. The following +restrictions hold: + + * all three arguments must be integral + * 'b' must be nonnegative + * at least one of 'a' or 'b' must be nonzero + * modulo must be nonzero and less than 10**prec in absolute value +[clinic start generated code]*/ + static PyObject * -ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds) +_decimal_Context_power_impl(PyObject *context, PyObject *base, PyObject *exp, + PyObject *mod) +/*[clinic end generated code: output=d2e68694ec545245 input=e9aef844813de243]*/ { - static char *kwlist[] = {"a", "b", "modulo", NULL}; - PyObject *base, *exp, *mod = Py_None; PyObject *a, *b, *c = NULL; PyObject *result; uint32_t status = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, - &base, &exp, &mod)) { - return NULL; - } - CONVERT_BINOP_RAISE(&a, &b, base, exp, context); if (mod != Py_None) { @@ -6020,7 +6087,7 @@ static PyMethodDef context_methods [] = { "subtract", ctx_mpd_qsub, METH_VARARGS, doc_ctx_subtract }, /* Binary or ternary arithmetic functions */ - { "power", _PyCFunction_CAST(ctx_mpd_qpow), METH_VARARGS|METH_KEYWORDS, doc_ctx_power }, + _DECIMAL_CONTEXT_POWER_METHODDEF /* Ternary arithmetic functions */ { "fma", ctx_mpd_qfma, METH_VARARGS, doc_ctx_fma }, @@ -6117,10 +6184,10 @@ static PyType_Spec context_spec = { static PyMethodDef _decimal_methods [] = { - { "getcontext", PyDec_GetCurrentContext, METH_NOARGS, doc_getcontext}, - { "setcontext", PyDec_SetCurrentContext, METH_O, doc_setcontext}, - { "localcontext", _PyCFunction_CAST(ctxmanager_new), METH_VARARGS|METH_KEYWORDS, doc_localcontext}, - { "IEEEContext", ieee_context, METH_O, doc_ieee_context}, + _DECIMAL_GETCONTEXT_METHODDEF + _DECIMAL_SETCONTEXT_METHODDEF + _DECIMAL_LOCALCONTEXT_METHODDEF + _DECIMAL_IEEECONTEXT_METHODDEF { NULL, NULL, 1, NULL } }; diff --git a/Modules/_decimal/clinic/_decimal.c.h b/Modules/_decimal/clinic/_decimal.c.h index 441515edbf60f6..7a2bcce1e9316b 100644 --- a/Modules/_decimal/clinic/_decimal.c.h +++ b/Modules/_decimal/clinic/_decimal.c.h @@ -6,8 +6,206 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +PyDoc_STRVAR(_decimal_IEEEContext__doc__, +"IEEEContext($module, bits, /)\n" +"--\n" +"\n" +"Return a context, initialized as one of the IEEE interchange formats.\n" +"\n" +"The argument must be a multiple of 32 and less than\n" +"IEEE_CONTEXT_MAX_BITS."); + +#define _DECIMAL_IEEECONTEXT_METHODDEF \ + {"IEEEContext", (PyCFunction)_decimal_IEEEContext, METH_O, _decimal_IEEEContext__doc__}, + +static PyObject * +_decimal_IEEEContext_impl(PyObject *module, Py_ssize_t bits); + +static PyObject * +_decimal_IEEEContext(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_ssize_t bits; + + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + bits = ival; + } + return_value = _decimal_IEEEContext_impl(module, bits); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_getcontext__doc__, +"getcontext($module, /)\n" +"--\n" +"\n" +"Get the current default context."); + +#define _DECIMAL_GETCONTEXT_METHODDEF \ + {"getcontext", (PyCFunction)_decimal_getcontext, METH_NOARGS, _decimal_getcontext__doc__}, + +static PyObject * +_decimal_getcontext_impl(PyObject *module); + +static PyObject * +_decimal_getcontext(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_getcontext_impl(module); +} + +PyDoc_STRVAR(_decimal_setcontext__doc__, +"setcontext($module, context, /)\n" +"--\n" +"\n" +"Set a new default context."); + +#define _DECIMAL_SETCONTEXT_METHODDEF \ + {"setcontext", (PyCFunction)_decimal_setcontext, METH_O, _decimal_setcontext__doc__}, + +PyDoc_STRVAR(_decimal_localcontext__doc__, +"localcontext($module, /, ctx=None, **kwargs)\n" +"--\n" +"\n" +"Return a context manager for a copy of the supplied context.\n" +"\n" +"That will set the default context to a copy of ctx on entry to the\n" +"with-statement and restore the previous default context when exiting\n" +"the with-statement. If no context is specified, a copy of the current\n" +"default context is used."); + +#define _DECIMAL_LOCALCONTEXT_METHODDEF \ + {"localcontext", _PyCFunction_CAST(_decimal_localcontext), METH_FASTCALL|METH_KEYWORDS, _decimal_localcontext__doc__}, + +static PyObject * +_decimal_localcontext_impl(PyObject *module, PyObject *local, PyObject *prec, + PyObject *rounding, PyObject *Emin, + PyObject *Emax, PyObject *capitals, + PyObject *clamp, PyObject *flags, PyObject *traps); + +static PyObject * +_decimal_localcontext(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 9 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(ctx), &_Py_ID(prec), &_Py_ID(rounding), &_Py_ID(Emin), &_Py_ID(Emax), &_Py_ID(capitals), &_Py_ID(clamp), &_Py_ID(flags), &_Py_ID(traps), }, + }; + #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[] = {"ctx", "prec", "rounding", "Emin", "Emax", "capitals", "clamp", "flags", "traps", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "localcontext", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[9]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *local = Py_None; + PyObject *prec = Py_None; + PyObject *rounding = Py_None; + PyObject *Emin = Py_None; + PyObject *Emax = Py_None; + PyObject *capitals = Py_None; + PyObject *clamp = Py_None; + PyObject *flags = Py_None; + PyObject *traps = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + local = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + prec = args[1]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + rounding = args[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + Emin = args[3]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[4]) { + Emax = args[4]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[5]) { + capitals = args[5]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[6]) { + clamp = args[6]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[7]) { + flags = args[7]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + traps = args[8]; +skip_optional_kwonly: + return_value = _decimal_localcontext_impl(module, local, prec, rounding, Emin, Emax, capitals, clamp, flags, traps); + +exit: + return return_value; +} + PyDoc_STRVAR(_decimal_Decimal_from_float__doc__, "from_float($type, f, /)\n" "--\n" @@ -71,6 +269,45 @@ _decimal_Decimal_from_number(PyObject *type, PyObject *number) return return_value; } +PyDoc_STRVAR(_decimal_Decimal___format____doc__, +"__format__($self, format_spec, override=, /)\n" +"--\n" +"\n" +"Formats the Decimal according to format_spec."); + +#define _DECIMAL_DECIMAL___FORMAT___METHODDEF \ + {"__format__", _PyCFunction_CAST(_decimal_Decimal___format__), METH_FASTCALL, _decimal_Decimal___format____doc__}, + +static PyObject * +_decimal_Decimal___format___impl(PyObject *dec, PyObject *fmtarg, + PyObject *override); + +static PyObject * +_decimal_Decimal___format__(PyObject *dec, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *fmtarg; + PyObject *override = NULL; + + if (!_PyArg_CheckPositional("__format__", nargs, 1, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("__format__", "argument 1", "str", args[0]); + goto exit; + } + fmtarg = args[0]; + if (nargs < 2) { + goto skip_optional; + } + override = args[1]; +skip_optional: + return_value = _decimal_Decimal___format___impl(dec, fmtarg, override); + +exit: + return return_value; +} + PyDoc_STRVAR(_decimal_Decimal_as_integer_ratio__doc__, "as_integer_ratio($self, /)\n" "--\n" @@ -846,4 +1083,86 @@ _decimal_Decimal_quantize(PyObject *self, PyObject *const *args, Py_ssize_t narg exit: return return_value; } -/*[clinic end generated code: output=f33166d1bf53e613 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(_decimal_Context_power__doc__, +"power($self, /, a, b, modulo=None)\n" +"--\n" +"\n" +"Compute a**b.\n" +"\n" +"If \'a\' is negative, then \'b\' must be integral. The result will be\n" +"inexact unless \'a\' is integral and the result is finite and can be\n" +"expressed exactly in \'precision\' digits. In the Python version the\n" +"result is always correctly rounded, in the C version the result is\n" +"almost always correctly rounded.\n" +"\n" +"If modulo is given, compute (a**b) % modulo. The following\n" +"restrictions hold:\n" +"\n" +" * all three arguments must be integral\n" +" * \'b\' must be nonnegative\n" +" * at least one of \'a\' or \'b\' must be nonzero\n" +" * modulo must be nonzero and less than 10**prec in absolute value"); + +#define _DECIMAL_CONTEXT_POWER_METHODDEF \ + {"power", _PyCFunction_CAST(_decimal_Context_power), METH_FASTCALL|METH_KEYWORDS, _decimal_Context_power__doc__}, + +static PyObject * +_decimal_Context_power_impl(PyObject *context, PyObject *base, PyObject *exp, + PyObject *mod); + +static PyObject * +_decimal_Context_power(PyObject *context, 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 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { _Py_LATIN1_CHR('a'), _Py_LATIN1_CHR('b'), &_Py_ID(modulo), }, + }; + #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[] = {"a", "b", "modulo", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "power", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *base; + PyObject *exp; + PyObject *mod = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + base = args[0]; + exp = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + mod = args[2]; +skip_optional_pos: + return_value = _decimal_Context_power_impl(context, base, exp, mod); + +exit: + return return_value; +} +/*[clinic end generated code: output=6bb5c926552c2760 input=a9049054013a1b77]*/ diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index bd1c1d5295d717..3ecfd34122ab6b 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -19,32 +19,6 @@ PyDoc_STRVAR(doc__decimal, "C decimal arithmetic module"); -PyDoc_STRVAR(doc_getcontext, -"getcontext($module, /)\n--\n\n\ -Get the current default context.\n\ -\n"); - -PyDoc_STRVAR(doc_setcontext, -"setcontext($module, context, /)\n--\n\n\ -Set a new default context.\n\ -\n"); - -PyDoc_STRVAR(doc_localcontext, -"localcontext($module, /, ctx=None, **kwargs)\n--\n\n\ -Return a context manager that will set the default context to a copy of ctx\n\ -on entry to the with-statement and restore the previous default context when\n\ -exiting the with-statement. If no context is specified, a copy of the current\n\ -default context is used.\n\ -\n"); - -PyDoc_STRVAR(doc_ieee_context, -"IEEEContext($module, bits, /)\n--\n\n\ -Return a context object initialized to the proper values for one of the\n\ -IEEE interchange formats. The argument must be a multiple of 32 and less\n\ -than IEEE_CONTEXT_MAX_BITS.\n\ -\n"); - - /******************************************************************************/ /* Decimal Object and Methods */ /******************************************************************************/ @@ -619,24 +593,6 @@ Plus corresponds to the unary prefix plus operator in Python, but applies\n\ the context to the result.\n\ \n"); -PyDoc_STRVAR(doc_ctx_power, -"power($self, /, a, b, modulo=None)\n--\n\n\ -Compute a**b. If 'a' is negative, then 'b' must be integral. The result\n\ -will be inexact unless 'a' is integral and the result is finite and can\n\ -be expressed exactly in 'precision' digits. In the Python version the\n\ -result is always correctly rounded, in the C version the result is almost\n\ -always correctly rounded.\n\ -\n\ -If modulo is given, compute (a**b) % modulo. The following restrictions\n\ -hold:\n\ -\n\ - * all three arguments must be integral\n\ - * 'b' must be nonnegative\n\ - * at least one of 'a' or 'b' must be nonzero\n\ - * modulo must be nonzero and less than 10**prec in absolute value\n\ -\n\ -\n"); - PyDoc_STRVAR(doc_ctx_quantize, "quantize($self, x, y, /)\n--\n\n\ Return a value equal to x (rounded), having the exponent of y.\n\ diff --git a/Objects/descrobject.c b/Objects/descrobject.c index d3d17e92b6d1e8..06a81a4fdbd865 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -39,41 +39,41 @@ descr_name(PyDescrObject *descr) } static PyObject * -descr_repr(PyDescrObject *descr, const char *format) +descr_repr(PyDescrObject *descr, const char *kind) { PyObject *name = NULL; if (descr->d_name != NULL && PyUnicode_Check(descr->d_name)) name = descr->d_name; - return PyUnicode_FromFormat(format, name, "?", descr->d_type->tp_name); + if (descr->d_type == &PyBaseObject_Type) { + return PyUnicode_FromFormat("<%s '%V'>", kind, name, "?"); + } + return PyUnicode_FromFormat("<%s '%V' of '%s' objects>", + kind, name, "?", descr->d_type->tp_name); } static PyObject * method_repr(PyObject *descr) { - return descr_repr((PyDescrObject *)descr, - ""); + return descr_repr((PyDescrObject *)descr, "method"); } static PyObject * member_repr(PyObject *descr) { - return descr_repr((PyDescrObject *)descr, - ""); + return descr_repr((PyDescrObject *)descr, "member"); } static PyObject * getset_repr(PyObject *descr) { - return descr_repr((PyDescrObject *)descr, - ""); + return descr_repr((PyDescrObject *)descr, "attribute"); } static PyObject * wrapperdescr_repr(PyObject *descr) { - return descr_repr((PyDescrObject *)descr, - ""); + return descr_repr((PyDescrObject *)descr, "slot wrapper"); } static int diff --git a/Objects/typeobject.c b/Objects/typeobject.c index fb33bc747d885b..9cead729b6fe7a 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4039,26 +4039,15 @@ subtype_getweakref(PyObject *obj, void *context) return Py_NewRef(result); } -/* Three variants on the subtype_getsets list. */ - -static PyGetSetDef subtype_getsets_full[] = { - {"__dict__", subtype_dict, subtype_setdict, - PyDoc_STR("dictionary for instance variables")}, - {"__weakref__", subtype_getweakref, NULL, - PyDoc_STR("list of weak references to the object")}, - {0} -}; - -static PyGetSetDef subtype_getsets_dict_only[] = { - {"__dict__", subtype_dict, subtype_setdict, - PyDoc_STR("dictionary for instance variables")}, - {0} +/* getset definitions for common descriptors */ +static PyGetSetDef subtype_getset_dict = { + "__dict__", subtype_dict, subtype_setdict, + PyDoc_STR("dictionary for instance variables"), }; -static PyGetSetDef subtype_getsets_weakref_only[] = { - {"__weakref__", subtype_getweakref, NULL, - PyDoc_STR("list of weak references to the object")}, - {0} +static PyGetSetDef subtype_getset_weakref = { + "__weakref__", subtype_getweakref, NULL, + PyDoc_STR("list of weak references to the object"), }; static int @@ -4594,10 +4583,36 @@ type_new_classmethod(PyObject *dict, PyObject *attr) return 0; } +/* Add __dict__ or __weakref__ descriptor */ +static int +type_add_common_descriptor(PyInterpreterState *interp, + PyObject **cache, + PyGetSetDef *getset_def, + PyObject *dict) +{ +#ifdef Py_GIL_DISABLED + PyMutex_Lock(&interp->cached_objects.descriptor_mutex); +#endif + PyObject *descr = *cache; + if (!descr) { + descr = PyDescr_NewGetSet(&PyBaseObject_Type, getset_def); + *cache = descr; + } +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&interp->cached_objects.descriptor_mutex); +#endif + if (!descr) { + return -1; + } + if (PyDict_SetDefaultRef(dict, PyDescr_NAME(descr), descr, NULL) < 0) { + return -1; + } + return 0; +} /* Add descriptors for custom slots from __slots__, or for __dict__ */ static int -type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) +type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict) { PyHeapTypeObject *et = (PyHeapTypeObject *)type; Py_ssize_t slotoffset = ctx->base->tp_basicsize; @@ -4635,6 +4650,30 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) type->tp_basicsize = slotoffset; type->tp_itemsize = ctx->base->tp_itemsize; type->tp_members = _PyHeapType_GET_MEMBERS(et); + + PyInterpreterState *interp = _PyInterpreterState_GET(); + + if (type->tp_dictoffset) { + if (type_add_common_descriptor( + interp, + &interp->cached_objects.dict_descriptor, + &subtype_getset_dict, + dict) < 0) + { + return -1; + } + } + if (type->tp_weaklistoffset) { + if (type_add_common_descriptor( + interp, + &interp->cached_objects.weakref_descriptor, + &subtype_getset_weakref, + dict) < 0) + { + return -1; + } + } + return 0; } @@ -4642,18 +4681,7 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) static void type_new_set_slots(const type_new_ctx *ctx, PyTypeObject *type) { - if (type->tp_weaklistoffset && type->tp_dictoffset) { - type->tp_getset = subtype_getsets_full; - } - else if (type->tp_weaklistoffset && !type->tp_dictoffset) { - type->tp_getset = subtype_getsets_weakref_only; - } - else if (!type->tp_weaklistoffset && type->tp_dictoffset) { - type->tp_getset = subtype_getsets_dict_only; - } - else { - type->tp_getset = NULL; - } + type->tp_getset = NULL; /* Special case some slots */ if (type->tp_dictoffset != 0 || ctx->nslot > 0) { @@ -4758,7 +4786,7 @@ type_new_set_attrs(const type_new_ctx *ctx, PyTypeObject *type) return -1; } - if (type_new_descriptors(ctx, type) < 0) { + if (type_new_descriptors(ctx, type, dict) < 0) { return -1; } @@ -6642,6 +6670,14 @@ _PyStaticType_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type) } +void +_PyTypes_FiniCachedDescriptors(PyInterpreterState *interp) +{ + Py_CLEAR(interp->cached_objects.dict_descriptor); + Py_CLEAR(interp->cached_objects.weakref_descriptor); +} + + static void type_dealloc(PyObject *self) { diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 09ce77fd12608f..a47e4d11b54441 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -1793,37 +1793,6 @@ sys__baserepl(PyObject *module, PyObject *Py_UNUSED(ignored)) return sys__baserepl_impl(module); } -PyDoc_STRVAR(sys__clear_type_descriptors__doc__, -"_clear_type_descriptors($module, type, /)\n" -"--\n" -"\n" -"Private function for clearing certain descriptors from a type\'s dictionary.\n" -"\n" -"See gh-135228 for context."); - -#define SYS__CLEAR_TYPE_DESCRIPTORS_METHODDEF \ - {"_clear_type_descriptors", (PyCFunction)sys__clear_type_descriptors, METH_O, sys__clear_type_descriptors__doc__}, - -static PyObject * -sys__clear_type_descriptors_impl(PyObject *module, PyObject *type); - -static PyObject * -sys__clear_type_descriptors(PyObject *module, PyObject *arg) -{ - PyObject *return_value = NULL; - PyObject *type; - - if (!PyObject_TypeCheck(arg, &PyType_Type)) { - _PyArg_BadArgument("_clear_type_descriptors", "argument", (&PyType_Type)->tp_name, arg); - goto exit; - } - type = arg; - return_value = sys__clear_type_descriptors_impl(module, type); - -exit: - return return_value; -} - PyDoc_STRVAR(sys__is_gil_enabled__doc__, "_is_gil_enabled($module, /)\n" "--\n" @@ -1979,4 +1948,4 @@ _jit_is_active(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=9052f399f40ca32d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=449d16326e69dcf6 input=a9049054013a1b77]*/ diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index e22a9cc1c75050..b6b1d2845ec2f1 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1906,6 +1906,7 @@ finalize_interp_clear(PyThreadState *tstate) _PyXI_Fini(tstate->interp); _PyExc_ClearExceptionGroupType(tstate->interp); _Py_clear_generic_types(tstate->interp); + _PyTypes_FiniCachedDescriptors(tstate->interp); /* Clear interpreter state and all thread states */ _PyInterpreterState_Clear(tstate); diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 19912b4a4c6198..bedbdfc489872e 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2644,46 +2644,6 @@ sys__baserepl_impl(PyObject *module) Py_RETURN_NONE; } -/*[clinic input] -sys._clear_type_descriptors - - type: object(subclass_of='&PyType_Type') - / - -Private function for clearing certain descriptors from a type's dictionary. - -See gh-135228 for context. -[clinic start generated code]*/ - -static PyObject * -sys__clear_type_descriptors_impl(PyObject *module, PyObject *type) -/*[clinic end generated code: output=5ad17851b762b6d9 input=dc536c97fde07251]*/ -{ - PyTypeObject *typeobj = (PyTypeObject *)type; - if (_PyType_HasFeature(typeobj, Py_TPFLAGS_IMMUTABLETYPE)) { - PyErr_SetString(PyExc_TypeError, "argument is immutable"); - return NULL; - } - PyObject *dict = _PyType_GetDict(typeobj); - PyObject *dunder_dict = NULL; - if (PyDict_Pop(dict, &_Py_ID(__dict__), &dunder_dict) < 0) { - return NULL; - } - PyObject *dunder_weakref = NULL; - if (PyDict_Pop(dict, &_Py_ID(__weakref__), &dunder_weakref) < 0) { - PyType_Modified(typeobj); - Py_XDECREF(dunder_dict); - return NULL; - } - PyType_Modified(typeobj); - // We try to hold onto a reference to these until after we call - // PyType_Modified(), in case their deallocation triggers somer user code - // that tries to do something to the type. - Py_XDECREF(dunder_dict); - Py_XDECREF(dunder_weakref); - Py_RETURN_NONE; -} - /*[clinic input] sys._is_gil_enabled -> bool @@ -2881,7 +2841,6 @@ static PyMethodDef sys_methods[] = { SYS__STATS_DUMP_METHODDEF #endif SYS__GET_CPU_COUNT_CONFIG_METHODDEF - SYS__CLEAR_TYPE_DESCRIPTORS_METHODDEF SYS__IS_GIL_ENABLED_METHODDEF SYS__DUMP_TRACELETS_METHODDEF {NULL, NULL} // sentinel diff --git a/Tools/c-analyzer/cpython/_analyzer.py b/Tools/c-analyzer/cpython/_analyzer.py index 6204353e9bd26a..6f0f464892845f 100644 --- a/Tools/c-analyzer/cpython/_analyzer.py +++ b/Tools/c-analyzer/cpython/_analyzer.py @@ -67,6 +67,7 @@ 'PyMethodDef', 'PyMethodDef[]', 'PyMemberDef[]', + 'PyGetSetDef', 'PyGetSetDef[]', 'PyNumberMethods', 'PySequenceMethods',