Commit 8a46a866 authored by Stefan Behnel's avatar Stefan Behnel

Merge branch '0.27.x'

parents f85ddd19 924599d6
...@@ -11,6 +11,9 @@ Bugs fixed ...@@ -11,6 +11,9 @@ Bugs fixed
* Comprehensions could incorrectly be optimised away when they appeared in boolean * Comprehensions could incorrectly be optimised away when they appeared in boolean
test contexts. (Github issue #1920) test contexts. (Github issue #1920)
* The special methods ``__eq__``, ``__lt__`` etc. in extension types did not type
their first argument as the type of the class but ``object``. (Github issue #1935)
* Crash on first lookup of "cline_in_traceback" option during exception handling. * Crash on first lookup of "cline_in_traceback" option during exception handling.
(Github issue #1907) (Github issue #1907)
......
...@@ -576,7 +576,7 @@ def get_special_method_signature(name): ...@@ -576,7 +576,7 @@ def get_special_method_signature(name):
if slot: if slot:
return slot.signature return slot.signature
elif name in richcmp_special_methods: elif name in richcmp_special_methods:
return binaryfunc return ibinaryfunc
else: else:
return None return None
......
...@@ -276,8 +276,10 @@ Arithmetic Methods ...@@ -276,8 +276,10 @@ Arithmetic Methods
Rich Comparisons Rich Comparisons
================ ================
* Starting with Cython 0.27, the Python special methods ``__eq__``, ``__lt__``, etc. can be implemented. * Starting with Cython 0.27, the Python
In previous versions, ``__richcmp__`` was the only way to implement rich comparisons. `special methods <https://docs.python.org/3/reference/datamodel.html#basic-customization>`_
``__eq__``, ``__lt__``, etc. can be implemented. In previous versions, ``__richcmp__`` was
the only way to implement rich comparisons.
* A single special method called ``__richcmp__()`` can be used to implement all the individual * A single special method called ``__richcmp__()`` can be used to implement all the individual
rich compare, special method types. rich compare, special method types.
* ``__richcmp__()`` takes an integer argument, indicating which operation is to be performed * ``__richcmp__()`` takes an integer argument, indicating which operation is to be performed
......
...@@ -127,9 +127,11 @@ take `self` as the first argument. ...@@ -127,9 +127,11 @@ take `self` as the first argument.
Rich comparisons Rich comparisons
----------------- -----------------
Starting with Cython 0.27, the Python special methods :meth:``__eq__``, :meth:``__lt__``, etc. Starting with Cython 0.27, the Python
can be implemented. In previous versions, :meth:``__richcmp__`` was the only way to implement `special methods <https://docs.python.org/3/reference/datamodel.html#basic-customization>`_
rich comparisons. It takes an integer indicating which operation is to be performed, as follows: :meth:``__eq__``, :meth:``__lt__``, etc. can be implemented. In previous versions,
:meth:``__richcmp__`` was the only way to implement rich comparisons. It takes an integer
indicating which operation is to be performed, as follows:
+-----+-----+-------+ +-----+-----+-------+
| < | 0 | Py_LT | | < | 0 | Py_LT |
...@@ -170,6 +172,8 @@ declare different types, conversions will be performed as necessary. ...@@ -170,6 +172,8 @@ declare different types, conversions will be performed as necessary.
General General
^^^^^^^ ^^^^^^^
https://docs.python.org/3/reference/datamodel.html#special-method-names
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+-----------------------------------------------------+
| Name | Parameters | Return type | Description | | Name | Parameters | Return type | Description |
+=======================+=======================================+=============+=====================================================+ +=======================+=======================================+=============+=====================================================+
...@@ -203,25 +207,29 @@ General ...@@ -203,25 +207,29 @@ General
Rich comparison operators 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 | | __richcmp__ |x, y, int op | object | Rich comparison (no direct Python equivalent) |
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+--------------------------------------------------------+
| __ne__ |x, y | object | x != y (falls back to ``__eq__`` if not available) | | __eq__ |self, y | object | self == y |
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+--------------------------------------------------------+
| __lt__ |x, y | object | x < y | | __ne__ |self, y | object | self != y (falls back to ``__eq__`` if not available) |
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+--------------------------------------------------------+
| __gt__ |x, y | object | x > y | | __lt__ |self, y | object | self < y |
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+--------------------------------------------------------+
| __le__ |x, y | object | x <= y | | __gt__ |self, y | object | self > y |
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+--------------------------------------------------------+
| __ge__ |x, y | object | x >= y | | __le__ |self, y | object | self <= y |
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+--------------------------------------------------------+
| __ge__ |self, y | object | self >= y |
+-----------------------+---------------------------------------+-------------+--------------------------------------------------------+
Arithmetic operators Arithmetic operators
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+-----------------------------------------------------+
| Name | Parameters | Return type | Description | | Name | Parameters | Return type | Description |
+=======================+=======================================+=============+=====================================================+ +=======================+=======================================+=============+=====================================================+
...@@ -267,6 +275,8 @@ Arithmetic operators ...@@ -267,6 +275,8 @@ Arithmetic operators
Numeric conversions Numeric conversions
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+-----------------------------------------------------+
| Name | Parameters | Return type | Description | | Name | Parameters | Return type | Description |
+=======================+=======================================+=============+=====================================================+ +=======================+=======================================+=============+=====================================================+
...@@ -286,6 +296,8 @@ Numeric conversions ...@@ -286,6 +296,8 @@ Numeric conversions
In-place arithmetic operators In-place arithmetic operators
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+-----------------------------------------------------+
| Name | Parameters | Return type | Description | | Name | Parameters | Return type | Description |
+=======================+=======================================+=============+=====================================================+ +=======================+=======================================+=============+=====================================================+
...@@ -319,6 +331,8 @@ In-place arithmetic operators ...@@ -319,6 +331,8 @@ In-place arithmetic operators
Sequences and mappings Sequences and mappings
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
https://docs.python.org/3/reference/datamodel.html#emulating-container-types
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+-----------------------------------------------------+
| Name | Parameters | Return type | Description | | Name | Parameters | Return type | Description |
+=======================+=======================================+=============+=====================================================+ +=======================+=======================================+=============+=====================================================+
...@@ -342,6 +356,8 @@ Sequences and mappings ...@@ -342,6 +356,8 @@ Sequences and mappings
Iterators Iterators
^^^^^^^^^ ^^^^^^^^^
https://docs.python.org/3/reference/datamodel.html#emulating-container-types
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+-----------------------------------------------------+
| Name | Parameters | Return type | Description | | Name | Parameters | Return type | Description |
+=======================+=======================================+=============+=====================================================+ +=======================+=======================================+=============+=====================================================+
...@@ -377,6 +393,8 @@ Buffer interface [legacy] (no Python equivalents - see note 1) ...@@ -377,6 +393,8 @@ Buffer interface [legacy] (no Python equivalents - see note 1)
Descriptor objects (see note 2) Descriptor objects (see note 2)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
https://docs.python.org/3/reference/datamodel.html#emulating-container-types
+-----------------------+---------------------------------------+-------------+-----------------------------------------------------+ +-----------------------+---------------------------------------+-------------+-----------------------------------------------------+
| Name | Parameters | Return type | Description | | Name | Parameters | Return type | Description |
+=======================+=======================================+=============+=====================================================+ +=======================+=======================================+=============+=====================================================+
......
...@@ -9,7 +9,7 @@ IS_PY2 = sys.version_info[0] == 2 ...@@ -9,7 +9,7 @@ IS_PY2 = sys.version_info[0] == 2
@cython.cclass @cython.cclass
class X(object): class X(object):
x = cython.declare(cython.int, visibility="public") x = cython.declare(cython.int)
def __init__(self, x): def __init__(self, x):
self.x = x self.x = x
...@@ -18,6 +18,12 @@ class X(object): ...@@ -18,6 +18,12 @@ class X(object):
return "<%d>" % self.x return "<%d>" % self.x
@cython.cfunc
@cython.locals(x=X)
def x_of(x):
return x.x
@cython.cclass @cython.cclass
class ClassEq(X): class ClassEq(X):
""" """
...@@ -74,9 +80,12 @@ class ClassEq(X): ...@@ -74,9 +80,12 @@ class ClassEq(X):
TypeError... TypeError...
""" """
def __eq__(self, other): def __eq__(self, other):
if isinstance(self, X): assert 1 <= self.x <= 2
if isinstance(other, X): assert isinstance(self, ClassEq), type(self)
return self.x == other.x if isinstance(other, X):
return self.x == x_of(other)
elif isinstance(other, int):
return self.x < other
return NotImplemented return NotImplemented
...@@ -134,9 +143,12 @@ class ClassEqNe(ClassEq): ...@@ -134,9 +143,12 @@ class ClassEqNe(ClassEq):
TypeError... TypeError...
""" """
def __ne__(self, other): def __ne__(self, other):
if isinstance(self, X): assert 1 <= self.x <= 2
if isinstance(other, X): assert isinstance(self, ClassEqNe), type(self)
return self.x != other.x if isinstance(other, X):
return self.x != x_of(other)
elif isinstance(other, int):
return self.x < other
return NotImplemented return NotImplemented
...@@ -208,11 +220,34 @@ class ClassEqNeGe(ClassEqNe): ...@@ -208,11 +220,34 @@ class ClassEqNeGe(ClassEqNe):
... else: a > b ... else: a > b
Traceback (most recent call last): Traceback (most recent call last):
TypeError... 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): def __ge__(self, other):
if isinstance(self, X): assert 1 <= self.x <= 2
if isinstance(other, X): assert isinstance(self, ClassEqNeGe), type(self)
return self.x >= other.x if isinstance(other, X):
return self.x >= x_of(other)
elif isinstance(other, int):
return self.x >= other
return NotImplemented return NotImplemented
...@@ -274,11 +309,34 @@ class ClassLe(X): ...@@ -274,11 +309,34 @@ class ClassLe(X):
True True
>>> b >= c >>> b >= c
True 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): def __le__(self, other):
if isinstance(self, X): assert 1 <= self.x <= 2
if isinstance(other, X): assert isinstance(self, ClassLe), type(self)
return self.x <= other.x if isinstance(other, X):
return self.x <= x_of(other)
elif isinstance(other, int):
return self.x <= other
return NotImplemented return NotImplemented
...@@ -320,11 +378,37 @@ class ClassLt(X): ...@@ -320,11 +378,37 @@ class ClassLt(X):
[<1>, <1>, <2>] [<1>, <1>, <2>]
>>> sorted([b, a, c]) >>> sorted([b, a, c])
[<1>, <1>, <2>] [<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): def __lt__(self, other):
if isinstance(self, X): assert 1 <= self.x <= 2
if isinstance(other, X): assert isinstance(self, ClassLt), type(self)
return self.x < other.x if isinstance(other, X):
return self.x < x_of(other)
elif isinstance(other, int):
return self.x < other
return NotImplemented return NotImplemented
...@@ -368,9 +452,12 @@ class ClassLtGtInherited(X): ...@@ -368,9 +452,12 @@ class ClassLtGtInherited(X):
[<1>, <1>, <2>] [<1>, <1>, <2>]
""" """
def __gt__(self, other): def __gt__(self, other):
if isinstance(self, X): assert 1 <= self.x <= 2
if isinstance(other, X): assert isinstance(self, ClassLtGtInherited), type(self)
return self.x > other.x if isinstance(other, X):
return self.x > x_of(other)
elif isinstance(other, int):
return self.x > other
return NotImplemented return NotImplemented
...@@ -412,17 +499,49 @@ class ClassLtGt(X): ...@@ -412,17 +499,49 @@ class ClassLtGt(X):
[<1>, <1>, <2>] [<1>, <1>, <2>]
>>> sorted([b, a, c]) >>> sorted([b, a, c])
[<1>, <1>, <2>] [<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): def __lt__(self, other):
if isinstance(self, X): assert 1 <= self.x <= 2
if isinstance(other, X): assert isinstance(self, ClassLtGt), type(self)
return self.x < other.x if isinstance(other, X):
return self.x < x_of(other)
elif isinstance(other, int):
return self.x < other
return NotImplemented return NotImplemented
def __gt__(self, other): def __gt__(self, other):
if isinstance(self, X): assert 1 <= self.x <= 2
if isinstance(other, X): assert isinstance(self, ClassLtGt), type(self)
return self.x > other.x if isinstance(other, X):
return self.x > x_of(other)
elif isinstance(other, int):
return self.x > other
return NotImplemented return NotImplemented
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment