Metadata-Version: 2.1
Name: abstracts
Version: 0.0.5
Summary: Abstract class and interface definitions
Home-page: https://github.com/phlax/pytooling/abstracts
Author: Ryan Northey
Author-email: ryan@synca.io
Maintainer: Ryan Northey
Maintainer-email: ryan@synca.io
License: Apache Software License 2.0
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Testing
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Operating System :: OS Independent
Classifier: License :: OSI Approved :: Apache Software License
Requires-Python: >=3.5
Provides-Extra: lint
Requires-Dist: flake8 ; extra == 'lint'
Provides-Extra: publish
Requires-Dist: wheel ; extra == 'publish'
Provides-Extra: test
Requires-Dist: pytest ; extra == 'test'
Requires-Dist: pytest-asyncio ; extra == 'test'
Requires-Dist: pytest-coverage ; extra == 'test'
Requires-Dist: pytest-patches ; extra == 'test'
Provides-Extra: types
Requires-Dist: mypy ; extra == 'types'


abstracts
=========

Abstract class and interface definitions.

Create an ``abstract.Abstraction``
----------------------------------

An ``Abstraction`` is a ``metaclass`` for defining abstract classes.

Let's define an abstract ``AFoo`` class and give it an abstract ``do_foo``
method.

Like any python class, an ``Abstraction`` can have any name, but it may
be helpful to distinguish abstract classes from others by prefixing their
name with ``A``.

.. code-block:: pycon

   >>> import abc
   >>> import abstracts

   >>> class AFoo(metaclass=abstracts.Abstraction):
   ...
   ...     @abc.abstractmethod
   ...     def do_foo(self):
   ...         raise NotImplementedError

Abstract classes **cannot** be instantiated directly.

.. code-block:: pycon

   >>> AFoo()
   Traceback (most recent call last):
   ...
   TypeError: Can't instantiate abstract class AFoo with abstract method... do_foo

Create an ``implementer`` for an ``abstract.Abstraction``
---------------------------------------------------------

In order to make use of ``AFoo``, we need to create an implementer for it.

.. code-block:: pycon

   >>> @abstracts.implementer(AFoo)
   ... class Foo:
   ...     pass

The implementer **must** implement all of the abstract methods,
defined by its abstract classes.

.. code-block:: pycon

   >>> Foo()
   Traceback (most recent call last):
   ...
   TypeError: Can't instantiate abstract class Foo with abstract method... do_foo

   >>> @abstracts.implementer(AFoo)
   ... class Foo2:
   ...
   ...     def do_foo(self):
   ...         return "DID FOO"

   >>> Foo2()
   <__main__.Foo2 object at ...>

An implementer inherits from its ``Abstractions``
-------------------------------------------------

An ``implementer`` class is a subclass of its ``Abstraction``.

.. code-block:: pycon

   >>> issubclass(Foo2, AFoo)
   True

Likewise an instance of an implementer is an instance of its ``Abstraction``

.. code-block:: pycon

   >>> isinstance(Foo2(), AFoo)
   True

The ``Abstraction`` class can be seen in the class ``bases``, and the
methods of the ``Abstraction`` can be invoked by the implementer.

.. code-block:: pycon

   >>> import inspect
   >>> AFoo in inspect.getmro(Foo2)
   True

Create an ``implementer`` that implements multiple ``Abstraction`` s.
---------------------------------------------------------------------

An implementer can implement multiple abstractions.

Let's create a second abstraction.

.. code-block:: pycon

   >>> class ABar(metaclass=abstracts.Abstraction):
   ...
   ...     @abc.abstractmethod
   ...     def do_bar(self):
   ...         raise NotImplementedError

And now we can create an implementer that implememts both the ``AFoo`` and ``ABar``
``Abstraction`` s.

.. code-block:: pycon

   >>> @abstracts.implementer((AFoo, ABar))
   ... class FooBar:
   ...
   ...     def do_foo(self):
   ...         return "DID FOO"
   ...
   ...     def do_bar(self):
   ...         return "DID BAR"

   >>> FooBar()
   <__main__.FooBar object at ...>

Defining abstract properties
----------------------------

Properties can be defined in an abstract class, and just like with normal
methods, they must be implemented by any implementers.

.. code-block:: pycon

   >>> class AMover(metaclass=abstracts.Abstraction):
   ...
   ...     @property
   ...     @abc.abstractmethod
   ...     def speed(self):
   ...         return 5
   ...
   ...     @property
   ...     @abc.abstractmethod
   ...     def direction(self):
   ...         return "forwards"


