{ "info": { "author": "Kevin Conway", "author_email": "kevinjacobconway@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "==========\ndiscoverpy\n==========\n\n*REST service framework for discoverable services.*\n\nWriting RESTful services can be repetitive. Every new resource in an API\nrequires that you pick a URL route, write code to validation data, write a\nrequest handler endpoint, convert your data to something which can be JSON\nencoded, JSON encode your data view, add pagination, set all the right headers,\nand return the proper response codes. Then you have write a new HTTP client,\ncreate object representation of your REST resources, and handle all your\nchosen HTTP response codes. Finally, you get a change to integrate your new\nservice into the code where you actually wanted it by using your Python client\nor hacking it up on the CLI.\n\nThis project takes all of that boilerplate work and automates it. You focus\non the important parts of managing your resource data and integrating the\nservice into your other code. We handle the REST.\n\nQuick Example\n=============\n\n.. code-block:: python\n\n from discoverpy.application import Application\n from discoverpy.service import Service\n from discoverpy.resources.readonly import ReadOnlyResource\n\n class HelloWorldResource(ReadOnlyResource):\n\n hellos = [{\"name\": \"world\"}, {\"name\": \"hello\"}]\n\n identifier = \"name\"\n\n def get(self, query, identifier):\n return {\n \"name\": identifier,\n }\n\n def load(self, query):\n return self.hellos\n\n service = Service(\n name=\"greeting\",\n description=\"A service for greeting people and machines.\",\n version=\"1\",\n )\n service.add(\n HelloWorldResource(\n name=\"hello\",\n description=\"Greet a person or machine with hello.\",\n\n )\n )\n\n app = Application(services=[service])\n\nNow run the service under any WSGI server and request away.\n\n.. code-block:: bash\n\n # Get a paginated list of resources.\n curl \"localhost:8888/greeting/v1/hello\"\n # Get an instance of a resource.\n curl \"localhost:8888/greeting/v1/hello/you\"\n\n # See your new service in the API discovery service.\n curl \"localhost:8888/discover/v1/service?name=greeting\"\n curl \"localhost:8888/discover/v1/resource?service=greeting&version=1\"\n\nThe example is degenerate, but the principle should visible: You don't have to\ndeal with boilerplate. Implementing a resource class which can manage your data\nis sufficient and the framework will handle the HTTP bits.\n\nService Discovery API\n=====================\n\nThe namesake of the project is rooted in the automatic service discovery API\nwhich is generated with each WSGI application. It uses the metadata given\nwhen you create Service or Resource objects to construct an API which allows\nconsumers to see what services your endpoint provides, what resources are\nexposed by those services, and the JSON schema used to validate input.\n\nThe service discovery API works as follows:\n\n- GET /discover/v1/service\n\n Get a paginated list of all services registered with the WSGI application.\n\n- GET /discover/v1/service/{service name}?version={service version}\n\n Get an instance of a service.\n\n- GET /discover/v1/resource?service={service name}&version={service version}\n\n Get a paginated list of resources registered with the service.\n\n- GET /discover/v1/resource/{resource name}?service={service name}&version={service version}\n\n Get an instance of a resource.\n\nThis API is used to power the automatic client.\n\nAutomatic Client\n================\n\nThe project ships with a Python client which leverages the service discovery\nAPI to automatically provide an interface to new services and resources.\n\n.. code-block:: python\n\n from discoverpy.client.api import Client\n\n client = Client(endpoint='http://localhost:8888')\n\n # Iterate over all services and resources.\n for service in client.services:\n\n print('name: {0}, version: {1}, description: {2}'.format(\n service.name,\n service.version,\n service.description,\n ))\n\n for resource in service.resources:\n\n print('name: {0}, description: {1}'.format(\n resource.name,\n resource.description,\n ))\n\n # Get a specific resource and iterate over its paginated output.\n resource = client.service('greeting', version='1').resource('hello')\n for hello in resource.load():\n\n print('name: {0}'.format(hello.name))\n\nResource Classes\n================\n\nThere is a base Resource class which can be extended, but resources can be any\nPython object which expose the following signatures:\n\n - identifier\n\n The field name of the resource identifier in a payload.\n\n - name\n\n The name of the resource.\n\n - description\n\n The description of the resource.\n\n - validator\n\n The JSON schema validator to use with the resource.\n\n - load(query)\n\n Return a two-tuple where the first element is the paginated content\n and the second is the total number of items which match the query.\n\n - get(query, identifier)\n\n Return a single resource matching the query.\n\n - create(query, data)\n\n Create a new resource and return it.\n\n - update(query, identifier, data)\n\n Update an instance and return the new data.\n\n - delete(query, identifier)\n\n Delete the given resource\n\nThe get, load, create, update, and delete methods should return Python\ndictionaries which can be JSON encoded. In each method the query parameter will\nbe a special Query object which wraps a URL query string for easier management,\nthe identifier will the the last portion of a URL, and data will be a Python\ndictionary which was decoded from JSON input.\n\nAny complete implementation of the Resource interface may be passed to the\nService object. There are built in versions of Resource which provide some\nhandy features.\n\nThe resources.readonly.ReadOnlyResource allows subclasses to only implement\nget and load. It will handle the other methods appropriately. The\nresources.sqlalchemy.SqlalchemyResource allows you to pass in a SQLAlchemy ORM\nmodel and generate an API around it. Look at the SqlalchemyResource as an\nexample for how complex resources may be implemented.\n\nLicense\n=======\n\n::\n\n (MIT License)\n\n Copyright (C) 2015 Kevin Conway\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to\n deal in the Software without restriction, including without limitation the\n rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n sell copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n IN THE SOFTWARE.\n\n\nContributing\n============\n\nAll contributions to this project are protected under the agreement found in\nthe `CONTRIBUTING` file. All contributors should read the agreement but, as\na summary::\n\n You give us the rights to maintain and distribute your code and we promise\n to maintain an open source distribution of anything you contribute.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/kevinconway/discover.py", "keywords": null, "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "discoverpy", "package_url": "https://pypi.org/project/discoverpy/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/discoverpy/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/kevinconway/discover.py" }, "release_url": "https://pypi.org/project/discoverpy/0.3.1/", "requires_dist": null, "requires_python": null, "summary": "REST framework for discoverable services.", "version": "0.3.1" }, "last_serial": 1428408, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "07c426a91491c411d048af3290d1e797", "sha256": "5ae4ed92ba1ca59b804181bb6eee0a020a70ce0c23206d57f12177e3ea3d6263" }, "downloads": -1, "filename": "discoverpy-0.1.0.tar.gz", "has_sig": false, "md5_digest": "07c426a91491c411d048af3290d1e797", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10870, "upload_time": "2015-02-08T07:41:36", "url": "https://files.pythonhosted.org/packages/f3/70/26485b51bedd6bd498d20b9c14d858277ac9322c17a890e382dc4b127761/discoverpy-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "224bf9493e5a81b681f3f566de6583af", "sha256": "b433cfc733d3c5931956183722832cc207c3dde588670954d80bea165b1dea35" }, "downloads": -1, "filename": "discoverpy-0.2.0.tar.gz", "has_sig": false, "md5_digest": "224bf9493e5a81b681f3f566de6583af", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13289, "upload_time": "2015-02-15T00:56:51", "url": "https://files.pythonhosted.org/packages/6f/5e/688bfaa2533c0595453d1c66fb72309fc2bb95fd5c86747a4a1e8973ceab/discoverpy-0.2.0.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "84ad923e3560d59e52c9ce4db444dc3c", "sha256": "4efbc8c1644babe03bbba4ddd46cd9c369975338b3c0a1e2755150a32f058e3c" }, "downloads": -1, "filename": "discoverpy-0.3.0.tar.gz", "has_sig": false, "md5_digest": "84ad923e3560d59e52c9ce4db444dc3c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21953, "upload_time": "2015-02-16T14:06:32", "url": "https://files.pythonhosted.org/packages/69/ea/d9c56d526924d8417879153da84baeaef208ad0bdc338a1d4932c63bec05/discoverpy-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "c469b37c2cdc4f2b2f0c3abe44e47b74", "sha256": "1496becf1ec2c914d950e3e874e1a8415fb6df641efe168c2a7b8a9dc9b2eff8" }, "downloads": -1, "filename": "discoverpy-0.3.1.tar.gz", "has_sig": false, "md5_digest": "c469b37c2cdc4f2b2f0c3abe44e47b74", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22009, "upload_time": "2015-02-18T12:05:09", "url": "https://files.pythonhosted.org/packages/c5/78/31d92f3610c382fd4e4a1f18d536b08e60ca229a8717d2d8e422a88364b3/discoverpy-0.3.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c469b37c2cdc4f2b2f0c3abe44e47b74", "sha256": "1496becf1ec2c914d950e3e874e1a8415fb6df641efe168c2a7b8a9dc9b2eff8" }, "downloads": -1, "filename": "discoverpy-0.3.1.tar.gz", "has_sig": false, "md5_digest": "c469b37c2cdc4f2b2f0c3abe44e47b74", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22009, "upload_time": "2015-02-18T12:05:09", "url": "https://files.pythonhosted.org/packages/c5/78/31d92f3610c382fd4e4a1f18d536b08e60ca229a8717d2d8e422a88364b3/discoverpy-0.3.1.tar.gz" } ] }