{ "info": { "author": "Gr\u00e9gory Salvan", "author_email": "apieum@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Other Environment", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "**************\nLiskov Utility\n**************\n\n.. image:: https://pypip.in/v/liskov/badge.png\n :target: https://pypi.python.org/pypi/liskov\n\nUtility to check if your subtypes pass supertypes tests.\n\n\nLiskov Substitution is related in SOLID principles.\nIt has been formulated by Barbara Liskov and Jeanette Wing\nin order to define more precisely the notion of subtypes.\n\nFor more details read: http://reports-archive.adm.cs.cmu.edu/anon/1999/CMU-CS-99-156.ps\n\nTo ensure a certain respect of Liskov Substitution principle in your program,\nyou can simply make your subtypes tests extends your supertypes tests, but\nimporting a supertype test directly in subtype file make the test\nbeing recognized by unit tests runners and run it several times.\n\nYou can just import it in a function, but to not repeat this every time,\nI've made a small util, wich offers 3 ways to declare a subtype test.\n\nEach solution gives you different expressiveness depending on your preference:\n - \"subtype\" function which just import and return a class from a module given as string argument.\n - \"behave_as\" metaclass generator function which returns a metaclass from given modules.\n - \"can_substitute\" decorator wich returns the class extending modules given as arguments.\n\nLiskov and Wing classified subtype relationships in two broad categories:\n - Extension subtypes: add methods or eventually states to supertypes\n - Constrained subtypes: when supertype enable variations in subtypes\n\nSince version 0.2 you can find some helpers to define constraints.(see example 4)\n\n---------------------------------------------------------------------\n\n**Table of Contents**\n\n\n.. contents::\n :local:\n :depth: 1\n :backlinks: none\n\n=============\nInstallation\n=============\n\nSimply install it from pypi::\n\n pip install liskov\n\nor from sources::\n\n git clone git@github.com:apieum/liskov.git\n cd liskov\n python setup.py install\n\n=====\nUsage\n=====\n\n------------------------\nExample 1 - \"subtype\":\n------------------------\n Use a lambda if too long.\n\n\n.. code-block:: python\n\n from liskov import subtype\n\n BasicCalc = lambda: subtype('testCalc.BasicCalcTest')\n BaseConverter = lambda: subtype('testConvert.BaseConverterTest')\n class ScientificCalcTest(BasicCalc(), BaseConverter()):\n def test_it_is_a_subtype_of_BasicCalc(self):\n from testCalc import BasicCalcTest\n assert isinstance(self, BasicCalcTest)\n\n def test_it_is_a_subtype_of_BaseConverter(self):\n from testConvert import BaseConverterTest\n assert isinstance(self, BaseConverterTest)\n\n------------------------\nExample 2 - \"behave_as\":\n------------------------\n*Python 2 version*\n\n.. code-block:: python\n\n from liskov import behave_as\n\n class ScientificCalcTest(object):\n __metaclass__ = behave_as('testCalc.BasicCalcTest', 'testConvert.BaseConverterTest')\n\n def test_it_is_a_subtype_of_BasicCalc(self):\n from testCalc import BasicCalcTest\n assert isinstance(self, BasicCalcTest)\n\n def test_it_is_a_subtype_of_BaseConverter(self):\n from testConvert import BaseConverterTest\n assert isinstance(self, BaseConverterTest)\n\n\n\n*Python 3 version*\n\n.. code-block:: python\n\n from liskov import behave_as\n\n metaclass = behave_as('testCalc.BasicCalcTest', 'testConvert.BaseConverterTest')\n\n class ScientificCalcTest(object, metaclass=metaclass):\n def test_it_is_a_subtype_of_BasicCalc(self):\n from testCalc import BasicCalcTest\n assert isinstance(self, BasicCalcTest)\n\n def test_it_is_a_subtype_of_BaseConverter(self):\n from testConvert import BaseConverterTest\n assert isinstance(self, BaseConverterTest)\n\n\n-----------------------------\nExample 3 - \"can_substitute\":\n-----------------------------\n\n.. code-block:: python\n\n from liskov import can_substitute\n\n @can_substitute('testCalc.BasicCalcTest', 'testConvert.BaseConverterTest')\n class ScientificCalcTest(object):\n def test_it_is_a_subtype_of_BasicCalc(self):\n from testCalc import BasicCalcTest\n assert isinstance(self, BasicCalcTest)\n\n def test_it_is_a_subtype_of_BaseConverter(self):\n from testConvert import BaseConverterTest\n assert isinstance(self, BaseConverterTest)\n\n\n-----------------------------\nExample 4 - Constraints:\n-----------------------------\n\nThis example follow Liskov and Wing constrained subtypes Elephants hierarchy example\nfrom \"Behavioural Subtyping using invariants and constraints\" (link above)\n\nElephants can be white, green or blue\nRoyalElephant is always blue\nAlbinoElephant is always white\n\nEach instance of Elephant in ElephantTest is made with \"new_elephant\"\nElephantTest test if an Elephant can be white, green or blue.\n\n\n*Declare Constraints with a decorator*\n\n\n.. code-block:: python\n\n from liskov import can_substitute, under_constraint\n import elephant\n\n @can_substitute('elephant.ElephantTest')\n @under_constraint('test_it_can_be_grey', 'test_it_can_be_white')\n class RoyalElephantTest(object):\n def new_elephant(self, *args):\n return elephant.RoyalElephant()\n\n\n*Declare Constraints with metaclass*\n\nPython 2 version\n\n.. code-block:: python\n\n from liskov import behave_as\n import elephant\n\n class RoyalElephantTest(object):\n __metaclass__ = behave_as('elephant.ElephantTest').except_for('test_it_can_be_grey', 'test_it_can_be_white')\n def new_elephant(self, *args):\n return elephant.RoyalElephant()\n\n\n\nPython 3 version\n\n.. code-block:: python\n\n from liskov import behave_as\n import elephant\n\n metaclass = behave_as('elephant.ElephantTest').except_for('test_it_can_be_grey', 'test_it_can_be_white')\n class RoyalElephantTest(object, metaclass=metaclass):\n def new_elephant(self, *args):\n return elephant.RoyalElephant()\n\n\n*Declare Constraints with subtype function*\n bind \"subtype\" to \"constrain\" with any of these operators: \"& | + -\"\n\n.. code-block:: python\n\n from liskov import subtype, constrain\n import elephant\n\n ConstrainedElephantTest = lambda: subtype('elephant.ElephantTest') & constrain('test_it_can_be_grey', 'test_it_can_be_white')\n\n class RoyalElephantTest(ConstrainedElephantTest()):\n def new_elephant(self, *args):\n return elephant.RoyalElephant()\n\n\n===========\nDevelopment\n===========\n\nFell free to give feedback or improvment.\n\nLaunch test::\n\n git clone git@github.com:apieum/liskov.git\n cd liskov\n nosetests --with-spec --spec-color\n\n\n.. image:: https://secure.travis-ci.org/apieum/liskov.png?branch=master\n :target: https://travis-ci.org/apieum/liskov", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://www.python.org/pypi/liskov", "keywords": null, "license": "LGPL", "maintainer": null, "maintainer_email": null, "name": "liskov", "package_url": "https://pypi.org/project/liskov/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/liskov/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://www.python.org/pypi/liskov" }, "release_url": "https://pypi.org/project/liskov/0.2.2/", "requires_dist": null, "requires_python": null, "summary": "Small helpers to make inheritance with unit tests", "version": "0.2.2" }, "last_serial": 882147, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "44648df9d2c9c61f20dc04aefaa0b7eb", "sha256": "310f6d633999a8119a862a92c38a7f530e4adb951f623497cb79c122eaf945ab" }, "downloads": -1, "filename": "liskov-0.1.tar.gz", "has_sig": false, "md5_digest": "44648df9d2c9c61f20dc04aefaa0b7eb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3374, "upload_time": "2013-10-04T03:35:00", "url": "https://files.pythonhosted.org/packages/dc/e5/c46a8a74adfba28a1f366bef07e309f62eb5bc98a3a5b7d0d1ba292e1d9a/liskov-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "0d183441a3a1a377f0e85e28878b4256", "sha256": "f16ab289ede3a755f7bfff31c6dcef689784fe8057d9fd96e0741a47c7c72b8f" }, "downloads": -1, "filename": "liskov-0.2.tar.gz", "has_sig": false, "md5_digest": "0d183441a3a1a377f0e85e28878b4256", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4598, "upload_time": "2013-10-04T11:31:30", "url": "https://files.pythonhosted.org/packages/36/cd/ff93167c7a28ff9c0977e390197655fc39100d7f4678b25f0b1408bcac79/liskov-0.2.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "4d9892286f0ca12cff842df897949630", "sha256": "7756ef711abf822ce5939bbcb1ae46046fbc1deb844380c182b4785b800835ca" }, "downloads": -1, "filename": "liskov-0.2.1.tar.gz", "has_sig": false, "md5_digest": "4d9892286f0ca12cff842df897949630", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5007, "upload_time": "2013-10-05T21:45:22", "url": "https://files.pythonhosted.org/packages/36/67/95bd8b587d5727c486d15e7385d52b42e19c889d618941a28c5ead0c49ea/liskov-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "556351409a43e696d2318598130f5983", "sha256": "204e865eba84d4aeb1375e814cdf4d9862d6cf46bf57ace0bb62b03cd894cb72" }, "downloads": -1, "filename": "liskov-0.2.2-py2.7.egg", "has_sig": false, "md5_digest": "556351409a43e696d2318598130f5983", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 8790, "upload_time": "2013-10-05T22:21:50", "url": "https://files.pythonhosted.org/packages/6a/74/b3dd236f41eab4b1d417893ce781c952a7f00662e202f100af58324e13e0/liskov-0.2.2-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "9374f0133bad267cf9883286469a6beb", "sha256": "bb85833acbec80eab416447c7ce71cae5bfb42098b124b213458ebbf0bb1c648" }, "downloads": -1, "filename": "liskov-0.2.2.tar.gz", "has_sig": false, "md5_digest": "9374f0133bad267cf9883286469a6beb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4989, "upload_time": "2013-10-05T22:21:47", "url": "https://files.pythonhosted.org/packages/c1/54/5fc890ec0c1ce4d8e011c264182f8c4dd5ed91751a15fbad21718acff34c/liskov-0.2.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "556351409a43e696d2318598130f5983", "sha256": "204e865eba84d4aeb1375e814cdf4d9862d6cf46bf57ace0bb62b03cd894cb72" }, "downloads": -1, "filename": "liskov-0.2.2-py2.7.egg", "has_sig": false, "md5_digest": "556351409a43e696d2318598130f5983", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 8790, "upload_time": "2013-10-05T22:21:50", "url": "https://files.pythonhosted.org/packages/6a/74/b3dd236f41eab4b1d417893ce781c952a7f00662e202f100af58324e13e0/liskov-0.2.2-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "9374f0133bad267cf9883286469a6beb", "sha256": "bb85833acbec80eab416447c7ce71cae5bfb42098b124b213458ebbf0bb1c648" }, "downloads": -1, "filename": "liskov-0.2.2.tar.gz", "has_sig": false, "md5_digest": "9374f0133bad267cf9883286469a6beb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4989, "upload_time": "2013-10-05T22:21:47", "url": "https://files.pythonhosted.org/packages/c1/54/5fc890ec0c1ce4d8e011c264182f8c4dd5ed91751a15fbad21718acff34c/liskov-0.2.2.tar.gz" } ] }