| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4701ff9 commit f237953
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -613,6 +613,38 @@ Object Protocol | |||
| 613 | 613 | ||
| 614 | 614 | .. versionadded:: 3.14 | |
| 615 | 615 | ||
| 616 | + .. c:function:: int PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *obj) | ||
| 617 | + | ||
| 618 | + Check if *obj* is a unique temporary object. | ||
| 619 | + Returns ``1`` if *obj* is known to be a unique temporary object, | ||
| 620 | + and ``0`` otherwise. This function cannot fail, but the check is | ||
| 621 | + conservative, and may return ``0`` in some cases even if *obj* is a unique | ||
| 622 | + temporary object. | ||
| 623 | + | ||
| 624 | + If an object is a unique temporary, it is guaranteed that the current code | ||
| 625 | + has the only reference to the object. For arguments to C functions, this | ||
| 626 | + should be used instead of checking if the reference count is ``1``. Starting | ||
| 627 | + with Python 3.14, the interpreter internally avoids some reference count | ||
| 628 | + modifications when loading objects onto the operands stack by | ||
| 629 | + :term:`borrowing <borrowed reference>` references when possible, which means | ||
| 630 | + that a reference count of ``1`` by itself does not guarantee that a function | ||
| 631 | + argument uniquely referenced. | ||
| 632 | + | ||
| 633 | + In the example below, ``my_func`` is called with a unique temporary object | ||
| 634 | + as its argument:: | ||
| 635 | + | ||
| 636 | + my_func([1, 2, 3]) | ||
| 637 | + | ||
| 638 | + In the example below, ``my_func`` is **not** called with a unique temporary | ||
| 639 | + object as its argument, even if its refcount is ``1``:: | ||
| 640 | + | ||
| 641 | + my_list = [1, 2, 3] | ||
| 642 | + my_func(my_list) | ||
| 643 | + | ||
| 644 | + See also the function :c:func:`Py_REFCNT`. | ||
| 645 | + | ||
| 646 | + .. versionadded:: 3.14 | ||
| 647 | + | ||
| 616 | 648 | .. c:function:: int PyUnstable_IsImmortal(PyObject *obj) | |
| 617 | 649 | ||
| 618 | 650 | This function returns non-zero if *obj* is :term:`immortal`, and zero | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,6 +23,8 @@ of Python objects. | |||
| 23 | 23 | ||
| 24 | 24 | Use the :c:func:`Py_SET_REFCNT()` function to set an object reference count. | |
| 25 | 25 | ||
| 26 | + See also the function :c:func:`PyUnstable_Object_IsUniqueReferencedTemporary()`. | ||
| 27 | + | ||
| 26 | 28 | .. versionchanged:: 3.10 | |
| 27 | 29 | :c:func:`Py_REFCNT()` is changed to the inline static function. | |
| 28 | 30 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -89,6 +89,10 @@ If you encounter :exc:`NameError`\s or pickling errors coming out of | |||
| 89 | 89 | :mod:`multiprocessing` or :mod:`concurrent.futures`, see the | |
| 90 | 90 | :ref:`forkserver restrictions <multiprocessing-programming-forkserver>`. | |
| 91 | 91 | ||
| 92 | + The interpreter avoids some reference count modifications internally when | ||
| 93 | + it's safe to do so. This can lead to different values returned from | ||
| 94 | + :func:`sys.getrefcount` and :c:func:`Py_REFCNT` compared to previous versions | ||
| 95 | + of Python. See :ref:`below <whatsnew314-refcount>` for details. | ||
| 92 | 96 | ||
| 93 | 97 | New features | |
| 94 | 98 | ============ | |
@@ -2215,6 +2219,11 @@ New features | |||
| 2215 | 2219 | take a C integer and produce a Python :class:`bool` object. (Contributed by | |
| 2216 | 2220 | Pablo Galindo in :issue:`45325`.) | |
| 2217 | 2221 | ||
| 2222 | + * Add :c:func:`PyUnstable_Object_IsUniqueReferencedTemporary` to determine if an object | ||
| 2223 | + is a unique temporary object on the interpreter's operand stack. This can | ||
| 2224 | + be used in some cases as a replacement for checking if :c:func:`Py_REFCNT` | ||
| 2225 | + is ``1`` for Python objects passed as arguments to C API functions. | ||
| 2226 | + | ||
| 2218 | 2227 | ||
| 2219 | 2228 | Limited C API changes | |
| 2220 | 2229 | --------------------- | |
@@ -2249,6 +2258,17 @@ Porting to Python 3.14 | |||
| 2249 | 2258 | a :exc:`UnicodeError` object. | |
| 2250 | 2259 | (Contributed by Bénédikt Tran in :gh:`127691`.) | |
| 2251 | 2260 | ||
| 2261 | + .. _whatsnew314-refcount: | ||
| 2262 | + | ||
| 2263 | + * The interpreter internally avoids some reference count modifications when | ||
| 2264 | + loading objects onto the operands stack by :term:`borrowing <borrowed reference>` | ||
| 2265 | + references when possible. This can lead to smaller reference count values | ||
| 2266 | + compared to previous Python versions. C API extensions that checked | ||
| 2267 | + :c:func:`Py_REFCNT` of ``1`` to determine if an function argument is not | ||
| 2268 | + referenced by any other code should instead use | ||
| 2269 | + :c:func:`PyUnstable_Object_IsUniqueReferencedTemporary` as a safer replacement. | ||
| 2270 | + | ||
| 2271 | + | ||
| 2252 | 2272 | * Private functions promoted to public C APIs: | |
| 2253 | 2273 | ||
| 2254 | 2274 | * ``_PyBytes_Join()``: :c:func:`PyBytes_Join`. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -476,6 +476,11 @@ PyAPI_FUNC(PyRefTracer) PyRefTracer_GetTracer(void**); | |||
| 476 | 476 | */ | |
| 477 | 477 | PyAPI_FUNC(int) PyUnstable_Object_EnableDeferredRefcount(PyObject *); | |
| 478 | 478 | ||
| 479 | + /* Determine if the object exists as a unique temporary variable on the | ||
| 480 | + * topmost frame of the interpreter. | ||
| 481 | + */ | ||
| 482 | + PyAPI_FUNC(int) PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *); | ||
| 483 | + | ||
| 479 | 484 | /* Check whether the object is immortal. This cannot fail. */ | |
| 480 | 485 | PyAPI_FUNC(int) PyUnstable_IsImmortal(PyObject *); | |
| 481 | 486 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,5 @@ | |||
| 1 | 1 | import enum | |
| 2 | + import sys | ||
| 2 | 3 | import textwrap | |
| 3 | 4 | import unittest | |
| 4 | 5 | from test import support | |
@@ -223,5 +224,17 @@ def __del__(self): | |||
| 223 | 224 | obj = MyObj() | |
| 224 | 225 | _testinternalcapi.incref_decref_delayed(obj) | |
| 225 | 226 | ||
| 227 | + def test_is_unique_temporary(self): | ||
| 228 | + self.assertTrue(_testcapi.pyobject_is_unique_temporary(object())) | ||
| 229 | + obj = object() | ||
| 230 | + self.assertFalse(_testcapi.pyobject_is_unique_temporary(obj)) | ||
| 231 | + | ||
| 232 | + def func(x): | ||
| 233 | + # This relies on the LOAD_FAST_BORROW optimization (gh-130704) | ||
| 234 | + self.assertEqual(sys.getrefcount(x), 1) | ||
| 235 | + self.assertFalse(_testcapi.pyobject_is_unique_temporary(x)) | ||
| 236 | + | ||
| 237 | + func(object()) | ||
| 238 | + | ||
| 226 | 239 | if __name__ == "__main__": | |
| 227 | 240 | unittest.main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,5 @@ | |||
| 1 | + Add :c:func:`PyUnstable_Object_IsUniqueReferencedTemporary` function for | ||
| 2 | + determining if an object exists as a unique temporary variable on the | ||
| 3 | + interpreter's stack. This is a replacement for some cases where checking | ||
| 4 | + that :c:func:`Py_REFCNT` is one is no longer sufficient to determine if it's | ||
| 5 | + safe to modify a Python object in-place with no visible side effects. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -131,6 +131,13 @@ pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj) | |||
| 131 | 131 | return PyLong_FromLong(result); | |
| 132 | 132 | } | |
| 133 | 133 | ||
| 134 | + static PyObject * | ||
| 135 | + pyobject_is_unique_temporary(PyObject *self, PyObject *obj) | ||
| 136 | + { | ||
| 137 | + int result = PyUnstable_Object_IsUniqueReferencedTemporary(obj); | ||
| 138 | + return PyLong_FromLong(result); | ||
| 139 | + } | ||
| 140 | + | ||
| 134 | 141 | static int MyObject_dealloc_called = 0; | |
| 135 | 142 | ||
| 136 | 143 | static void | |
@@ -478,6 +485,7 @@ static PyMethodDef test_methods[] = { | |||
| 478 | 485 | {"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS}, | |
| 479 | 486 | {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, | |
| 480 | 487 | {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, | |
| 488 | + {"pyobject_is_unique_temporary", pyobject_is_unique_temporary, METH_O}, | ||
| 481 | 489 | {"test_py_try_inc_ref", test_py_try_inc_ref, METH_NOARGS}, | |
| 482 | 490 | {"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS}, | |
| 483 | 491 | {"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS}, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,6 +15,7 @@ | |||
| 15 | 15 | #include "pycore_hamt.h" // _PyHamtItems_Type | |
| 16 | 16 | #include "pycore_initconfig.h" // _PyStatus_OK() | |
| 17 | 17 | #include "pycore_instruction_sequence.h" // _PyInstructionSequence_Type | |
| 18 | + #include "pycore_interpframe.h" // _PyFrame_Stackbase() | ||
| 18 | 19 | #include "pycore_interpolation.h" // _PyInterpolation_Type | |
| 19 | 20 | #include "pycore_list.h" // _PyList_DebugMallocStats() | |
| 20 | 21 | #include "pycore_long.h" // _PyLong_GetZero() | |
@@ -2621,6 +2622,29 @@ PyUnstable_Object_EnableDeferredRefcount(PyObject *op) | |||
| 2621 | 2622 | #endif | |
| 2622 | 2623 | } | |
| 2623 | 2624 | ||
| 2625 | + int | ||
| 2626 | + PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *op) | ||
| 2627 | + { | ||
| 2628 | + if (!_PyObject_IsUniquelyReferenced(op)) { | ||
| 2629 | + return 0; | ||
| 2630 | + } | ||
| 2631 | + | ||
| 2632 | + _PyInterpreterFrame *frame = _PyEval_GetFrame(); | ||
| 2633 | + if (frame == NULL) { | ||
| 2634 | + return 0; | ||
| 2635 | + } | ||
| 2636 | + | ||
| 2637 | + _PyStackRef *base = _PyFrame_Stackbase(frame); | ||
| 2638 | + _PyStackRef *stackpointer = frame->stackpointer; | ||
| 2639 | + while (stackpointer > base) { | ||
| 2640 | + stackpointer--; | ||
| 2641 | + if (op == PyStackRef_AsPyObjectBorrow(*stackpointer)) { | ||
| 2642 | + return PyStackRef_IsHeapSafe(*stackpointer); | ||
| 2643 | + } | ||
| 2644 | + } | ||
| 2645 | + return 0; | ||
| 2646 | + } | ||
| 2647 | + | ||
| 2624 | 2648 | int | |
| 2625 | 2649 | PyUnstable_TryIncRef(PyObject *op) | |
| 2626 | 2650 | { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments