{ "info": { "author": "Bartek Kryza", "author_email": "bkryza@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.6" ], "description": "decorest - decorator heavy REST client for Python\n#################################################\n\n.. image::\thttps://img.shields.io/travis/bkryza/decorest.svg\n :target: https://travis-ci.org/bkryza/decorest\n\n.. image:: https://img.shields.io/pypi/v/decorest.svg\n :target: https://pypi.python.org/pypi/decorest\n\n.. image:: https://img.shields.io/pypi/l/decorest.svg\n :target: https://pypi.python.org/pypi/decorest\n\n.. image:: https://img.shields.io/pypi/pyversions/decorest.svg\n :target: https://pypi.python.org/pypi/decorest\n\nDeclarative, decorator-based REST client for Python.\n\n.. role:: py(code)\n :language: python\n\n\n.. contents::\n\nOverview\n========\n\ndecorest_ library provides an easy to use declarative REST API client interface,\nwhere definition of the API methods using decorators automatically produces\na working REST client with no additional code. In practice the library provides\nonly an interface to describe and interact with REST services - the actual work\nis done underneath by the requests_ library.\n\nFor example:\n\n.. code-block:: python\n\n from decorest import RestClient, GET\n\n class DogClient(RestClient):\n def __init__(self, endpoint):\n super(DogClient, self).__init__(endpoint)\n\n @GET('breed/{breed_name}/list')\n def list_subbreeds(self, breed_name):\n \"\"\"List all sub-breeds\"\"\"\n\n client = DogClient('https://dog.ceo/api')\n\n print(client.list_subbreeds('hound'))\n\n\nInstallation\n============\n\nUsing pip:\n\n.. code-block:: bash\n\n pip install decorest\n\nUsage\n=====\n\nBasics\n------\n\nFor most typical cases the usage should be fairly straightforward. Simply create a\nsubclass of :py:`decorest.RestClient` and define methods, which will perform calls\nto the actual REST service. You can declare how each function should perform\nthe request to the service solely using decorators attached to the\nmethod definition. The method itself is not expected to have any implementation,\nexcept maybe for a docstring.\n\nAfter your API client class definition is complete, simply create an instance\nof it and you're good to go. This library relies on the functionality provided\nby the requests_ library, which means that any valid named argument, which\ncould be passed to a requests_ HTTP call can be also passed to the calls\nof the client methods and will be forwarded as is.\n\nFor more information checkout sample clients in `examples`.\n\nDecorators\n----------\n\nBelow is a list of all supported decorators along with short explanation and\nexamples. Some decorators can be attached to both client class as well as\nmethods, in which case the class-level decorator is applied to all HTTP methods\nin that class. Furthermore, each decorator can be overridden directly during\nthe method call by providing a named argument with name equal to the decorator\nname.\n\n\n@GET, @PUT, @POST, @PATCH, @DELETE, @HEAD, @OPTIONS\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nMarks the request with a specific HTTP method and the path relative to\nendpoint provided as argument. The path can contain variables enclosed\nin curly brackets, e.g.:\n\n.. code-block:: python\n\n @GET('breed/{breed_name}/list')\n def list_subbreeds(self, breed_name):\n \"\"\"List all sub-breeds\"\"\"\n\nwhich will be replaced by the arguments from the method definition.\nThese decorators apply only to methods.\n\n@query\n~~~~~~\n\nAdds a query parameter to the request. URL encoding will be applied to\nthe value using :py:`urlencode`, e.g.:\n\n.. code-block:: python\n\n @GET('breed/{breed_name}/list')\n @query('long_names', 'longNames')\n @query('limit')\n def list_subbreeds(self, breed_name, long_names, limit=100):\n \"\"\"List all sub-breeds\"\"\"\n\nThis decorator can take a single string parameter, which determines the name\nof the method argument whose value will be added as the query argument value\nof the same name.\n\nIn case 2 arguments are provided, the second argument determines the actual\nquery key name, which will be used in the request query (if for some reason\nit should be different than the method argument name).\n\nFurthermore, if a default value is provided in a method declaration, it\nwill be used whenever a value for this argument is not provided during\ninvocation.\n\nFor example, the following invocation of the above method:\n\n.. code-block:: python\n\n client.list_subbreeds('hound', 1)\n\nwill result in the following query:\n\n.. code-block::\n\n https://dog.ceo/api/breed/hound?longNames=1&limit=100\n\nThis decorator can be added only to methods.\n\n@form\n~~~~~~\n\nAdds a form parameter to the request. For example:\n\n.. code-block:: python\n\n @POST('breed')\n @form('breed_name')\n @form('breed_url', 'breed_wikipedia_link')\n def add_breed(self, breed_name, breed_url):\n \"\"\"Add sub-breed\"\"\"\n\nThis decorator can take a single string parameter, which determines the name\nof the method argument whose value will be added as the query argument value\nof the same name.\n\nIn case 2 arguments are provided, the second argument determines the actual\nform field name, which will be used in the request form (if for some reason\nit cannot be the same as the method argument name).\n\nIf a method has at least one :py:`@form` decorator attached, the `Content-type`\nheader value will be always set to `application/x-www-form-urlencoded`.\n\nThis decorator can be added only to methods.\n\n@header\n~~~~~~~\n\nAdds a header key-value pair to the request, e.g.:\n\n.. code-block:: python\n\n @GET('breed/{breed_name}/list')\n @header('accept', 'application/json')\n def list_subbreeds(self, breed_name):\n \"\"\"List all sub-breeds\"\"\"\n\nThis decorator can be added to both methods and client class. The class level\ndecorators will be added to every method and can be overridden using method\nlevel decorators.\n\n@body\n~~~~~\n\nBody decorator enables to specify which of the method parameters should provide\nthe body content to the request, e.g.:\n\n.. code-block:: python\n\n @POST('pet')\n @header('content-type', 'application/json')\n @header('accept', 'application/json')\n @body('pet')\n def add_pet(self, pet):\n \"\"\"Add a new pet to the store\"\"\"\n\n:py:`@body` decorator can take an optional argument which provides a serialization\nhandler, which will be invoked automatically before passing the argument as\nbody content, which can be a simple lambda or a more complex function with some\nlogic. For example:\n\n.. code-block:: python\n\n @POST('pet')\n @header('content-type', 'application/json')\n @header('accept', 'application/json')\n @body('pet', lambda p: json.dumps(p))\n def add_pet(self, pet):\n \"\"\"Add a new pet to the store\"\"\"\n\nThe above code will automatically stringify the dictionary provided as\nvalue of 'pet' argument using :py:`json.dumps()` function.\n\n@on\n~~~\n\nBy default the request method will not return requests_ response object,\nbut the response will depend on the content type of the response.\n\nIn case the HTTP request succeeds the following results are expected:\n\n- :py:`response.json()` if the content type of response is JSON\n- :py:`response.content` if the content type is binary\n- :py:`response.text` otherwise\n\nIn case the request fails, :py:`response.raise_for_status()` is called and\nshould be handled in the code.\n\nIn case another behavior is required, custom handlers can be provided\nfor each method using lambdas or functions. The provided handler is\nexpected to take only a single argument, which is the requests_ response\nobject, e.g.:\n\n.. code-block:: python\n\n @GET('breed/{breed_name}/list')\n @header('accept', 'application/json')\n @on(200, lambda r: r.json())\n def list_subbreeds(self, breed_name):\n \"\"\"List all sub-breeds\"\"\"\n\nThis decorator can be applied to both methods and classes, however when\napplied to a class the handler will be called for method which receives\nthe provided status code.\n\nThe first argument of this decorator must be an integer. On Python 3 it\nalso possible to pass :py:`...` (i.e. Ellipsis) object, which is equivalent\nto :py:`HttpStatus.ANY`. Any other value passed for this argument will\nraise :py:`TypeError`.\n\n@content\n~~~~~~~~\nThis decorator is a shortcut for :py:`@header('content-type', ...)`, e.g:\n\n.. code-block:: python\n\n @POST('pet')\n @content('application/json')\n @header('accept', 'application/json')\n @body('pet', lambda p: json.dumps(p))\n def add_pet(self, pet):\n \"\"\"Add a new pet to the store\"\"\"\n\n@accept\n~~~~~~~~\nThis decorator is a shortcut for :py:`@header('accept', ...)`, e.g:\n\n.. code-block:: python\n\n @GET('breed/{breed_name}/list')\n @content('application/json')\n @accept('application/xml')\n def list_subbreeds(self, breed_name):\n \"\"\"List all sub-breeds\"\"\"\n\n@endpoint\n~~~~~~~~\nThis decorator enables to define a default endpoint for the service,\nwhich then doesn't have to be provided in the client constructor:\n\n.. code-block:: python\n\n @endpoint('https://dog.ceo/api')\n class DogClient(RestClient):\n \"\"\"List all sub-breeds\"\"\"\n def __init__(self, endpoint=None):\n super(DogClient, self).__init__(endpoint)\n\nThe endpoint provided in the client constructor will take precedence\nhowever.\n\n\n@timeout\n~~~~~~~~\nSpecifies a default timeout value (in seconds) for method or entire API.\n\n.. code-block:: python\n\n @endpoint('https://dog.ceo/api')\n @timeout(5)\n class DogClient(RestClient):\n \"\"\"List all sub-breeds\"\"\"\n def __init__(self, endpoint=None):\n super(DogClient, self).__init__(endpoint)\n\n@stream\n~~~~~~~\nThis decorator allows to specify a method which returns binary stream of data.\nAdding this decorator to a method will add a :py:`stream=True`\nargument to the requests_ call and will by default return entire requests\nobject which then can be accessed for instance using :py:`iter_content()` method.\n\n.. code-block:: python\n\n ...\n\n class MyClient(RestClient):\n ...\n\n @GET('stream/{n}/{m}')\n @stream\n @query('size')\n @query('offset', 'off')\n def stream(self, n, m, size, offset):\n \"\"\"Get data range\"\"\"\n\n ...\n\n with client.stream(2,4, 1024, 200) as r:\n for b in r.iter_content(chunk_size=100):\n content.append(b)\n\n\nSessions\n--------\n\nBased on the functionality provided by requests_ library in the form of\nsession objects, sessions can significantly improve the performance of the\nclient in case multiple responses are performed as well as maintain certain\ninformation between requests such as session cookies.\n\nSessions in decorest_ can either be created and closed manually:\n\n.. code-block:: python\n\n s = client._session()\n s.list_subbreeds('hound')\n s.list_subbreeds('husky')\n s._close()\n\nor can be used via the context manager :py:`with` operator:\n\n.. code-block:: python\n\n with client._session() as s:\n s.list_subbreeds('hound')\n s.list_subbreeds('husky')\n\nAll session specific methods begin with a single underscore, in order not\nto interfere with any possible API method names defined in the base client\nclass.\n\nIf some additional customization of the session is required, the underlying\n`requests session`_ object can be retrieved from decorest_ session object\nusing :py:`_requests_session` attribute:\n\n.. code-block:: python\n\n with client._session() as s:\n s._requests_session.verify = '/path/to/cert.pem'\n s.list_subbreeds('hound')\n s.list_subbreeds('husky')\n\nAuthentication\n--------------\n\nSince authentication is highly specific to actual invocation of the REST API,\nand not to it's specification, there is not decorator for authentication,\nbut instead an authentication object (compatible with `requests_`\nauthentication mechanism) can be set in the client object using\n:py:`_set_auth()` method, for example:\n\n.. code-block:: python\n\n client._set_auth(HTTPBasicAuth('user', 'password')\n with client._session() as s:\n s._requests_session.verify = '/path/to/cert.pem'\n s.list_subbreeds('hound')\n s.list_subbreeds('husky')\n\nThe authentication object will be used in both regular API calls, as well\nas when using sessions.\n\n\nGrouping API methods\n---------------------------\n\nFor larger API's it can be useful to be able to split the API definition\ninto multiple files but still use it from a single instance in the code.\n\nThis can be achieved by creating separate client classes for each group\nof operations and then create a common class, which inherits from all the\ngroup clients and provides entire API from one instance.\n\nFor example of this checkout the `Petstore Swagger client example`_.\n\n\nCaveats\n-------\n\nDecorator order\n~~~~~~~~~~~~~~~\n\nDecorators can be basically added in any order, except for the HTTP method\ndecorator (e.g. :py:`@GET()`), which should always be at the top of the given\ndecorator list. Third party decorators should be added above the HTTP method\ndecorators.\n\nName conflicts\n~~~~~~~~~~~~~~\n\nDecorators can sometimes generate conflicts with decorated method or function\nnames in case they have the same name as they get merged into the :py:`__globals__`\ndictionary. In case this is an issue, decorest decorators should be used with full\nmodule namespace:\n\n.. code-block:: python\n\n @decorest.POST('pet')\n @decorest.content('application/json')\n @decorest.header('accept', 'application/json')\n @decorest.body('pet', lambda p: json.dumps(p))\n def add_pet(self, pet):\n \"\"\"Add a new pet to the store\"\"\"\n\n\nCompatibility with other decorators\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn general the decorators should work with other decorators which return\nfunction objects, but your mileage may vary. In general third-party decorators\nshould be added above the HTTP method decorators as only the HTTP decorators\nmake the actual HTTP request. Thus, typical decorators, which try to wrap\nthe actual call should get the HTTP callable returned by HTTP method decorators\nsuch as :py:`@GET()`.\n\nCurrently, it is not possible to add decorators such as :py:`@classmethod`\nor :py:`@staticmethod` to API methods, as the invocation requires an instance\nof client class.\n\nLicense\n=======\n\nCopyright 2018 Bartosz Kryza \n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n.. _tests: https://github.com/bkryza/decorest/tree/master/tests\n.. _requests: https://github.com/requests/requests\n.. _`requests session`: http://docs.python-requests.org/en/master/user/advanced/#session-objects\n.. _decorest: https://github.com/bkryza/decorest\n.. _`descriptor objects`: https://docs.python.org/3/c-api/descriptor.html\n.. _`Petstore Swagger client example`: https://github.com/bkryza/decorest/blob/master/examples/swagger_petstore/petstore_client.py\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/bkryza/decorest", "keywords": "", "license": "Apache 2.0", "maintainer": "", "maintainer_email": "", "name": "decorest", "package_url": "https://pypi.org/project/decorest/", "platform": "", "project_url": "https://pypi.org/project/decorest/", "project_urls": { "Homepage": "https://github.com/bkryza/decorest" }, "release_url": "https://pypi.org/project/decorest/0.0.5/", "requires_dist": null, "requires_python": "", "summary": "`decorest` library provides an easy to use declarative REST API client interface, where definition of the API methods using decorators automatically gives a working REST client with no additional code.", "version": "0.0.5" }, "last_serial": 4251365, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "0bebd956e9de2eb687fd6b375ad7f19c", "sha256": "dd0a3c65da986fc3284c62d99f34c1889be1a437eee8787f5a184311183da711" }, "downloads": -1, "filename": "decorest-0.0.1.tar.gz", "has_sig": false, "md5_digest": "0bebd956e9de2eb687fd6b375ad7f19c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8115, "upload_time": "2018-02-08T01:39:33", "url": "https://files.pythonhosted.org/packages/40/61/09cbdc6d009e9feb6ca8c1beca95d16a5f2a63cb868240940a5a99f4320c/decorest-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "9b70a5ca614e6b80b87cb4fc45ab690a", "sha256": "87702e3c91b81bd3eaf856c6d58ff869705a212a2689a06df3365af2d08478a1" }, "downloads": -1, "filename": "decorest-0.0.2.tar.gz", "has_sig": false, "md5_digest": "9b70a5ca614e6b80b87cb4fc45ab690a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13770, "upload_time": "2018-02-11T23:25:50", "url": "https://files.pythonhosted.org/packages/79/a3/5b35a2db902802ae30dd6fae1f005b86da15c4893f8598d4be50d175fa13/decorest-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "fb0ee1de9f940f5f9f55ecc77593658b", "sha256": "e5c05fe4dabd26297f7d27a0f83e840a43c782ef460bbf2f3d36b630492f32ac" }, "downloads": -1, "filename": "decorest-0.0.3.tar.gz", "has_sig": false, "md5_digest": "fb0ee1de9f940f5f9f55ecc77593658b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13607, "upload_time": "2018-04-02T12:59:11", "url": "https://files.pythonhosted.org/packages/13/ac/07dfd314a33f2b892b3a53d3b71f8ce11c64e8992d92a1ac786279c9c084/decorest-0.0.3.tar.gz" } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "481f1319a923ef1336bb9270ce7abc19", "sha256": "4c42dc5cea9f22fa878ba0d0a0d0af50e7f59641495aa37bbd39117c935b6c9d" }, "downloads": -1, "filename": "decorest-0.0.5.tar.gz", "has_sig": false, "md5_digest": "481f1319a923ef1336bb9270ce7abc19", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17802, "upload_time": "2018-09-08T09:59:29", "url": "https://files.pythonhosted.org/packages/b2/b1/857a34415a89a6c96026136e16e0709cc8a7b3b0e4a9b2b643a4327c0f04/decorest-0.0.5.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "481f1319a923ef1336bb9270ce7abc19", "sha256": "4c42dc5cea9f22fa878ba0d0a0d0af50e7f59641495aa37bbd39117c935b6c9d" }, "downloads": -1, "filename": "decorest-0.0.5.tar.gz", "has_sig": false, "md5_digest": "481f1319a923ef1336bb9270ce7abc19", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17802, "upload_time": "2018-09-08T09:59:29", "url": "https://files.pythonhosted.org/packages/b2/b1/857a34415a89a6c96026136e16e0709cc8a7b3b0e4a9b2b643a4327c0f04/decorest-0.0.5.tar.gz" } ] }