Skip to content

Commit 233a0d5

Browse files
committed
Add Py_Is(), Py_IsNone(), Py_IsTrue(), Py_IsFalse()
1 parent 2c86932 commit 233a0d5

File tree

5 files changed

+158
-0
lines changed

5 files changed

+158
-0
lines changed

README.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ Upgrade Operations
108108
* Replace ``obj->ob_refcnt = refcnt;`` with ``Py_SET_REFCNT(obj, refcnt);``.
109109
* Replace ``Py_REFCNT(obj) = refcnt;`` with ``Py_SET_REFCNT(obj, refcnt);``.
110110

111+
* ``Py_Is``:
112+
113+
* Replace ``x == Py_None`` with ``Py_IsNone(x)``.
114+
* Replace ``x == Py_True`` with ``Py_IsTrue(x)``.
115+
* Replace ``x == Py_False`` with ``Py_IsFalse(x)``.
116+
* Replace ``x != Py_None`` with ``!Py_IsNone(x)``.
117+
* Replace ``x != Py_True`` with ``!Py_IsTrue(x)``.
118+
* Replace ``x != Py_False`` with ``!Py_IsFalse(x)``.
119+
111120
* ``PyObject_NEW``:
112121

113122
* Replace ``PyObject_NEW(...)`` with ``PyObject_New(...)``.
@@ -156,6 +165,10 @@ Python 3.10
156165

157166
PyObject* Py_NewRef(PyObject *obj);
158167
PyObject* Py_XNewRef(PyObject *obj);
168+
int Py_Is(PyObject *x, PyObject *y);
169+
int Py_IsNone(PyObject *x);
170+
int Py_IsTrue(PyObject *x);
171+
int Py_IsFalse(PyObject *x);
159172

160173
int PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value);
161174

@@ -324,6 +337,7 @@ Links
324337
Changelog
325338
=========
326339

340+
* 2021-04-09: Add Py_Is(), Py_IsNone(), Py_IsTrue(), Py_IsFalse() functions.
327341
* 2021-04-01:
328342

329343
* Add ``Py_SETREF()``, ``Py_XSETREF()`` and ``Py_UNUSED()``.

pythoncapi_compat.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,23 @@ static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt)
113113
} while (0)
114114
#endif
115115

116+
117+
// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse()
118+
// to Python 3.10.0b1.
119+
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is)
120+
# define Py_Is(x, y) ((x) == (y))
121+
#endif
122+
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone)
123+
# define Py_IsNone(x) Py_Is(x, Py_None)
124+
#endif
125+
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsTrue)
126+
# define Py_IsTrue(x) Py_Is(x, Py_True)
127+
#endif
128+
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsFalse)
129+
# define Py_IsFalse(x) Py_Is(x, Py_False)
130+
#endif
131+
132+
116133
// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4
117134
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE)
118135
static inline void

tests/test_pythoncapi_compat_cext.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,40 @@ test_object(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
8686
}
8787

8888

89+
static PyObject *
90+
test_py_is(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
91+
{
92+
PyObject *o_none = Py_None;
93+
PyObject *o_true = Py_True;
94+
PyObject *o_false = Py_False;
95+
PyObject *obj = PyList_New(0);
96+
if (obj == NULL) {
97+
return NULL;
98+
}
99+
100+
/* test Py_Is() */
101+
assert(Py_Is(obj, obj));
102+
assert(!Py_Is(obj, o_none));
103+
104+
/* test Py_IsNone() */
105+
assert(Py_IsNone(o_none));
106+
assert(!Py_IsNone(obj));
107+
108+
/* test Py_IsTrue() */
109+
assert(Py_IsTrue(o_true));
110+
assert(!Py_IsTrue(o_false));
111+
assert(!Py_IsTrue(obj));
112+
113+
/* testPy_IsFalse() */
114+
assert(Py_IsFalse(o_false));
115+
assert(!Py_IsFalse(o_true));
116+
assert(!Py_IsFalse(obj));
117+
118+
Py_DECREF(obj);
119+
Py_RETURN_NONE;
120+
}
121+
122+
89123
static PyObject *
90124
test_steal_ref(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
91125
{
@@ -358,6 +392,7 @@ test_module(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
358392

359393
static struct PyMethodDef methods[] = {
360394
{"test_object", test_object, METH_NOARGS, NULL},
395+
{"test_py_is", test_py_is, METH_NOARGS, NULL},
361396
{"test_steal_ref", test_steal_ref, METH_NOARGS, NULL},
362397
#if !defined(PYPY_VERSION)
363398
{"test_frame", test_frame, METH_NOARGS, NULL},

tests/test_upgrade_pythoncapi.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def reformat(source):
2222

2323

2424
class Tests(unittest.TestCase):
25+
maxDiff = 80 * 30
26+
2527
def _test_patch_file(self, tmp_dir):
2628
# test Patcher.patcher()
2729
source = """
@@ -473,6 +475,67 @@ def test_py_decref_assign(self):
473475
}
474476
""")
475477

478+
def test_py_is(self):
479+
self.check_replace("""
480+
void test_py_is(PyObject *x)
481+
{
482+
if (x == Py_None) {
483+
return 1;
484+
}
485+
if (x == Py_True) {
486+
return 2;
487+
}
488+
if (x == Py_False) {
489+
return 3;
490+
}
491+
return 0;
492+
}
493+
494+
void test_py_is_not(PyObject *x)
495+
{
496+
if (x != Py_None) {
497+
return 1;
498+
}
499+
if (x != Py_True) {
500+
return 2;
501+
}
502+
if (x != Py_False) {
503+
return 3;
504+
}
505+
return 0;
506+
}
507+
""", """
508+
#include "pythoncapi_compat.h"
509+
510+
void test_py_is(PyObject *x)
511+
{
512+
if (Py_IsNone(x)) {
513+
return 1;
514+
}
515+
if (Py_IsTrue(x)) {
516+
return 2;
517+
}
518+
if (Py_IsFalse(x)) {
519+
return 3;
520+
}
521+
return 0;
522+
}
523+
524+
void test_py_is_not(PyObject *x)
525+
{
526+
if (!Py_IsNone(x)) {
527+
return 1;
528+
}
529+
if (!Py_IsTrue(x)) {
530+
return 2;
531+
}
532+
if (!Py_IsFalse(x)) {
533+
return 3;
534+
}
535+
return 0;
536+
}
537+
""")
538+
476539

477540
if __name__ == "__main__":
478541
unittest.main()

upgrade_pythoncapi.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,33 @@ def replace2(regs):
301301
NEED_PYTHONCAPI_COMPAT = True
302302

303303

304+
class Py_Is(Operation):
305+
NAME = "Py_Is"
306+
DOC = ('replace "x == Py_None" with Py_IsNone(x), '
307+
'replace "x != Py_None" with !Py_IsNone(x), '
308+
'and do the same for Py_True/Py_False with Py_IsTrue()/Py_IsFalse()')
309+
310+
def replace2(regs):
311+
x = regs.group(1)
312+
y = regs.group(2)
313+
if y == 'NULL':
314+
return regs.group(0)
315+
return f'{x} = _Py_StealRef({y});'
316+
317+
REPLACE = []
318+
id_regex = r'(%s)' % ID_REGEX
319+
for name in ('None', 'True', 'False'):
320+
REPLACE.extend((
321+
(re.compile(fr'({ID_REGEX}) == Py_{name}\b'),
322+
fr'Py_Is{name}(\1)'),
323+
(re.compile(fr'({ID_REGEX}) != Py_{name}\b'),
324+
fr'!Py_Is{name}(\1)'),
325+
))
326+
327+
# Need Py_IsNone(), Py_IsTrue(), Py_IsFalse(): new in Python 3.10
328+
NEED_PYTHONCAPI_COMPAT = True
329+
330+
304331
OPERATIONS = [
305332
Py_SET_TYPE,
306333
Py_SET_SIZE,
@@ -310,6 +337,8 @@ def replace2(regs):
310337
Py_SIZE,
311338
Py_REFCNT,
312339

340+
Py_Is,
341+
313342
PyObject_NEW,
314343
PyMem_MALLOC,
315344
PyObject_MALLOC,

0 commit comments

Comments
 (0)