diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2020-07-09 14:14:02 +0200 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2020-07-09 14:14:02 +0200 |
commit | ec99487becc3a4a4f3bbd83be0bc31f027c8e11e (patch) | |
tree | 944c53ec7f42d6619c8ad4196bf13c7e11db2f86 | |
parent | 85d9738e05b9822d13c15740c91ac1eeaa73647e (diff) | |
download | cython-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.py | 22 | ||||
-rw-r--r-- | docs/src/userguide/special_methods.rst | 41 |
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: |