{ "info": { "author": "Kamil Sindi", "author_email": "ksindi@ksindi.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6" ], "description": "Implements\n==========\n\n.. image:: https://travis-ci.org/ksindi/implements.svg?branch=master\n :target: https://travis-ci.org/ksindi/ksindi/implements\n :alt: Build Status\n.. image:: https://readthedocs.org/projects/implements/badge/?version=v0.1.1\n :target: http://implements.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n.. image:: https://img.shields.io/pypi/v/implements.svg\n :target: https://pypi.python.org/pypi/implements\n :alt: PyPI Version\n\n*Pythonic interfaces using decorators*\n\nInstall\n-------\n\nImplements is available on PyPI and can be installed with `pip `_::\n\n pip install implements\n \nNote Python 3.5+ is required as it relies on new features of `inspect` module.\n\nAdvantages\n----------\n\n1. `Favor composition over inheritance `_.\n\n2. Inheriting from multiple classes can be problematic, especially when the superclasses have the same method name but different signatures. Implements will throw a descriptive error if that happens to ensure integrity of contracts.\n\n3. The decorators are evaluated at import time. Any errors will be raised then and not when an object is instantiated or a method is called.\n\n4. It's cleaner. Using decorators makes it clear we want share behavior. Also, arguments are not allowed to be renamed.\n\n5. Codebase is tiny: you can just copy the file over. This repo exists more for test coverage.\n\nUsage\n-----\n\n.. code-block:: python\n\n from implements import Interface, implements\n\n\n class Duck:\n def __init__(self, age):\n self.age = age\n\n\n class Flyable(Interface):\n @staticmethod\n def migrate(direction):\n pass\n\n def fly(self) -> str:\n pass\n\n\n class Quackable(Interface):\n def fly(self) -> bool:\n pass\n\n def quack(self):\n pass\n\n\n @implements(Flyable)\n @implements(Quackable)\n class MallardDuck(Duck):\n def __init__(self, age):\n super(MallardDuck, self).__init__(age)\n\n def migrate(self, dir):\n return True\n\n def fly(self):\n pass\n\n\nThe above would throw the following errors:\n\n.. code-block:: python\n\n NotImplementedError: 'MallardDuck' must implement method 'fly((self) -> bool)' defined in interface 'Quackable'\n NotImplementedError: 'MallardDuck' must implement method 'quack((self))' defined in interface 'Quackable'\n NotImplementedError: 'MallardDuck' must implement method 'migrate((direction))' defined in interface 'Flyable'\n\nYou can find a more detailed example in ``example.py`` and by looking at ``tests.py``.\n\nJustification\n-------------\n\nThere are currently two idiomatic ways to rewrite the above example.\n\nThe first way is to write base classes with mixins raising ``NotImplementedError`` in each method.\n\n.. code-block:: python\n\n class Duck:\n def __init__(self, age):\n self.age = age\n\n\n class Flyable:\n @staticmethod\n def migrate(direction):\n raise NotImplementedError(\"Flyable is an abstract class\")\n\n def fly(self) -> str:\n raise NotImplementedError(\"Flyable is an abstract class\")\n\n\n class Quackable:\n def fly(self) -> bool:\n raise NotImplementedError(\"Quackable is an abstract class\")\n\n def quack(self):\n raise NotImplementedError(\"Quackable is an abstract class\")\n\n\n class MallardDuck(Duck, Quackable, Flyable):\n\n def __init__(self, age):\n super(MallardDuck, self).__init__(age)\n\n def migrate(self, dir):\n return True\n\n def fly(self):\n pass\n\nBut there are a couple drawbacks implementing it this way:\n\n1. We would only get a ``NotImplementedError`` when calling ``quack`` which can happen much later during runtime. Also, raising ``NotImplementedError`` everywhere looks clunky.\n\n2. It's unclear without checking each parent class where super is being called.\n\n3. Similarly the return types of ``fly`` in ``Flyable`` and ``Quackable`` are different. Someone unfamiliar with Python would have to read up on `Method Resolution Order `_.\n\n4. The writer of ``MallardDuck`` made method ``migrate`` an instance method and renamed the argument to ``dir`` which is confusing.\n\n5. We really want to be differentiating between behavior and inheritance.\n\nThe advantage of using implements is it looks cleaner and you would get errors at import time instead of when the method is actually called.\n\nAnother way is to use abstract base classes from the built-in ``abc`` module:\n\n.. code-block:: python\n\n from abc import ABCMeta, abstractmethod, abstractstaticmethod\n\n\n class Duck(metaclass=ABCMeta):\n def __init__(self, age):\n self.age = age\n\n\n class Flyable(metaclass=ABCMeta):\n @abstractstaticmethod\n def migrate(direction):\n pass\n\n @abstractmethod\n def fly(self) -> str:\n pass\n\n\n class Quackable(metaclass=ABCMeta):\n @abstractmethod\n def fly(self) -> bool:\n pass\n\n @abstractmethod\n def quack(self):\n pass\n\n\n class MallardDuck(Duck, Quackable, Flyable):\n def __init__(self, age):\n super(MallardDuck, self).__init__(age)\n\n def migrate(self, dir):\n return True\n\n def fly(self):\n pass\n\n\nUsing abstract base classes has the advantage of throwing an error earlier\non instantiation if a method is not implemented; also, there are static analysis\ntools that warn if two methods have different signatures. But it doesn't solve\nissues 2-4 and implements will throw an error even earlier in import.\nIt also in my opinion doesn't look pythonic.\n\nCredit\n------\n\nImplementation was inspired by a `PR `_ of @elifiner.\n\nTest\n----\n\nRunning unit tests::\n\n make test\n\nRunning linter::\n\n make lint\n\nRunning tox::\n\n make test-all\n\nLicense\n-------\n\nMIT\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://implements.readthedocs.io", "keywords": "implements", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "implements", "package_url": "https://pypi.org/project/implements/", "platform": "", "project_url": "https://pypi.org/project/implements/", "project_urls": { "Homepage": "http://implements.readthedocs.io" }, "release_url": "https://pypi.org/project/implements/0.1.4/", "requires_dist": null, "requires_python": "", "summary": "pythonic interfaces", "version": "0.1.4" }, "last_serial": 5134069, "releases": { "0.1.1": [ { "comment_text": "", "digests": { "md5": "6c1384a9dd1cf719f9267e48c29fc390", "sha256": "7ed560c09a331650bc7e11148840837b55dc1fbe9b676ea21cfd2eec15338faa" }, "downloads": -1, "filename": "implements-0.1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "6c1384a9dd1cf719f9267e48c29fc390", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 5434, "upload_time": "2017-05-28T03:49:15", "url": "https://files.pythonhosted.org/packages/20/bb/08d6d4dd984ed5d7c2b543d142a34c5601150d58266c699513c188224445/implements-0.1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "31111439e575ffe711d9e2d5a1dab826", "sha256": "1361f720d2126c391f32779a05deceb443dfd0a47cf5645beae431ff450f88fe" }, "downloads": -1, "filename": "implements-0.1.1.tar.gz", "has_sig": false, "md5_digest": "31111439e575ffe711d9e2d5a1dab826", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14542, "upload_time": "2017-05-28T03:49:12", "url": "https://files.pythonhosted.org/packages/6c/e0/8fca895c6025b767f954ec373c167f672b8c037fc9cbfec88c6f8f3efd66/implements-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "2ba656a4cb78df0ad86957343b7ab325", "sha256": "df313e0351aa908e9121c822ab7181a4f600275025ec3066685601eadfe34de6" }, "downloads": -1, "filename": "implements-0.1.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "2ba656a4cb78df0ad86957343b7ab325", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 6011, "upload_time": "2017-05-28T14:19:14", "url": "https://files.pythonhosted.org/packages/08/5b/fd345c52a1b0ee6be182363a7bdd8f4a0324c5afff186224d8d320e64a50/implements-0.1.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "760d65085a615902f3526162ad4bd15e", "sha256": "27be6d24fd5949ff4b87d29b55706acb595ed993c6f1fa9054be035575e40cd4" }, "downloads": -1, "filename": "implements-0.1.2.tar.gz", "has_sig": false, "md5_digest": "760d65085a615902f3526162ad4bd15e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14990, "upload_time": "2017-05-28T14:19:12", "url": "https://files.pythonhosted.org/packages/20/67/d4bc30ea5825c85b2688378ab74bf1b5efa33dd51950f010fb572b483736/implements-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "9d168ea163fea51c0f3767ac828e7205", "sha256": "1a177e0a3937ebac563e4692be38f8f7346e339642527644b9515d18e12e0860" }, "downloads": -1, "filename": "implements-0.1.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "9d168ea163fea51c0f3767ac828e7205", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 6014, "upload_time": "2017-05-31T16:17:53", "url": "https://files.pythonhosted.org/packages/f5/09/7fbfe25249ec8ada22b1e70a2c7445af0444450d3df595d1011d7eab5530/implements-0.1.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "684b0db411e224c70d575d3561721aff", "sha256": "98fd3e7b53e78b00b673ec8e6bd175a601fc7c669215e1c379770cadcf60892a" }, "downloads": -1, "filename": "implements-0.1.3.tar.gz", "has_sig": false, "md5_digest": "684b0db411e224c70d575d3561721aff", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14990, "upload_time": "2017-05-31T16:17:51", "url": "https://files.pythonhosted.org/packages/fb/70/130e69402e1b88d698db7e6be49f2e11110816797fa1392ce7da136bb2bc/implements-0.1.3.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "93fadfed71c13daecd301ae9454a24c2", "sha256": "6e5e8ed393395b505eed0bc3ea19db65cecd7435480ce591fd6b61c7fece5582" }, "downloads": -1, "filename": "implements-0.1.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "93fadfed71c13daecd301ae9454a24c2", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 4246, "upload_time": "2019-04-12T13:52:23", "url": "https://files.pythonhosted.org/packages/0c/5e/7d2db804ff4aa479b99241d3295e5bb6fe12a73b8b7ef615f71fa5571d6e/implements-0.1.4-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bcc6144d334dc87340732d4ae72f59fb", "sha256": "afbbbe392fa3cc537ebffe4de70dfcdcb5caf54303e27e40812cd18fb12ef643" }, "downloads": -1, "filename": "implements-0.1.4.tar.gz", "has_sig": false, "md5_digest": "bcc6144d334dc87340732d4ae72f59fb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14795, "upload_time": "2019-04-12T13:52:20", "url": "https://files.pythonhosted.org/packages/9b/20/80b5718eb6be1feac123147deaa61e44fc47981569676d4ce0a654670182/implements-0.1.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "93fadfed71c13daecd301ae9454a24c2", "sha256": "6e5e8ed393395b505eed0bc3ea19db65cecd7435480ce591fd6b61c7fece5582" }, "downloads": -1, "filename": "implements-0.1.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "93fadfed71c13daecd301ae9454a24c2", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 4246, "upload_time": "2019-04-12T13:52:23", "url": "https://files.pythonhosted.org/packages/0c/5e/7d2db804ff4aa479b99241d3295e5bb6fe12a73b8b7ef615f71fa5571d6e/implements-0.1.4-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bcc6144d334dc87340732d4ae72f59fb", "sha256": "afbbbe392fa3cc537ebffe4de70dfcdcb5caf54303e27e40812cd18fb12ef643" }, "downloads": -1, "filename": "implements-0.1.4.tar.gz", "has_sig": false, "md5_digest": "bcc6144d334dc87340732d4ae72f59fb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14795, "upload_time": "2019-04-12T13:52:20", "url": "https://files.pythonhosted.org/packages/9b/20/80b5718eb6be1feac123147deaa61e44fc47981569676d4ce0a654670182/implements-0.1.4.tar.gz" } ] }