Metaclass for inheriting comparison functions#

This module defines a metaclass InheritComparisonMetaclass to inherit comparison functions in Cython extension types. In Python 2, the special methods __richcmp__, __cmp__ and __hash__ are only inherited as a whole: defining just 1 or 2 of these will prevent the others from being inherited.

To solve this issue, you can use InheritComparisonMetaclass as a Cython “metaclass” (see sage.cpython.cython_metaclass for the general mechanism). If you do this for an extension type which defines neither __richcmp__ nor __cmp__, then both these methods are inherited from the base class (the MRO is not used).

In Sage, this is in particular used for sage.structure.element.Element to support comparisons using the coercion framework.

None of this is relevant to Python classes, which inherit comparison methods anyway.

AUTHOR:

  • Jeroen Demeyer (2015-05-22): initial version, see Issue #18329

class sage.misc.inherit_comparison.InheritComparisonClasscallMetaclass[source]#

Bases: ClasscallMetaclass, InheritComparisonMetaclass

Combine ClasscallMetaclass with InheritComparisonMetaclass.

class sage.misc.inherit_comparison.InheritComparisonMetaclass[source]#

Bases: type

If the type does not define __richcmp__ nor __cmp__, inherit both these methods from the base class. The difference with plain extension types is that comparison is inherited even if __hash__ is defined.

EXAMPLES:

sage: # needs sage.misc.cython
sage: cython(
....: '''
....: cimport cython
....:
....: from sage.misc.inherit_comparison cimport InheritComparisonMetaclass
....:
....: cdef class Base():
....:     def __richcmp__(left, right, int op):
....:         print("Calling Base.__richcmp__")
....:         return left is right
....:
....: cdef class Derived(Base):
....:     def __hash__(self):
....:         return 1
....:
....: cdef class DerivedWithRichcmp(Base):
....:     @cython.always_allow_keywords(False)
....:     def __getmetaclass__(_):
....:         from sage.misc.inherit_comparison import InheritComparisonMetaclass
....:         return InheritComparisonMetaclass
....:     def __hash__(self):
....:         return 1
....: ''')
sage: a = Derived()
sage: a == a
True
sage: b = DerivedWithRichcmp()
sage: b == b
Calling Base.__richcmp__
True
>>> from sage.all import *
>>> # needs sage.misc.cython
>>> cython(
... '''
... cimport cython
....:
>>> from sage.misc.inherit_comparison cimport InheritComparisonMetaclass
....:
>>> cdef class Base():
...     def __richcmp__(left, right, int op):
...         print("Calling Base.__richcmp__")
...         return left is right
....:
>>> cdef class Derived(Base):
...     def __hash__(self):
...         return Integer(1)
....:
>>> cdef class DerivedWithRichcmp(Base):
...     @cython.always_allow_keywords(False)
...     def __getmetaclass__(_):
...         from sage.misc.inherit_comparison import InheritComparisonMetaclass
...         return InheritComparisonMetaclass
...     def __hash__(self):
...         return Integer(1)
... ''')
>>> a = Derived()
>>> a == a
True
>>> b = DerivedWithRichcmp()
>>> b == b
Calling Base.__richcmp__
True