summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2020-07-09 14:14:02 +0200
committerStefan Behnel <stefan_ml@behnel.de>2020-07-09 14:14:02 +0200
commitec99487becc3a4a4f3bbd83be0bc31f027c8e11e (patch)
tree944c53ec7f42d6619c8ad4196bf13c7e11db2f86
parent85d9738e05b9822d13c15740c91ac1eeaa73647e (diff)
downloadcython-ec99487becc3a4a4f3bbd83be0bc31f027c8e11e.tar.gz
Update the documentation on the arithmetic special methods and issue a "backwards compatibility" warning when the reversed method is not implemented.
See https://github.com/cython/cython/issues/2056
-rw-r--r--Cython/Compiler/ModuleNode.py22
-rw-r--r--docs/src/userguide/special_methods.rst41
2 files changed, 43 insertions, 20 deletions
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index bd4e5ca49..64265e9af 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -2078,15 +2078,17 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
extra_arg_decl = ', PyObject* extra_arg'
else:
error(pos, "Unexpected type slot signature: %s" % slot)
+ return
- def has_slot_method(method_name):
+ def get_slot_method_cname(method_name):
entry = scope.lookup(method_name)
- return bool(entry and entry.is_special and entry.func_cname)
+ return entry.func_cname if entry and entry.is_special else None
+
def call_slot_method(method_name, reverse):
- entry = scope.lookup(method_name)
- if entry and entry.is_special and entry.func_cname:
+ func_cname = get_slot_method_cname(method_name)
+ if func_cname:
return "%s(%s%s)" % (
- entry.func_cname,
+ func_cname,
"right, left" if reverse else "left, right",
extra_arg)
else:
@@ -2095,13 +2097,21 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
'Py_TYPE(right)->tp_base' if reverse else 'Py_TYPE(left)->tp_base',
extra_arg)
+ if get_slot_method_cname(slot.left_slot.method_name) and not get_slot_method_cname(slot.right_slot.method_name):
+ warning(pos, "Extension type implements %s() but not %s(). "
+ "The behaviour has changed from previous Cython versions to match Python semantics. "
+ "You can implement both special methods in a backwards compatible way." % (
+ slot.left_slot.method_name,
+ slot.right_slot.method_name,
+ ))
+
code.putln(
TempitaUtilityCode.load_as_string(
"BinopSlot", "ExtensionTypes.c",
context={
"func_name": func_name,
"slot_name": slot.slot_name,
- "overloads_left": int(has_slot_method(slot.left_slot.method_name)),
+ "overloads_left": int(bool(get_slot_method_cname(slot.left_slot.method_name))),
"call_left": call_slot_method(slot.left_slot.method_name, reverse=False),
"call_right": call_slot_method(slot.right_slot.method_name, reverse=True),
"type_cname": scope.parent_type.typeptr_cname,
diff --git a/docs/src/userguide/special_methods.rst b/docs/src/userguide/special_methods.rst
index bde0b61b4..c3cad40a2 100644
--- a/docs/src/userguide/special_methods.rst
+++ b/docs/src/userguide/special_methods.rst
@@ -129,20 +129,33 @@ executed unless they are explicitly called by the subclass.
Arithmetic methods
-------------------
-Arithmetic operator methods, such as :meth:`__add__`, behave differently from their
-Python counterparts. There are no separate "reversed" versions of these
-methods (:meth:`__radd__`, etc.) Instead, if the first operand cannot perform the
-operation, the same method of the second operand is called, with the operands
-in the same order.
-
-This means that you can't rely on the first parameter of these methods being
-"self" or being the right type, and you should test the types of both operands
-before deciding what to do. If you can't handle the combination of types you've
-been given, you should return `NotImplemented`.
-
-This also applies to the in-place arithmetic method :meth:`__ipow__`. It doesn't apply
-to any of the other in-place methods (:meth:`__iadd__`, etc.) which always
-take `self` as the first argument.
+Arithmetic operator methods, such as :meth:`__add__`, used to behave differently
+from their Python counterparts in Cython 0.x, following the low-level semantics
+of the C-API slot functions. Since Cython 3.0, they are called in the same way
+as in Python, including the separate "reversed" versions of these methods
+(:meth:`__radd__`, etc.).
+
+Previously, if the first operand could not perform the operation, the same method
+of the second operand was called, with the operands in the same order.
+This means that you could not rely on the first parameter of these methods being
+"self" or being the right type, and you needed to test the types of both operands
+before deciding what to do.
+
+If backwards compatibility is needed, the normal operator method (``__add__``, etc.)
+can still be implemented to support both variants, applying a type check to the
+arguments. The reversed method (``__radd__``, etc.) can always be implemented
+with ``self`` as first argument and will be ignored by older Cython versions, whereas
+Cython 3.x and later will only call the normal method with the expected argument order,
+and otherwise call the reversed method instead.
+
+If you can't handle the combination of types you've been given, you should return
+`NotImplemented`. This will let Python's operator implementation first try to apply
+the reversed operator to the second operand, and failing that as well, report an
+appropriate error to the user.
+
+This change in behaviour also applies to the in-place arithmetic method :meth:`__ipow__`.
+It does not apply to any of the other in-place methods (:meth:`__iadd__`, etc.)
+which always take `self` as the first argument.
.. _righ_comparisons: