diff --git a/python/style.rst b/python/style.rst index 1fe8556e..eeb729ee 100644 --- a/python/style.rst +++ b/python/style.rst @@ -746,6 +746,8 @@ For example: Using `super` ensures a consistent Method Resolution Order, and prevents inherited methods from being called multiple times. In Python 3, `super` does not require naming the class that it is part of, making its use simpler and removing a maintenance issue. +.. _style-guide-py-super-multiple-inheritance: + super() and Multiple Inheritance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -762,6 +764,30 @@ If your class may be used in multiple inheritance, ensure that all relevant clas For more details, see the `super documentation `, the `astropy coding guide `__, and `this article from Raymond Hettinger `__. +.. _style-guide-py-abstract-builtins: + +Abstract subclasses of builtins +------------------------------- + +Abstract subclasses of builtins (classes that include both `abc.ABC` and a :ref:`builtin class ` in their hierarchy) are not checked for abstract methods on instantiation. +This means that `abc.abstractmethod` decorators will not prevent the class from being instantiated. +For example, an abstract :ref:`Exception ` subclass can still be instantiated without a concrete override of its abstract method(s), resulting in runtime errors when those methods are called. +You can work around this by defining ``__new__`` on an abstract builtin subclass to check for abstract methods: + +.. code-block:: python + :name: abstract-builtin + + def __new__(cls, *args: Any, **kwargs: Any) -> MyAbstractClass: + # Have to override __new__ because builtin subclasses aren't checked + # for abstract methods; see https://github.com/python/cpython/issues/50246 + if cls.__abstractmethods__: + raise TypeError( + f"Can't instantiate abstract class {cls.__name__} with " + f"abstract methods: {','.join(sorted(cls.__abstractmethods__))}" + ) + return super().__new__(cls, *args, **kwargs) + + .. _style-guide-py-comparisons: 9. Comparisons