diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2017-10-13 23:36:13 +0200 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2017-10-13 23:37:18 +0200 |
commit | 628ffd74faccb7dc783666c4b5135f3e008638e8 (patch) | |
tree | 2f6ff714c984527b243e0867a7cc645cecfb7318 | |
parent | 4c461c93ba8539cb71ab279e4c2ca82436fb07b3 (diff) | |
download | cython-628ffd74faccb7dc783666c4b5135f3e008638e8.tar.gz |
Fix the first argument special method signatures of "__eq__", "__lt__" and the other richcmp methods to be of "self" type, in accordance with to the CPython specs.
See https://docs.python.org/3/reference/datamodel.html#emulating-container-types
Closes #1935.
-rw-r--r-- | Cython/Compiler/TypeSlots.py | 2 | ||||
-rw-r--r-- | docs/src/userguide/special_methods.rst | 30 | ||||
-rw-r--r-- | tests/run/ext_auto_richcmp.py | 171 |
3 files changed, 161 insertions, 42 deletions
diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index afafb53d8..ae0257ff0 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -576,7 +576,7 @@ def get_special_method_signature(name): if slot: return slot.signature elif name in richcmp_special_methods: - return binaryfunc + return ibinaryfunc else: return None diff --git a/docs/src/userguide/special_methods.rst b/docs/src/userguide/special_methods.rst index 84c5950c3..3882cb84e 100644 --- a/docs/src/userguide/special_methods.rst +++ b/docs/src/userguide/special_methods.rst @@ -209,21 +209,21 @@ Rich comparison operators https://docs.python.org/3/reference/datamodel.html#basic-customization -+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ -| __richcmp__ |x, y, int op | object | Rich comparison (no direct Python equivalent) | -+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ -| __eq__ |x, y | object | x == y | -+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ -| __ne__ |x, y | object | x != y (falls back to ``__eq__`` if not available) | -+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ -| __lt__ |x, y | object | x < y | -+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ -| __gt__ |x, y | object | x > y | -+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ -| __le__ |x, y | object | x <= y | -+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ -| __ge__ |x, y | object | x >= y | -+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ ++-----------------------+---------------------------------------+-------------+--------------------------------------------------------+ +| __richcmp__ |x, y, int op | object | Rich comparison (no direct Python equivalent) | ++-----------------------+---------------------------------------+-------------+--------------------------------------------------------+ +| __eq__ |self, y | object | self == y | ++-----------------------+---------------------------------------+-------------+--------------------------------------------------------+ +| __ne__ |self, y | object | self != y (falls back to ``__eq__`` if not available) | ++-----------------------+---------------------------------------+-------------+--------------------------------------------------------+ +| __lt__ |self, y | object | self < y | ++-----------------------+---------------------------------------+-------------+--------------------------------------------------------+ +| __gt__ |self, y | object | self > y | ++-----------------------+---------------------------------------+-------------+--------------------------------------------------------+ +| __le__ |self, y | object | self <= y | ++-----------------------+---------------------------------------+-------------+--------------------------------------------------------+ +| __ge__ |self, y | object | self >= y | ++-----------------------+---------------------------------------+-------------+--------------------------------------------------------+ Arithmetic operators ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/run/ext_auto_richcmp.py b/tests/run/ext_auto_richcmp.py index ef42856f9..3d8f87659 100644 --- a/tests/run/ext_auto_richcmp.py +++ b/tests/run/ext_auto_richcmp.py @@ -9,7 +9,7 @@ IS_PY2 = sys.version_info[0] == 2 @cython.cclass class X(object): - x = cython.declare(cython.int, visibility="public") + x = cython.declare(cython.int) def __init__(self, x): self.x = x @@ -18,6 +18,12 @@ class X(object): return "<%d>" % self.x +@cython.cfunc +@cython.locals(x=X) +def x_of(x): + return x.x + + @cython.cclass class ClassEq(X): """ @@ -74,9 +80,12 @@ class ClassEq(X): TypeError... """ def __eq__(self, other): - if isinstance(self, X): - if isinstance(other, X): - return self.x == other.x + assert 1 <= self.x <= 2 + assert isinstance(self, ClassEq), type(self) + if isinstance(other, X): + return self.x == x_of(other) + elif isinstance(other, int): + return self.x < other return NotImplemented @@ -134,9 +143,12 @@ class ClassEqNe(ClassEq): TypeError... """ def __ne__(self, other): - if isinstance(self, X): - if isinstance(other, X): - return self.x != other.x + assert 1 <= self.x <= 2 + assert isinstance(self, ClassEqNe), type(self) + if isinstance(other, X): + return self.x != x_of(other) + elif isinstance(other, int): + return self.x < other return NotImplemented @@ -208,11 +220,34 @@ class ClassEqNeGe(ClassEqNe): ... else: a > b Traceback (most recent call last): TypeError... - """ + + >>> 2 <= a + False + >>> a >= 2 + False + >>> 1 <= a + True + >>> a >= 1 + True + >>> a >= 2 + False + + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: 'x' <= a + Traceback (most recent call last): + TypeError... + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: a >= 'x' + Traceback (most recent call last): + TypeError... + """ def __ge__(self, other): - if isinstance(self, X): - if isinstance(other, X): - return self.x >= other.x + assert 1 <= self.x <= 2 + assert isinstance(self, ClassEqNeGe), type(self) + if isinstance(other, X): + return self.x >= x_of(other) + elif isinstance(other, int): + return self.x >= other return NotImplemented @@ -274,11 +309,34 @@ class ClassLe(X): True >>> b >= c True + + >>> 2 >= a + True + >>> a <= 2 + True + >>> 1 >= a + True + >>> a <= 1 + True + >>> a <= 0 + False + + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: 'x' >= a + Traceback (most recent call last): + TypeError... + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: a <= 'x' + Traceback (most recent call last): + TypeError... """ def __le__(self, other): - if isinstance(self, X): - if isinstance(other, X): - return self.x <= other.x + assert 1 <= self.x <= 2 + assert isinstance(self, ClassLe), type(self) + if isinstance(other, X): + return self.x <= x_of(other) + elif isinstance(other, int): + return self.x <= other return NotImplemented @@ -320,11 +378,37 @@ class ClassLt(X): [<1>, <1>, <2>] >>> sorted([b, a, c]) [<1>, <1>, <2>] + + >>> 2 > a + True + >>> a < 2 + True + >>> 1 > a + False + >>> a < 1 + False + + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: 1 < a + Traceback (most recent call last): + TypeError... + + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: 'x' > a + Traceback (most recent call last): + TypeError... + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: a < 'x' + Traceback (most recent call last): + TypeError... """ def __lt__(self, other): - if isinstance(self, X): - if isinstance(other, X): - return self.x < other.x + assert 1 <= self.x <= 2 + assert isinstance(self, ClassLt), type(self) + if isinstance(other, X): + return self.x < x_of(other) + elif isinstance(other, int): + return self.x < other return NotImplemented @@ -368,9 +452,12 @@ class ClassLtGtInherited(X): [<1>, <1>, <2>] """ def __gt__(self, other): - if isinstance(self, X): - if isinstance(other, X): - return self.x > other.x + assert 1 <= self.x <= 2 + assert isinstance(self, ClassLtGtInherited), type(self) + if isinstance(other, X): + return self.x > x_of(other) + elif isinstance(other, int): + return self.x > other return NotImplemented @@ -412,17 +499,49 @@ class ClassLtGt(X): [<1>, <1>, <2>] >>> sorted([b, a, c]) [<1>, <1>, <2>] + + >>> 2 > a + True + >>> 2 < a + False + >>> a < 2 + True + >>> a > 2 + False + + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: 'x' > a + Traceback (most recent call last): + TypeError... + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: 'x' < a + Traceback (most recent call last): + TypeError... + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: a < 'x' + Traceback (most recent call last): + TypeError... + >>> if IS_PY2: raise TypeError # doctest: +ELLIPSIS + ... else: a > 'x' + Traceback (most recent call last): + TypeError... """ def __lt__(self, other): - if isinstance(self, X): - if isinstance(other, X): - return self.x < other.x + assert 1 <= self.x <= 2 + assert isinstance(self, ClassLtGt), type(self) + if isinstance(other, X): + return self.x < x_of(other) + elif isinstance(other, int): + return self.x < other return NotImplemented def __gt__(self, other): - if isinstance(self, X): - if isinstance(other, X): - return self.x > other.x + assert 1 <= self.x <= 2 + assert isinstance(self, ClassLtGt), type(self) + if isinstance(other, X): + return self.x > x_of(other) + elif isinstance(other, int): + return self.x > other return NotImplemented |