diff options
author | Victor Stinner <vstinner@python.org> | 2021-04-10 22:17:39 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-10 22:17:39 (GMT) |
commit | 09bbebea163fe7303264cf4069c51d4d2f22fde4 (patch) | |
tree | af9449e38983bab8ebf5a667c149cb0d33e05543 | |
parent | 6e468cb16bde483ad73c1eb13b20a08d74e30846 (diff) | |
download | cpython-09bbebea163fe7303264cf4069c51d4d2f22fde4.zip cpython-09bbebea163fe7303264cf4069c51d4d2f22fde4.tar.gz cpython-09bbebea163fe7303264cf4069c51d4d2f22fde4.tar.bz2 |
bpo-43753: Add Py_Is() and Py_IsNone() functions (GH-25227)
Add the Py_Is(x, y) function to test if the 'x' object is the 'y'
object, the same as "x is y" in Python. Add also the Py_IsNone(),
Py_IsTrue(), Py_IsFalse() functions to test if an object is,
respectively, the None singleton, the True singleton or the False
singleton.
-rw-r--r-- | Doc/c-api/structures.rst | 31 | ||||
-rw-r--r-- | Doc/data/stable_abi.dat | 4 | ||||
-rw-r--r-- | Doc/whatsnew/3.10.rst | 7 | ||||
-rw-r--r-- | Include/boolobject.h | 8 | ||||
-rw-r--r-- | Include/object.h | 9 | ||||
-rw-r--r-- | Misc/NEWS.d/next/C API/2021-04-06-20-52-44.bpo-43753.xUsHp1.rst | 6 | ||||
-rw-r--r-- | Modules/_testcapimodule.c | 111 | ||||
-rw-r--r-- | Objects/object.c | 27 | ||||
-rw-r--r-- | Python/ceval.c | 33 |
9 files changed, 198 insertions, 38 deletions
diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 37072d3..20d5485 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -62,6 +62,37 @@ the definition of all other Python objects. See documentation of :c:type:`PyVarObject` above. +.. c:function:: int Py_Is(const PyObject *x, const PyObject *y) + + Test if the *x* object is the *y* object, the same as ``x is y`` in Python. + + .. versionadded:: 3.10 + + +.. c:function:: int Py_IsNone(const PyObject *x) + + Test if an object is the ``None`` singleton, + the same as ``x is None`` in Python. + + .. versionadded:: 3.10 + + +.. c:function:: int Py_IsTrue(const PyObject *x) + + Test if an object is the ``True`` singleton, + the same as ``x is True`` in Python. + + .. versionadded:: 3.10 + + +.. c:function:: int Py_IsFalse(const PyObject *x) + + Test if an object is the ``False`` singleton, + the same as ``x is False`` in Python. + + .. versionadded:: 3.10 + + .. c:function:: PyTypeObject* Py_TYPE(const PyObject *o) Get the type of the Python object *o*. diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index cd9e384..b7e3ef4 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -767,7 +767,11 @@ Py_HasFileSystemDefaultEncoding Py_IncRef Py_Initialize Py_InitializeEx +Py_Is +Py_IsFalse Py_IsInitialized +Py_IsNone +Py_IsTrue Py_LeaveRecursiveCall Py_Main Py_MakePendingCalls diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 7cf5576..18d83b6 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -1385,6 +1385,13 @@ New Features build (``Py_TRACE_REFS`` macro). (Contributed by Victor Stinner in :issue:`43688`.) +* Add the :c:func:`Py_Is(x, y) <Py_Is>` function to test if the *x* object is + the *y* object, the same as ``x is y`` in Python. Add also the + :c:func:`Py_IsNone`, :c:func:`Py_IsTrue`, :c:func:`Py_IsFalse` functions to + test if an object is, respectively, the ``None`` singleton, the ``True`` + singleton or the ``False`` singleton. + (Contributed by Victor Stinner in :issue:`43753`.) + Porting to Python 3.10 ---------------------- diff --git a/Include/boolobject.h b/Include/boolobject.h index 6673d72..e1c8699 100644 --- a/Include/boolobject.h +++ b/Include/boolobject.h @@ -21,6 +21,14 @@ PyAPI_DATA(struct _longobject) _Py_FalseStruct, _Py_TrueStruct; #define Py_False ((PyObject *) &_Py_FalseStruct) #define Py_True ((PyObject *) &_Py_TrueStruct) +// Test if an object is the True singleton, the same as "x is True" in Python. +PyAPI_FUNC(int) Py_IsTrue(PyObject *x); +#define Py_IsTrue(x) Py_Is((x), Py_True) + +// Test if an object is the False singleton, the same as "x is False" in Python. +PyAPI_FUNC(int) Py_IsFalse(PyObject *x); +#define Py_IsFalse(x) Py_Is((x), Py_False) + /* Macros for returning Py_True or Py_False, respectively */ #define Py_RETURN_TRUE return Py_NewRef(Py_True) #define Py_RETURN_FALSE return Py_NewRef(Py_False) diff --git a/Include/object.h b/Include/object.h index 3138db0..695f015 100644 --- a/Include/object.h +++ b/Include/object.h @@ -122,6 +122,11 @@ typedef struct { #define _PyVarObject_CAST_CONST(op) ((const PyVarObject*)(op)) +// Test if the 'x' object is the 'y' object, the same as "x is y" in Python. +PyAPI_FUNC(int) Py_Is(PyObject *x, PyObject *y); +#define Py_Is(x, y) ((x) == (y)) + + static inline Py_ssize_t _Py_REFCNT(const PyObject *ob) { return ob->ob_refcnt; } @@ -586,6 +591,10 @@ Don't forget to apply Py_INCREF() when returning this value!!! PyAPI_DATA(PyObject) _Py_NoneStruct; /* Don't use this directly */ #define Py_None (&_Py_NoneStruct) +// Test if an object is the None singleton, the same as "x is None" in Python. +PyAPI_FUNC(int) Py_IsNone(PyObject *x); +#define Py_IsNone(x) Py_Is((x), Py_None) + /* Macro for returning Py_None from a function */ #define Py_RETURN_NONE return Py_NewRef(Py_None) diff --git a/Misc/NEWS.d/next/C API/2021-04-06-20-52-44.bpo-43753.xUsHp1.rst b/Misc/NEWS.d/next/C API/2021-04-06-20-52-44.bpo-43753.xUsHp1.rst new file mode 100644 index 0000000..f0eac4c --- /dev/null +++ b/Misc/NEWS.d/next/C API/2021-04-06-20-52-44.bpo-43753.xUsHp1.rst @@ -0,0 +1,6 @@ +Add the :c:func:`Py_Is(x, y) <Py_Is>` function to test if the *x* object is the +*y* object, the same as ``x is y`` in Python. Add also the :c:func:`Py_IsNone`, +:c:func:`Py_IsTrue`, :c:func:`Py_IsFalse` functions to test if an object is, +respectively, the ``None`` singleton, the ``True`` singleton or the ``False`` +singleton. +Patch by Victor Stinner. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 20107f2..db62aea 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5401,32 +5401,98 @@ test_set_type_size(PyObject *self, PyObject *Py_UNUSED(ignored)) } -// Test Py_NewRef() and Py_XNewRef() functions +#define TEST_REFCOUNT() \ + do { \ + PyObject *obj = PyList_New(0); \ + if (obj == NULL) { \ + return NULL; \ + } \ + assert(Py_REFCNT(obj) == 1); \ + \ + /* test Py_NewRef() */ \ + PyObject *ref = Py_NewRef(obj); \ + assert(ref == obj); \ + assert(Py_REFCNT(obj) == 2); \ + Py_DECREF(ref); \ + \ + /* test Py_XNewRef() */ \ + PyObject *xref = Py_XNewRef(obj); \ + assert(xref == obj); \ + assert(Py_REFCNT(obj) == 2); \ + Py_DECREF(xref); \ + \ + assert(Py_XNewRef(NULL) == NULL); \ + \ + Py_DECREF(obj); \ + Py_RETURN_NONE; \ + } while (0) \ + + +// Test Py_NewRef() and Py_XNewRef() macros static PyObject* -test_refcount(PyObject *self, PyObject *Py_UNUSED(ignored)) +test_refcount_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) { - PyObject *obj = PyList_New(0); - if (obj == NULL) { - return NULL; - } - assert(Py_REFCNT(obj) == 1); + TEST_REFCOUNT(); +} + +#undef Py_NewRef +#undef Py_XNewRef + +// Test Py_NewRef() and Py_XNewRef() functions, after undefining macros. +static PyObject* +test_refcount_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_REFCOUNT(); +} - // Test Py_NewRef() - PyObject *ref = Py_NewRef(obj); - assert(ref == obj); - assert(Py_REFCNT(obj) == 2); - Py_DECREF(ref); - // Test Py_XNewRef() - PyObject *xref = Py_XNewRef(obj); - assert(xref == obj); - assert(Py_REFCNT(obj) == 2); - Py_DECREF(xref); +// Test Py_Is() function +#define TEST_PY_IS() \ + do { \ + PyObject *o_none = Py_None; \ + PyObject *o_true = Py_True; \ + PyObject *o_false = Py_False; \ + PyObject *obj = PyList_New(0); \ + if (obj == NULL) { \ + return NULL; \ + } \ + \ + /* test Py_Is() */ \ + assert(Py_Is(obj, obj)); \ + assert(!Py_Is(obj, o_none)); \ + \ + /* test Py_None */ \ + assert(Py_Is(o_none, o_none)); \ + assert(!Py_Is(obj, o_none)); \ + \ + /* test Py_True */ \ + assert(Py_Is(o_true, o_true)); \ + assert(!Py_Is(o_false, o_true)); \ + assert(!Py_Is(obj, o_true)); \ + \ + /* test Py_False */ \ + assert(Py_Is(o_false, o_false)); \ + assert(!Py_Is(o_true, o_false)); \ + assert(!Py_Is(obj, o_false)); \ + \ + Py_DECREF(obj); \ + Py_RETURN_NONE; \ + } while (0) + +// Test Py_Is() macro +static PyObject* +test_py_is_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_PY_IS(); +} - assert(Py_XNewRef(NULL) == NULL); +#undef Py_Is - Py_DECREF(obj); - Py_RETURN_NONE; +// Test Py_Is() function, after undefining its macro. +static PyObject* +test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_PY_IS(); } @@ -5716,7 +5782,10 @@ static PyMethodDef TestMethods[] = { {"pynumber_tobase", pynumber_tobase, METH_VARARGS}, {"without_gc", without_gc, METH_O}, {"test_set_type_size", test_set_type_size, METH_NOARGS}, - {"test_refcount", test_refcount, METH_NOARGS}, + {"test_refcount_macros", test_refcount_macros, METH_NOARGS}, + {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS}, + {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, + {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {"fatal_error", test_fatal_error, METH_VARARGS, PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")}, {NULL, NULL} /* sentinel */ diff --git a/Objects/object.c b/Objects/object.c index 1224160..4b67840 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2279,6 +2279,33 @@ Py_XNewRef(PyObject *obj) return _Py_XNewRef(obj); } +#undef Py_Is +#undef Py_IsNone +#undef Py_IsTrue +#undef Py_IsFalse + +// Export Py_Is(), Py_IsNone(), Py_IsTrue(), Py_IsFalse() as regular functions +// for the stable ABI. +int Py_Is(PyObject *x, PyObject *y) +{ + return (x == y); +} + +int Py_IsNone(PyObject *x) +{ + return Py_Is(x, Py_None); +} + +int Py_IsTrue(PyObject *x) +{ + return Py_Is(x, Py_True); +} + +int Py_IsFalse(PyObject *x) +{ + return Py_Is(x, Py_False); +} + #ifdef __cplusplus } #endif diff --git a/Python/ceval.c b/Python/ceval.c index ea31179..c121160 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1,4 +1,3 @@ - /* Execute compiled code */ /* XXX TO DO: @@ -2570,7 +2569,7 @@ main_loop: gen_status = PyIter_Send(receiver, v, &retval); } else { _Py_IDENTIFIER(send); - if (v == Py_None && PyIter_Check(receiver)) { + if (Py_IsNone(v) && PyIter_Check(receiver)) { retval = Py_TYPE(receiver)->tp_iternext(receiver); } else { @@ -2634,7 +2633,7 @@ main_loop: case TARGET(GEN_START): { PyObject *none = POP(); Py_DECREF(none); - if (none != Py_None) { + if (!Py_IsNone(none)) { if (oparg > 2) { _PyErr_SetString(tstate, PyExc_SystemError, "Illegal kind for GEN_START"); @@ -3615,7 +3614,7 @@ main_loop: case TARGET(IS_OP): { PyObject *right = POP(); PyObject *left = TOP(); - int res = (left == right)^oparg; + int res = Py_Is(left, right) ^ oparg; PyObject *b = res ? Py_True : Py_False; Py_INCREF(b); SET_TOP(b); @@ -3744,11 +3743,11 @@ main_loop: PREDICTED(POP_JUMP_IF_FALSE); PyObject *cond = POP(); int err; - if (cond == Py_True) { + if (Py_IsTrue(cond)) { Py_DECREF(cond); DISPATCH(); } - if (cond == Py_False) { + if (Py_IsFalse(cond)) { Py_DECREF(cond); JUMPTO(oparg); DISPATCH(); @@ -3768,11 +3767,11 @@ main_loop: PREDICTED(POP_JUMP_IF_TRUE); PyObject *cond = POP(); int err; - if (cond == Py_False) { + if (Py_IsFalse(cond)) { Py_DECREF(cond); DISPATCH(); } - if (cond == Py_True) { + if (Py_IsTrue(cond)) { Py_DECREF(cond); JUMPTO(oparg); DISPATCH(); @@ -3792,12 +3791,12 @@ main_loop: case TARGET(JUMP_IF_FALSE_OR_POP): { PyObject *cond = TOP(); int err; - if (cond == Py_True) { + if (Py_IsTrue(cond)) { STACK_SHRINK(1); Py_DECREF(cond); DISPATCH(); } - if (cond == Py_False) { + if (Py_IsFalse(cond)) { JUMPTO(oparg); DISPATCH(); } @@ -3816,12 +3815,12 @@ main_loop: case TARGET(JUMP_IF_TRUE_OR_POP): { PyObject *cond = TOP(); int err; - if (cond == Py_False) { + if (Py_IsFalse(cond)) { STACK_SHRINK(1); Py_DECREF(cond); DISPATCH(); } - if (cond == Py_True) { + if (Py_IsTrue(cond)) { JUMPTO(oparg); DISPATCH(); } @@ -3966,7 +3965,7 @@ main_loop: goto error; } PUSH(values_or_none); - if (values_or_none == Py_None) { + if (Py_IsNone(values_or_none)) { Py_INCREF(Py_False); PUSH(Py_False); DISPATCH(); @@ -4157,7 +4156,7 @@ main_loop: exc = TOP(); val = SECOND(); tb = THIRD(); - assert(exc != Py_None); + assert(!Py_IsNone(exc)); assert(!PyLong_Check(exc)); exit_func = PEEK(7); PyObject *stack[4] = {NULL, exc, val, tb}; @@ -5235,7 +5234,7 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) type = exc_info->exc_type; value = exc_info->exc_value; tb = exc_info->exc_traceback; - if (type == Py_None || type == NULL) { + if (Py_IsNone(type) || type == NULL) { _PyErr_SetString(tstate, PyExc_RuntimeError, "No active exception to reraise"); return 0; @@ -5293,7 +5292,7 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) else if (PyExceptionInstance_Check(cause)) { fixed_cause = cause; } - else if (cause == Py_None) { + else if (Py_IsNone(cause)) { Py_DECREF(cause); fixed_cause = NULL; } @@ -5987,7 +5986,7 @@ int _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi) { PyThreadState *tstate = _PyThreadState_GET(); - if (v != Py_None) { + if (!Py_IsNone(v)) { Py_ssize_t x; if (_PyIndex_Check(v)) { x = PyNumber_AsSsize_t(v, NULL); |