{ "info": { "author": "Alice Bevan-McGregor", "author_email": "alice@gothcandy.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "=====================\nweb.dispatch.resource\n=====================\n\n \u00a9 2009-2019 Alice Bevan-McGregor and contributors.\n\n..\n\n https://github.com/marrow/web.dispatch.resource\n\n..\n\n |latestversion| |ghtag| |masterstatus| |mastercover| |masterreq| |ghwatch| |ghstar|\n\n\n\nIntroduction\n============\n\nDispatch is the process of taking some starting point and a path, then resolving the object that path refers to. This\nprocess is common to almost every web application framework (transforming URLs into controllers), RPC system, and even\nfilesystem shell. Other terms for this process include: \"traversal\", \"routing\", or \"lookup\".\n\nResource dispatch utilizes the HTTP verb (provided as the ``HTTP_METHOD`` WSGI environment variable) to determine which\nmethod to call.\n\nThis package speaks a standardized `dispatch protocol `__\nand is not entirely intended for direct use by most developers. The target audience is instead the authors of\nframeworks that may require such modular dispatch for use by their own users.\n\n\nInstallation\n============\n\nInstalling ``web.dispatch.resource`` is easy, just execute the following in a terminal::\n\n pip install web.dispatch.resource\n\n**Note:** We *strongly* recommend always using a container, virtualization, or sandboxing environment of some kind when\ndeveloping using Python; installing things system-wide is yucky (for a variety of reasons) nine times out of ten. We\nprefer light-weight `virtualenv `__, others prefer solutions as\nrobust as `Vagrant `__.\n\nIf you add ``web.dispatch.resource`` to the ``install_requires`` argument of the call to ``setup()`` in your\napplication's ``setup.py`` file, this dispatcher will be automatically installed and made available when your own\napplication or library is installed. We recommend using \"less than\" version numbers to ensure there are no\nunintentional side-effects when updating. Use ``web.dispatch.resource<2.1`` to get all bugfixes for the current\nrelease, and ``web.dispatch.resource<3.0`` to get bugfixes and feature updates while ensuring that large breaking\nchanges are not installed.\n\n\nDevelopment Version\n-------------------\n\n |developstatus| |developcover| |ghsince| |issuecount| |ghfork|\n\nDevelopment takes place on `GitHub `__ in the \n`web.dispatch.resource `__ project. Issue tracking, documentation,\nand downloads are provided there.\n\nInstalling the current development version requires `Git `_, a distributed source code management\nsystem. If you have Git you can run the following to download and *link* the development version into your Python\nruntime::\n\n git clone https://github.com/marrow/web.dispatch.resource.git\n pip install -e web.dispatch.resource\n\nYou can then upgrade to the latest version at any time::\n\n cd web.dispatch.resource\n git pull\n pip install -U -e .\n\nIf you would like to make changes and contribute them back to the project, fork the GitHub project, make your changes,\nand submit a pull request. This process is beyond the scope of this documentation; for more information see\n`GitHub's documentation `_.\n\n\nUsage\n=====\n\nThis section is split to cover framework authors who will need to integrate the overall protocol into their systems,\nand the object interactions this form of dispatch provides for end users.\n\n\nDispatchable Objects\n--------------------\n\nThis form of dispatch relies on having an object whose attributes, named after HTTP verbs, are callable. Typically\nclasses with methods are used for this purpose. A basic example, using the ``web.dispatch.resource:Resource`` helper\nclass, would be::\n\n class Potato(Resource):\n def get(self):\n return \"This is a marvellous potato.\"\n\n\nThis represents a resource (thus the name) with two different endpoints based on the HTTP verb in the request. Fairly\nbasic so far. To define a collection of resources, however, things get a little more complex::\n\n class Field(Collection):\n __resource__ = Potato\n\n potatoes = 10\n\n def get(self):\n return str(self.potatoes) + \" potatoes in the field.\"\n\n def post(self):\n Field.potatoes += 1\n return \"There are now \" + str(Field.potatoes) + \" potatoes in the field.\"\n\n def delete(self):\n Field.potatoes = 0\n return \"You monster.\"\n\n def __getitem__(self, index):\n try:\n index = int(index)\n except ValueError:\n raise KeyError()\n\n if index <= 0 or index > self.potatoes:\n raise KeyError()\n\n return index\n\nThis defines a resource (since colections are also resources) with a few standard operations on it, plus this strange\ndouble underscore method. This is a standard Python feature that lets you define that instances of your class can be\naccessed using mapping subscripts, like a dictionary. This is how resource dispatch looks up individual items out of\ncollections.\n\nIf a KeyError is raised in ``__getitem__``, then that identifier is assumed to not exist.\n\nThe result of this lookup (using the next path element being dispatched against) is passed positionally to the\nconstructor of the class pointed to by the ``__resource__`` attribute of the ``Collection`` subclass, as is a\nreference to the collection that spawned it.\n\nWe can now update our initial example resource to behave as part of a collection::\n\n class Potato(Resource):\n def get(self):\n return \"One of \" + str(self._collection.potatoes) + \" beautiful potatoes.\"\n\n def delete(self):\n self._collection.potatoes -= 1\n return \"You monster.\"\n\nThe text result of a ``GET`` request to ``/`` will be ``10 potatoes in the field.`` You can probably infer the\nremaining behaviour.\n\n\nFurther Descent\n~~~~~~~~~~~~~~~\n\nCustom verbs may be defined as additional methods. Any method whose name is not prefixed with an underscore is treated\nas an HTTP verb. Lastly, if there are remaining path elements, and the next matches an attribute whose value is a\nclass, then that class will be instantiated and yielded as the next step of dispatch.\n\n\nFramework Authors\n-----------------\n\nTo get started using resource dispatch to route requests in your web application, you're going to need to instantiate\nthe dispatcher::\n\n from web.dispatch.resource import ResourceDispatch\n\n dispatch = ResourceDispatch() # There is currently no configuration.\n\nOnce you have that, you'll need a WSGI environment in some form of attribute access object used as the context. Our\nexamples here will use WebOb to provide a mock environment for us::\n\n from webob import Request, Response\n req = Request.objects.blank('/', method=\"delete\")\n context = Context(environ=req.environ, request=req, response=Response())\n\nNow that we have a prepared dispatcher, and prepared context, we'll need to prepare the path according to the\nprotocol::\n\n path = req.path_info.split('/') # Initial path from the request's PATH_INFO.\n path = path[1:] # Skip singular leading slashes; see the API specification.\n path = deque(path) # Provide the path as a deque instance, allowing popleft.\n\nThe above doesn't need to be split apart exaclty like that, but you get the idea of the processing steps that need to\nbe completed prior to calling the dispatcher. The above might happen only once for the entire duration of a request\nwithin a web framework, for example.\n\nWe can now call the dispatcher and iterate dispatch events::\n\n for segment, handler, endpoint, *meta in dispatch(context, some_object, path):\n print(segment, handler, endpoint) # Do something with this information.\n\nWhen a context is provided, it is passed as the first argument to any instantiated classes. After completing iteration,\ncheck the final ``endpoint``. If it is ``True`` then the path was successfully mapped to the object referenced by the\n``handler`` variable, otherwise it represents the deepest object that was able to be found.\n\nYou can always just skip straight to the answer if you so chooose::\n\n segment, handler, endpoint, *meta = list(dispatch(context, some_object, path))[-1]\n\nHowever, providing some mechanism for callbacks or notifications of dispatch is often far more generally useful\n\n**Note:** It is entirely permissable or dispatchers to return ``None`` as a processed path segment. Resource dispatch\nwill, under most circumstances not involving attributes who are classes, will use ``None`` in this way.\n\nPython 2 & 3 Compatibility\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe dispatch protocol is designed to be extendable in the future by using ``namedtuple`` subclasses, however this has\nan impact on usage as you may have noticed the ``*meta`` in there. This syntax, introduced in Python 3, will gather any\nextraneous tuple elements into a separate list. If you actually care about the metadata, do not unpack the tuple this\nway. Instead::\n\n for meta in dispatch(None, some_object, path):\n segment, handler, endpoint = step[:3] # Unpack, but preserve.\n print(segment, handler, endpoint, meta) # Do something with this information.\n\nThis document is written from the perspective of modern Python 3, and throwing away the metadata within the ``for``\nstatement itself provides more compact examples. The above method of unpacking the first three values is the truly\nportable way to do this across versions.\n\n\nVersion History\n===============\n\nVersion 3.0\n-----------\n\n* **Updated minimum Python version.** Marrow Package now requires Python 3.6 or later.\n\n* **Removed Python 2 support and version specific code.** The project has been updated to modern Python packaging standards, including modern namespace use. Modern namespaces are wholly incompatible with the previous namespacing mechanism; this project can not be simultaneously installed with any Marrow project that is Python 2 compatible.\n\nVersion 2.0\n-----------\n\n* Extract of the resource dispatch mechanism from WebCore.\n* Updated to utilize the standardized dispatch protocol.\n\nVersion 1.x\n-----------\n\n* Process fully integrated in the WebCore web framework as the \"RESTful dialect\".\n\n\nLicense\n=======\n\nweb.dispatch.resource has been released under the MIT Open Source license.\n\nThe MIT License\n---------------\n\nCopyright \u00a9 2009-2019 Alice Bevan-McGregor and contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\ndocumentation files (the \u201cSoftware\u201d), to deal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the\nSoftware.\n\nTHE SOFTWARE IS PROVIDED \u201cAS IS\u201d, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n.. |ghwatch| image:: https://img.shields.io/github/watchers/marrow/web.dispatch.resource.svg?style=social&label=Watch\n :target: https://github.com/marrow/web.dispatch.resource/subscription\n :alt: Subscribe to project activity on Github.\n\n.. |ghstar| image:: https://img.shields.io/github/stars/marrow/web.dispatch.resource.svg?style=social&label=Star\n :target: https://github.com/marrow/web.dispatch.obresourceject/subscription\n :alt: Star this project on Github.\n\n.. |ghfork| image:: https://img.shields.io/github/forks/marrow/web.dispatch.resource.svg?style=social&label=Fork\n :target: https://github.com/marrow/web.dispatch.resource/fork\n :alt: Fork this project on Github.\n\n.. |masterstatus| image:: http://img.shields.io/travis/marrow/web.dispatch.resource/master.svg?style=flat\n :target: https://travis-ci.org/marrow/web.dispatch.resource/branches\n :alt: Release build status.\n\n.. |mastercover| image:: http://img.shields.io/codecov/c/github/marrow/web.dispatch.resource/master.svg?style=flat\n :target: https://codecov.io/github/marrow/web.dispatch.resource?branch=master\n :alt: Release test coverage.\n\n.. |masterreq| image:: https://img.shields.io/requires/github/marrow/web.dispatch.resource.svg\n :target: https://requires.io/github/marrow/web.dispatch.resource/requirements/?branch=master\n :alt: Status of release dependencies.\n\n.. |developstatus| image:: http://img.shields.io/travis/marrow/web.dispatch.resource/develop.svg?style=flat\n :target: https://travis-ci.org/marrow/web.dispatch.resource/branches\n :alt: Development build status.\n\n.. |developcover| image:: http://img.shields.io/codecov/c/github/marrow/web.dispatch.resource/develop.svg?style=flat\n :target: https://codecov.io/github/marrow/web.dispatch.resource?branch=develop\n :alt: Development test coverage.\n\n.. |developreq| image:: https://img.shields.io/requires/github/marrow/web.dispatch.resource.svg\n :target: https://requires.io/github/marrow/web.dispatch.resource/requirements/?branch=develop\n :alt: Status of development dependencies.\n\n.. |issuecount| image:: http://img.shields.io/github/issues-raw/marrow/web.dispatch.resource.svg?style=flat\n :target: https://github.com/marrow/web.dispatch.resource/issues\n :alt: Github Issues\n\n.. |ghsince| image:: https://img.shields.io/github/commits-since/marrow/web.dispatch.resource/2.0.0.svg\n :target: https://github.com/marrow/web.dispatch.resource/commits/develop\n :alt: Changes since last release.\n\n.. |ghtag| image:: https://img.shields.io/github/tag/marrow/web.dispatch.resource.svg\n :target: https://github.com/marrow/web.dispatch.resource/tree/2.0.0\n :alt: Latest Github tagged release.\n\n.. |latestversion| image:: http://img.shields.io/pypi/v/web.dispatch.resource.svg?style=flat\n :target: https://pypi.python.org/pypi/web.dispatch.resource\n :alt: Latest released version.\n\n.. |cake| image:: http://img.shields.io/badge/cake-lie-1b87fb.svg?style=flat\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "https://github.com/marrow/web.dispatch.resource/releases", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/marrow/web.dispatch.resource", "keywords": "marrow,dispatch,web.dispatch,resource dispatch,endpoint discovery,WebCore,REST", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "web.dispatch.resource", "package_url": "https://pypi.org/project/web.dispatch.resource/", "platform": "", "project_url": "https://pypi.org/project/web.dispatch.resource/", "project_urls": { "Documentation": "https://github.com/marrow/web.dispatch.resource/#readme", "Download": "https://github.com/marrow/web.dispatch.resource/releases", "Funding": "https://www.patreon.com/GothAlice", "Homepage": "https://github.com/marrow/web.dispatch.resource", "Issue Tracker": "https://github.com/marrow/web.dispatch.resource/issues", "Repository": "https://github.com/marrow/web.dispatch.resource/" }, "release_url": "https://pypi.org/project/web.dispatch.resource/3.0.0/", "requires_dist": [ "web.dispatch (~=3.0.1)", "pytest ; extra == 'development'", "pytest-cov ; extra == 'development'", "pytest-flakes ; extra == 'development'", "pytest-catchlog ; extra == 'development'", "WebOb ; extra == 'development'", "pre-commit ; extra == 'development'" ], "requires_python": "", "summary": "Resource dispatch; a method to resolve a request to an endopint using the WSGI HTTP_METHOD and attribute access.", "version": "3.0.0" }, "last_serial": 5382749, "releases": { "2.0.0": [ { "comment_text": "", "digests": { "md5": "df088c309bde4714006843b5a55f77be", "sha256": "fb58432cf96707cdc6090db3635d6d7cdfacc25cd3af4914622f4ab4770907f4" }, "downloads": -1, "filename": "web.dispatch.resource-2.0.0-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "df088c309bde4714006843b5a55f77be", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 21768, "upload_time": "2016-09-26T15:05:56", "url": "https://files.pythonhosted.org/packages/1d/26/e3b6acbb157a0ae7387fdbc97f10a0a34447b6f8fffaffe1144745a71ff0/web.dispatch.resource-2.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6470d4ce5efefdd7f46c4f9f3e3d5951", "sha256": "7d25202d9cfc01e39dbc64ea04f1ebb73bd955c5357be73046f6fb2a613334ef" }, "downloads": -1, "filename": "web.dispatch.resource-2.0.0.tar.gz", "has_sig": true, "md5_digest": "6470d4ce5efefdd7f46c4f9f3e3d5951", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16245, "upload_time": "2016-09-26T15:05:51", "url": "https://files.pythonhosted.org/packages/c2/69/b1fadaa5dfa4c034bc7263b3ad3516f58a999281a60b67db31a0d70f4898/web.dispatch.resource-2.0.0.tar.gz" } ], "3.0.0": [ { "comment_text": "", "digests": { "md5": "8ff08f3ed16c5dd22d56e9bb21086c11", "sha256": "3728cd55e345c4c2941d562951aaa6adf1512ba53132958fa2e67dc42306acdd" }, "downloads": -1, "filename": "web.dispatch.resource-3.0.0-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "8ff08f3ed16c5dd22d56e9bb21086c11", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 15655, "upload_time": "2019-06-10T17:51:16", "url": "https://files.pythonhosted.org/packages/7c/4a/2c6832f229fb6529694f7aafe4635ed156315b22a56ee006963215591730/web.dispatch.resource-3.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5496ac95d3d96de9ac20a20a94baf32e", "sha256": "b7015f0e317060a0697806b9b77c286a92c5be2e1ee6c5a855883594f4a9f2e7" }, "downloads": -1, "filename": "web.dispatch.resource-3.0.0.tar.gz", "has_sig": true, "md5_digest": "5496ac95d3d96de9ac20a20a94baf32e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16615, "upload_time": "2019-06-10T17:51:18", "url": "https://files.pythonhosted.org/packages/ea/d7/790629c05d309dec6ed5998ca6ed18feddea774d77f9751e5a6b315402c4/web.dispatch.resource-3.0.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8ff08f3ed16c5dd22d56e9bb21086c11", "sha256": "3728cd55e345c4c2941d562951aaa6adf1512ba53132958fa2e67dc42306acdd" }, "downloads": -1, "filename": "web.dispatch.resource-3.0.0-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "8ff08f3ed16c5dd22d56e9bb21086c11", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 15655, "upload_time": "2019-06-10T17:51:16", "url": "https://files.pythonhosted.org/packages/7c/4a/2c6832f229fb6529694f7aafe4635ed156315b22a56ee006963215591730/web.dispatch.resource-3.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5496ac95d3d96de9ac20a20a94baf32e", "sha256": "b7015f0e317060a0697806b9b77c286a92c5be2e1ee6c5a855883594f4a9f2e7" }, "downloads": -1, "filename": "web.dispatch.resource-3.0.0.tar.gz", "has_sig": true, "md5_digest": "5496ac95d3d96de9ac20a20a94baf32e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16615, "upload_time": "2019-06-10T17:51:18", "url": "https://files.pythonhosted.org/packages/ea/d7/790629c05d309dec6ed5998ca6ed18feddea774d77f9751e5a6b315402c4/web.dispatch.resource-3.0.0.tar.gz" } ] }