{ "info": { "author": "baxbaxwalanuksiwe", "author_email": "baxbaxwalanuksiwe@gmail.com", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Software Development", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities" ], "description": "modulable.py\r\n============\r\n\r\n|PyPI version| |License| |Supported Python| |Format| |Downloads|\r\n\r\n``modular.py`` is a small library that helps you write a modular and\r\nmaintainable codebase.\r\n\r\nThe library works with class: the loaded plug-ins are \"injected\" in\r\nthe given class. You can specify the way of injection, either by stacking\r\nfunctions, with the ``modulable`` decorator, by overloading the base method, with\r\nthe ``overridable`` decorator, and finally with the ``alternative`` decorator,\r\nwhich runs every function until it finds one that doesn't raise an exception.\r\n\r\nThose decorators conserve the original method's informations, such as name,\r\nmodule, docstring, and annotations.\r\n\r\n\r\nExample\r\n-------\r\n\r\nLet's say you want to build a modular shell, where the users can implement their\r\nown commands and prompt for example.\r\n\r\n.. code:: python3\r\n\r\n from modular import *\r\n\r\n class Shell(Modular, plugin_directory='plugins'):\r\n\r\nThis declares a modular class, whose plug-ins are in the ``plugins`` directory\r\nrelative to the current working directory.\r\n\r\nThe library will load every plug-in (must be a .py file) in that directory when\r\nthe class is instantiated.\r\n\r\nIt is convenient to declare a ``init`` method, called within the real\r\n``__init__`` constructor, to allow users to initialize their plug-in specific\r\nattributes:\r\n\r\n.. code:: python3\r\n\r\n def __init__(self, *args, **kwds):\r\n self.init(*args, **kwds)\r\n\r\n @modulable\r\n def init(self, *args, **kwds):\r\n self.running = False\r\n\r\nHere, we decorate the ``init`` method with ``modulable``. That means every\r\nplug-ins' implementations of the ``init`` method will be executed with the given\r\narguments.\r\n\r\nNext, we want a function that is executed between every command, and, say, a\r\nmethod that returns the shell prompt:\r\n\r\n.. code:: python3\r\n\r\n @modulable\r\n def update(self):\r\n pass\r\n\r\n @overridable\r\n def prompt(self):\r\n return '> '\r\n\r\nThis time, we use the ``overridable`` decorator. This decorator uses the last\r\nimplementation of ``prompt`` loaded.\r\n\r\nLastly, we want a function that reacts on user input. What we want here is to\r\nrun every implementation until one works (i.e. doesn't raise an error). To do\r\nthat, you can use the ``alternative`` decorator which takes an exception type\r\nand calls every implementation one-by-one until one doesn't raise an error of\r\nthis type:\r\n\r\n.. code:: python3\r\n\r\n @alternative(ValueError)\r\n def react(self, i):\r\n if i:\r\n print('Unrecognized command:', repr(i))\r\n\r\nWe provide a default case here, if there isn't any implementation working.\r\n\r\nFinally, we define some non-modulable methods to make the whole thing works:\r\n\r\n.. code:: python3\r\n\r\n def run(self):\r\n self.running = True\r\n\r\n while self.running:\r\n i = input(self.prompt())\r\n self.react(i)\r\n self.update()\r\n\r\nFor instance, our shell doesn't implement any plug-in. Just for the example,\r\nwe'll implement a ``quit`` plug-in, which stops the shell when the user types in\r\n``quit``, a ``greet`` plug-in, and finally we'll customize our prompt.\r\n\r\nThe implementation of the ``quit`` command is pretty straight-forward:\r\n\r\n.. code:: python3\r\n\r\n def react(self, i):\r\n if i == 'quit':\r\n self.running = False\r\n else:\r\n raise ValueError\r\n\r\nBy raising ``ValueError``, we delegate the input processing to the next\r\nimplementation of ``react``.\r\n\r\nThe ``greet`` plug-in does the same, with a bit more complex parsing:\r\n\r\n.. code:: python3\r\n\r\n def react(self, i):\r\n lexemes = i.split()\r\n\r\n try:\r\n if lexemes[0] == 'greet':\r\n print('Hey', lexemes[1], '!')\r\n else:\r\n raise ValueError\r\n except IndexError:\r\n raise ValueError\r\n\r\nFinally, lets define a prompt that displays the command count:\r\n\r\n.. code:: python3\r\n\r\n def init(self, *args, **kwds):\r\n self.command_count = 0\r\n\r\n def update(self):\r\n self.command_count += 1\r\n\r\n def prompt(self):\r\n return '[{}]: '.format(self.command_count)\r\n\r\nThe plug-ins must be contained in the specified plug-in directory in the class\r\ndeclaration, here, ``plugins``. You should have a similar directory tree:\r\n\r\n::\r\n\r\n .\r\n \u251c\u2500\u2500 plugins\r\n \u2502 \u251c\u2500\u2500 command_count_prompt.py\r\n \u2502 \u251c\u2500\u2500 greet.py\r\n \u2502 \u2514\u2500\u2500 quit.py\r\n \u2514\u2500\u2500 shell.py\r\n\r\nTo use this class, simply instantiate a ``Shell`` object and call its ``run``\r\nmethod:\r\n\r\n.. code:: python3\r\n\r\n sh = Shell()\r\n sh.run()\r\n\r\nHere's what it does:\r\n\r\n::\r\n\r\n [0]:\r\n [1]:\r\n [2]: greet Jonathan\r\n Hey Jonathan !\r\n [3]:\r\n [4]:\r\n [5]: unknown command\r\n Unrecognized command: 'unknown command'\r\n [6]:\r\n [7]: quit\r\n\r\nYou can see the complete code in the `example directory`_.\r\n\r\n\r\nAdvanced use\r\n------------\r\n\r\nYou can temporarily load a plug-in with the ``plugin`` context manager:\r\n\r\n.. code:: python3\r\n\r\n with Shell.plugin('greet'):\r\n sh.run()\r\n\r\nYou can also check the loaded plug-ins by typing ``Shell.loaded_plugins``.\r\n\r\nFinally, there is an optional ``virtual`` keyword argument at class definition.\r\n``virtual`` is set to ``False`` by default, but if set to ``True``, the class\r\nwill not load the plug-ins automatically:\r\n\r\n.. code:: python3\r\n\r\n class AbstractShell(Modular, plugins='plugins', virtual=True):\r\n ...\r\n\r\n\r\nInstallation\r\n------------\r\n\r\n* Via `pip`_:\r\n\r\n.. code:: bash\r\n\r\n $ pip install modulable\r\n\r\n\r\nAnd, if you're on Linux, and face a permission error, make sure to\r\nrun ``sudo`` with the ``-H`` option:\r\n\r\n.. code:: bash\r\n\r\n $ sudo -H pip install modulable\r\n\r\n* Via `git`_:\r\n\r\n.. code:: bash\r\n\r\n $ git clone http://github.com/felko/modulable.git\r\n $ cd modulable\r\n $ sudo -H python3.4 setup.py install\r\n\r\nOr, if you're on Windows:\r\n\r\n.. code:: bash\r\n\r\n $ git clone http://github.com/felko/modulable.git\r\n $ cd modulable\r\n $ py -3.4 setup.py install\r\n\r\nIf you don't have `git`_, you can download the zip file `here `_.\r\n\r\n\r\nLinks\r\n-----\r\n\r\n- GitHub: http://github.com/felko/modulable\r\n- Issue Tracker: http://github.com/feko/modulable/issues\r\n- PyPI: http://pypi.python.org/pypi/modulable\r\n- Download: http://pypi.python.org/pypi/modulable#downloads\r\n\r\n\r\nLicense\r\n-------\r\n\r\n``modulable`` is distributed under the `MIT license`_.\r\n\r\n\r\n.. _pip: http://pip.readthedocs.io/\r\n.. _example directory: https://github.com/felko/modulable/tree/master/examples\r\n.. _MIT license: http://opensource.org/licenses/MIT\r\n.. _git: https://git-scm.com/\r\n\r\n\r\n.. |PyPI version| image:: https://img.shields.io/pypi/v/modulable.svg\r\n :target: https://pypi.python.org/pypi/modulable\r\n :alt: Latest PyPI Version\r\n.. |License| image:: https://img.shields.io/pypi/l/modulable.svg\r\n :target: https://pypi.python.org/pypi/modulable\r\n :alt: License\r\n.. |Supported Python| image:: https://img.shields.io/pypi/pyversions/modulable.svg\r\n :target: https://pypi.python.org/pypi/modulable\r\n :alt: Supported Python Versions\r\n.. |Format| image:: https://img.shields.io/pypi/format/modulable.svg\r\n :target: https://pypi.python.org/pypi/modulable\r\n :alt: Format\r\n.. |Downloads| image:: https://img.shields.io/pypi/dm/modulable.svg\r\n :target: https://pypi.python.org/pypi/modulable\r\n :alt: Downloads", "description_content_type": null, "docs_url": null, "download_url": "https://github.com/felko/modulable", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/felko/modulable", "keywords": "plugin module framework extensible", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "modulable", "package_url": "https://pypi.org/project/modulable/", "platform": "any", "project_url": "https://pypi.org/project/modulable/", "project_urls": { "Download": "https://github.com/felko/modulable", "Homepage": "https://github.com/felko/modulable" }, "release_url": "https://pypi.org/project/modulable/1.0/", "requires_dist": null, "requires_python": null, "summary": "A lightweight library for writing modular code", "version": "1.0" }, "last_serial": 2402294, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "ec656db02210f8e14f678e0c15d775dd", "sha256": "3ba8e49194a7545313494451587b678b87ebce7cb7dab26d23c9dca74c68c7c2" }, "downloads": -1, "filename": "modulable-1.0.zip", "has_sig": false, "md5_digest": "ec656db02210f8e14f678e0c15d775dd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10958, "upload_time": "2016-10-16T12:28:59", "url": "https://files.pythonhosted.org/packages/d9/82/57992c779a18f962f10f633259f09bbd281c947fe9cac877a2f56f22386a/modulable-1.0.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "ec656db02210f8e14f678e0c15d775dd", "sha256": "3ba8e49194a7545313494451587b678b87ebce7cb7dab26d23c9dca74c68c7c2" }, "downloads": -1, "filename": "modulable-1.0.zip", "has_sig": false, "md5_digest": "ec656db02210f8e14f678e0c15d775dd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10958, "upload_time": "2016-10-16T12:28:59", "url": "https://files.pythonhosted.org/packages/d9/82/57992c779a18f962f10f633259f09bbd281c947fe9cac877a2f56f22386a/modulable-1.0.zip" } ] }