From f52afe61cbae996b9a8b819e5d4afcd0af639385 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Wed, 12 Jun 2024 04:04:30 +0300 Subject: [PATCH 1/4] in_ and not_in --- Doc/library/operator.rst | 10 +++++ Lib/operator.py | 15 +++++++- Lib/test/test_operator.py | 16 ++++++++ Modules/_operator.c | 40 ++++++++++++++++++++ Modules/clinic/_operator.c.h | 72 +++++++++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 2 deletions(-) diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index a9a6026af406fe8..e43fcc515345851 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -208,6 +208,16 @@ Operations which work with sequences (some of them with mappings too) include: Return ``a + b`` for *a* and *b* sequences. +.. function:: in_(a, b) + + Return the outcome of the test ``a in b``. + + +.. function:: not_in(a, b) + + Return the outcome of the test ``a not in b``. + + .. function:: contains(a, b) __contains__(a, b) diff --git a/Lib/operator.py b/Lib/operator.py index 02ccdaa13ddb318..7c36d650f430f5e 100644 --- a/Lib/operator.py +++ b/Lib/operator.py @@ -10,7 +10,8 @@ This is the pure Python implementation of the module. """ -__all__ = ['abs', 'add', 'and_', 'attrgetter', 'call', 'concat', 'contains', 'countOf', +__all__ = ['abs', 'add', 'and_', 'attrgetter', 'call', 'concat', + 'in_', 'not_in', 'contains', 'countOf', 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand', 'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul', 'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', @@ -150,10 +151,22 @@ def concat(a, b): raise TypeError(msg) return a + b + +def in_(a, b): + "Same as a in b." + return a in b + + +def not_in(a, b): + "Same as a not in b." + return a not in b + + def contains(a, b): "Same as b in a (note reversed operands)." return b in a + def countOf(a, b): "Return the number of items in a which are, or which equal, b." count = 0 diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index f8eac8dc0026369..e8cdd0178d57583 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -290,6 +290,22 @@ def test_rshift(self): self.assertEqual(operator.rshift(5, 0), 5) self.assertRaises(ValueError, operator.rshift, 2, -1) + def test_in(self): + operator = self.module + self.assertRaises(TypeError, operator.in_) + self.assertRaises(TypeError, operator.in_, None, None) + self.assertRaises(ZeroDivisionError, operator.in_, 1, BadIterable()) + self.assertTrue(operator.in_(2, range(4))) + self.assertFalse(operator.in_(5, range(4))) + + def test_not_in(self): + operator = self.module + self.assertRaises(TypeError, operator.not_in) + self.assertRaises(TypeError, operator.not_in, None, None) + self.assertRaises(ZeroDivisionError, operator.not_in, 1, BadIterable()) + self.assertFalse(operator.not_in(2, range(4))) + self.assertTrue(operator.not_in(5, range(4))) + def test_contains(self): operator = self.module self.assertRaises(TypeError, operator.contains) diff --git a/Modules/_operator.c b/Modules/_operator.c index 5d3f88327d19ad7..3e038c7916c7d00 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -474,6 +474,44 @@ _operator_iconcat_impl(PyObject *module, PyObject *a, PyObject *b) return PySequence_InPlaceConcat(a, b); } +/*[clinic input] +_operator.in_ -> bool + + a: object + b: object + / + +Same as a in b. +[clinic start generated code]*/ + +static int +_operator_in__impl(PyObject *module, PyObject *a, PyObject *b) +/*[clinic end generated code: output=cd4eca456096fa9a input=8bd20ace23fe9808]*/ +{ + return PySequence_Contains(b, a); +} + +/*[clinic input] +_operator.not_in -> bool + + a: object + b: object + / + +Same as a not in b. +[clinic start generated code]*/ + +static int +_operator_not_in_impl(PyObject *module, PyObject *a, PyObject *b) +/*[clinic end generated code: output=93a4db26908835bd input=203499d558a29ee3]*/ +{ + int result = PySequence_Contains(b, a); + if (result != -1) { + result = 1 - result; + } + return result; +} + /*[clinic input] _operator.contains -> bool @@ -910,6 +948,8 @@ _operator_call(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje static struct PyMethodDef operator_methods[] = { _OPERATOR_TRUTH_METHODDEF + _OPERATOR_IN__METHODDEF + _OPERATOR_NOT_IN_METHODDEF _OPERATOR_CONTAINS_METHODDEF _OPERATOR_INDEXOF_METHODDEF _OPERATOR_COUNTOF_METHODDEF diff --git a/Modules/clinic/_operator.c.h b/Modules/clinic/_operator.c.h index 08615d690922a1c..2351fe330a77253 100644 --- a/Modules/clinic/_operator.c.h +++ b/Modules/clinic/_operator.c.h @@ -886,6 +886,76 @@ _operator_iconcat(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(_operator_in___doc__, +"in_($module, a, b, /)\n" +"--\n" +"\n" +"Same as a in b."); + +#define _OPERATOR_IN__METHODDEF \ + {"in_", _PyCFunction_CAST(_operator_in_), METH_FASTCALL, _operator_in___doc__}, + +static int +_operator_in__impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +_operator_in_(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + int _return_value; + + if (!_PyArg_CheckPositional("in_", nargs, 2, 2)) { + goto exit; + } + a = args[0]; + b = args[1]; + _return_value = _operator_in__impl(module, a, b); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(_operator_not_in__doc__, +"not_in($module, a, b, /)\n" +"--\n" +"\n" +"Same as a not in b."); + +#define _OPERATOR_NOT_IN_METHODDEF \ + {"not_in", _PyCFunction_CAST(_operator_not_in), METH_FASTCALL, _operator_not_in__doc__}, + +static int +_operator_not_in_impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +_operator_not_in(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + int _return_value; + + if (!_PyArg_CheckPositional("not_in", nargs, 2, 2)) { + goto exit; + } + a = args[0]; + b = args[1]; + _return_value = _operator_not_in_impl(module, a, b); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + PyDoc_STRVAR(_operator_contains__doc__, "contains($module, a, b, /)\n" "--\n" @@ -1489,4 +1559,4 @@ _operator__compare_digest(PyObject *module, PyObject *const *args, Py_ssize_t na exit: return return_value; } -/*[clinic end generated code: output=ddbba2cd943571eb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c240b42c5e1a2d8f input=a9049054013a1b77]*/ From 5a045dc1840e03260d68fd0170a73d5d756116fe Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 01:12:20 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst diff --git a/Misc/NEWS.d/next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst b/Misc/NEWS.d/next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst new file mode 100644 index 000000000000000..48cd39da12e60de --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst @@ -0,0 +1 @@ +:func:`operator.in_` and :func:`operator.not_in` added to ``operator`` module. From 2c0d4f93f8862087003d324ea885fbc6c03dd06a Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Wed, 12 Jun 2024 04:42:58 +0300 Subject: [PATCH 3/4] versionadded docs --- Doc/library/operator.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index e43fcc515345851..bd79039ccb22498 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -212,11 +212,15 @@ Operations which work with sequences (some of them with mappings too) include: Return the outcome of the test ``a in b``. + .. versionadded:: 3.14 + .. function:: not_in(a, b) Return the outcome of the test ``a not in b``. + .. versionadded:: 3.14 + .. function:: contains(a, b) __contains__(a, b) From eb342ea786228da29ee55e7eb8cb18ba505ff964 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Wed, 12 Jun 2024 19:26:45 +0300 Subject: [PATCH 4/4] review suggestions --- Lib/operator.py | 11 +++-------- .../2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst | 2 +- Modules/_operator.c | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Lib/operator.py b/Lib/operator.py index 7c36d650f430f5e..2514fc9e4d3a68a 100644 --- a/Lib/operator.py +++ b/Lib/operator.py @@ -10,14 +10,13 @@ This is the pure Python implementation of the module. """ -__all__ = ['abs', 'add', 'and_', 'attrgetter', 'call', 'concat', - 'in_', 'not_in', 'contains', 'countOf', +__all__ = ['abs', 'add', 'and_', 'attrgetter', 'call', 'concat', 'contains', 'countOf', 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand', - 'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul', + 'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul', 'in_', 'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_', 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le', 'length_hint', 'lshift', 'lt', 'matmul', 'methodcaller', 'mod', - 'mul', 'ne', 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', + 'mul', 'ne', 'neg', 'not_', 'not_in', 'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub', 'truediv', 'truth', 'xor'] from builtins import abs as _abs @@ -151,22 +150,18 @@ def concat(a, b): raise TypeError(msg) return a + b - def in_(a, b): "Same as a in b." return a in b - def not_in(a, b): "Same as a not in b." return a not in b - def contains(a, b): "Same as b in a (note reversed operands)." return b in a - def countOf(a, b): "Return the number of items in a which are, or which equal, b." count = 0 diff --git a/Misc/NEWS.d/next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst b/Misc/NEWS.d/next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst index 48cd39da12e60de..4f795b9804a6bac 100644 --- a/Misc/NEWS.d/next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst +++ b/Misc/NEWS.d/next/Library/2024-06-12-01-12-19.gh-issue-120376.R0lyvC.rst @@ -1 +1 @@ -:func:`operator.in_` and :func:`operator.not_in` added to ``operator`` module. +Add :func:`operator.in_` and :func:`operator.not_in` to the :mod:`operator` module. diff --git a/Modules/_operator.c b/Modules/_operator.c index 3e038c7916c7d00..0a342e49eadee47 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -507,7 +507,7 @@ _operator_not_in_impl(PyObject *module, PyObject *a, PyObject *b) { int result = PySequence_Contains(b, a); if (result != -1) { - result = 1 - result; + result = !result; } return result; }