summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2017-10-13 23:36:13 +0200
committerStefan Behnel <stefan_ml@behnel.de>2017-10-13 23:37:18 +0200
commit628ffd74faccb7dc783666c4b5135f3e008638e8 (patch)
tree2f6ff714c984527b243e0867a7cc645cecfb7318
parent4c461c93ba8539cb71ab279e4c2ca82436fb07b3 (diff)
downloadcython-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.py2
-rw-r--r--docs/src/userguide/special_methods.rst30
-rw-r--r--tests/run/ext_auto_richcmp.py171
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