[Python-checkins] bpo-43753: Add Py_Is() and Py_IsNone() functions (GH-25227)

vstinner webhook-mailer at python.org
Sat Apr 10 18:17:47 EDT 2021


https://github.com/python/cpython/commit/09bbebea163fe7303264cf4069c51d4d2f22fde4
commit: 09bbebea163fe7303264cf4069c51d4d2f22fde4
branch: master
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-04-11T00:17:39+02:00
summary:

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.

files:
A Misc/NEWS.d/next/C API/2021-04-06-20-52-44.bpo-43753.xUsHp1.rst
M Doc/c-api/structures.rst
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.10.rst
M Include/boolobject.h
M Include/object.h
M Modules/_testcapimodule.c
M Objects/object.c
M Python/ceval.c

diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst
index 37072d30d5715..20d5485d5544c 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 cd9e384eaea2b..b7e3ef4a676a8 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 7cf5576765748..18d83b676295e 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 6673d7206c0b3..e1c8699ee70fa 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 3138db0a0a3e4..695f01564282c 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 0000000000000..f0eac4cf8e737
--- /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 20107f225794a..db62aea421c80 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 1224160dd50c4..4b678403c0761 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 ea31179f8629b..c12116044b54e 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1,4 +1,3 @@
-
 /* Execute compiled code */
 
 /* XXX TO DO:
@@ -2570,7 +2569,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
                 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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
         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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
         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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
             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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
             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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
         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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
         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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
                 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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
             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);



More information about the Python-checkins mailing list