{ "info": { "author": "Mike Wooster", "author_email": "", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.6" ], "description": "# Python API Client\n\nA client for communicating with an api should be a clean abstraction\nover the third part api you are communicating with. It should be easy to \nunderstand and have the sole responsibility of calling the endpoints and\nreturning data.\n\nTo achieve this, `APIClient` takes care of the other (often duplicated) \nresponsibilities, such as authentication and response handling, moving \nthat code away from the clean abstraction you have designed.\n\n## Quick links\n1. [Installation](#Installation)\n2. [Client in action](#Usage)\n3. [Adding retries to requests](#Retrying)\n4. [Working with paginated responses](#Pagination)\n5. [Authenticating your requests](#Authentication-Methods)\n6. [Handling the formats of your responses](#Response-Handlers)\n7. [Correctly encoding your outbound request data](#Request-Formatters)\n8. [Handling bad requests and responses](#Exceptions)\n9. [Endpoints as code](#Endpoints)\n\n## Installation\n\n```\npip install api-client\n```\n\n## Usage\n\n### Simple Example\n```\nfrom apiclient import APIClient\n\nclass MyClient(APIClient):\n\n def list_customers(self):\n url = \"http://example.com/customers\"\n return self.get(url)\n\n def add_customer(self, customer_info):\n url = \"http://example.com/customers\"\n return self.post(url, data=customer_info)\n\n>>> client = MyClient()\n>>> client.add_customer({\"name\": \"John Smith\", \"age\": 28})\n>>> client.list_customers()\n[\n ...,\n {\"name\": \"John Smith\", \"age\": 28},\n]\n```\nThe `APIClient` exposes a number of predefined methods that you can call\nThis example uses `get` to perform a GET request on an endpoint.\nOther methods include: `post`, `put`, `patch` and `delete`. More \ninformation on these methods is documented in the [Interface](#APIClient-Interface).\n\n\nFor a more complex use case example, see: [Extended example](#Extended-Example)\n\n## Retrying\n\nTo add some robustness to your client, the power of [tenacity](https://github.com/jd/tenacity)\nhas been harnessed to add a `@retry_request` decorator to the `apiclient` toolkit.\n\nThis will retry any request which responds with a 5xx status_code (which is normally safe\nto do as this indicates something went wrong when trying to make the request), or when an\n`UnexpectedError` occurs when attempting to establish the connection.\n\n`@retry_request` has been configured to retry for a maximum of 5 minutes, with an exponential\nbackoff strategy. For more complicated uses, the user can use tenacity themselves to create\ntheir own custom decorator.\n\nUsage:\n\n```\nfrom apiclient import retry_request\n\nclass MyClient(APIClient):\n\n @retry_request\n def retry_enabled_method():\n ...\n\n```\n\nFor more complex use cases, you can build your own retry decorator using\ntenacity along with the custom retry strategy.\n\nFor example, you can build a retry decorator that retries `APIRequestError`\nwhich waits for 2 seconds between retries and gives up after 5 attempts.\n\n```\nimport tenacity\nfrom apiclient.retrying import retry_if_api_request_error\n\nretry_decorator = tenacity.retry(\n retry=retry_if_api_request_error(),\n wait=tenacity.wait_fixed(2),\n stop=tenacity.stop_after_attempt(5),\n reraise=True,\n)\n```\n\nOr you can build a decorator that will retry only on specific status \ncodes (following a failure).\n\n```\nretry_decorator = tenacity.retry(\n retry=retry_if_api_request_error(status_codes=[500, 501, 503]),\n wait=tenacity.wait_fixed(2),\n stop=tenacity.stop_after_attempt(5),\n reraise=True,\n)\n```\n\n\n## Pagination\n\nIn order to support contacting pages that respond with multiple pages of data when making get requests,\nadd a `@paginated` decorator to your client method. `@paginated` can paginate the requests either where\nthe pages are specified in the query parameters, or by modifying the url.\n\nUsage is simple in both cases; paginator decorators take a Callable with two required arguments:\n- `by_query_params` -> callable takes `response` and `previous_page_params`.\n- `by_url` -> callable takes `respones` and `previous_page_url`.\nThe callable will need to return either the params in the case of `by_query_params`, or a new url in the \ncase of `by_url`.\nIf the response is the last page, the function should return None.\n\nUsage:\n\n```\nfrom apiclient import paginated\n\n\ndef next_page_by_params(response, previous_page_params):\n # Function reads the response data and returns the query param\n # that tells the next request to go to.\n return {\"next\": response[\"pages\"][\"next\"]\n\n\ndef next_page_by_url(response, previous_page_url):\n # Function reads the response and returns the url as string\n # where the next page of data lives.\n return response[\"pages\"][\"next\"][\"url\"]\n\n\nclass MyClient(APIClient):\n\n @paginated(by_query_params=next_page_by_params)\n def paginated_example_one():\n ...\n\n @paginated(by_url=next_page_by_url)\n def paginated_example_two():\n ...\n\n```\n\n\n## Authentication Methods\nAuthentication methods provide a way in which you can customize the\nclient with various authentication schemes through dependency injection,\nmeaning you can change the behaviour of the client without changing the\nunderlying implementation.\n\nThe apiclient supports the following authentication methods, by specifying\nthe initialized class on initialization of the client, as follows:\n```\nclient = ClientImplementation(\n authentication_method=(),\n response_handler=...,\n request_formatter=...,\n)\n```\n\n### `NoAuthentication`\nThis authentication method simply does not add anything to the client,\nallowing the api to contact APIs that do not enforce any authentication.\n\nExample:\n```\nclient = ClientImplementation(\n authentication_method=NoAuthentication(),\n response_handler=...,\n request_formatter=...,\n)\n```\n\n### `QueryParameterAuthentication`\nThis authentication method adds the relevant parameter and token to the\nclient query parameters. Usage is as follows:\n\n```\nclient = ClientImplementation(\n authentication_method=QueryParameterAuthentication(parameter=\"apikey\", token=\"secret_token\"),\n response_handler=...,\n request_formatter=...,\n)\n```\nExample. Contacting a url with the following data\n```\nhttp://api.example.com/users?age=27\n```\nWill add the authentication parameters to the outgoing request:\n```\nhttp://api.example.com/users?age=27&apikey=secret_token\n```\n\n### `HeaderAuthentication`\nThis authentication method adds the relevant authorization header to\nthe outgoing request. Usage is as follows:\n```\nclient = ClientImplementation(\n authentication_method=HeaderAuthentication(token=\"secret_value\"),\n response_handler=...,\n request_formatter=...,\n)\n\n# Constructs request header:\n{\"Authorization\": \"Bearer secret_value\"}\n```\nThe `Authorization` parameter and `Bearer` scheme can be adjusted by\nspecifying on method initialization.\n```\nauthentication_method=HeaderAuthentication(\n token=\"secret_value\"\n parameter=\"apikey\",\n scheme=\"Token\",\n)\n\n# Constructs request header:\n{\"apikey\": \"Token secret_value\"}\n```\n\nOr alternatively, when APIs do not require a scheme to be set, you can\nspecify it as a value that evaluates to False to remove the scheme from\nthe header:\n```\nauthentication_method=HeaderAuthentication(\n token=\"secret_value\"\n parameter=\"token\",\n scheme=None,\n)\n\n# Constructs request header:\n{\"token\": \"secret_value\"}\n```\n\n### `BasicAuthentication`\nThis authentication method enables specifying a username and password to APIs\nthat require such.\n```\nclient = ClientImplementation(\n authentication_method=BasicAuthentication(username=\"foo\", password=\"secret_value\"),\n response_handler=...,\n request_formatter=...,\n)\n```\n\n### `CookieAuthentication`\nThis authentication method allows a user to specify a url which is used\nto authenticate an initial request, made at APIClient initialization,\nwith the authorization tokens then persisted for the duration of the \nclient instance in cookie storage.\n\nThese cookies use the `http.cookiejar.CookieJar()` and are set on the\nsession so that all future requests contain these cookies.\n\nAs the method of authentication at the endpoint is not standardised\nacross API's, the authentication method can be customized using one of\nthe already defined authentication methods; `QueryParameterAuthentication`, \n`HeaderAuthentication`, `BasicAuthentication`.\n\n```\nclient = ClientImplementation(\n authentication_method=(\n CookieAuthentication(\n auth_url=\"https://example.com/authenticate\",\n authentication=HeaderAuthentication(\"1234-secret-key\"),\n ),\n response_handler=...,\n request_formatter=...,\n)\n```\n\n## Response Handlers\n\nResponse handlers provide a standard way of handling the final response\nfollowing a successful request to the API. These must inherit from\n`BaseResponseHandler` and implement the `get_request_data()` method which\nwill take the `requests.Response` object and parse the data accordingly.\n\nThe apiclient supports the following response handlers, by specifying\nthe class on initialization of the client as follows:\n\nThe response handler can be omitted, in which case no formatting is applied to the\noutgoing data.\n\n```\nclient = ClientImplementation(\n authentication_method=...,\n response_handler=,\n request_formatter=...,\n)\n```\n\n### `RequestsResponseHandler`\nHandler that simply returns the original `Response` object with no\nalteration.\n\nExample:\n```\nclient = ClientImplementation(\n authentication_method=...,\n response_handler=RequestsResponseHandler,\n request_formatter=...,\n)\n```\n\n### `JsonResponseHandler`\nHandler that parses the response data to `json` and returns the dictionary.\nIf an error occurs trying to parse to json then a `UnexpectedError`\nwill be raised.\n\nExample:\n```\nclient = ClientImplementation(\n authentication_method=...,\n response_handler=JsonResponseHandler,\n request_formatter=...,\n)\n```\n\n### `XmlResponseHandler`\nHandler that parses the response data to an `xml.etree.ElementTree.Element`.\nIf an error occurs trying to parse to xml then a `UnexpectedError`\nwill be raised.\n\nExample:\n```\nclient = ClientImplementation(\n authentication_method=...,\n response_handler=XmlResponseHandler,\n request_formatter=...,\n)\n```\n\n### `YamlResponseHandler`\nHandler that parses the response data in `yaml` format and returns the\ndictionary. If an error occurs trying to parse the yaml then an `UnexpectedError`\nwill be raised.\n\nExample:\n```\nclient = ClientImplementation(\n authentication_method=...,\n response_handler=YamlResponseHandler,\n request_formatter=...,\n)\n```\n\n## Request Formatters\n\nRequest formatters provide a way in which the outgoing request data can\nbe encoded before being sent, and to set the headers appropriately.\n\nThese must inherit from `BaseRequestFormatter` and implement the `format()`\nmethod which will take the outgoing `data` object and format accordingly\nbefore making the request.\n\nThe apiclient supports the following request formatters, by specifying\nthe class on initialization of the client as follows:\n\n```\nclient = ClientImplementation(\n authentication_method=...,\n response_handler=...,\n request_formatter=,\n)\n```\n\n### `JsonRequestFormatter`\n\nFormatter that converts the data into a json format and adds the\n`application/json` Content-type header to the outgoing requests.\n\n\nExample:\n```\nclient = ClientImplementation(\n authentication_method=...,\n response_handler=...,\n request_formatter=JsonRequestFormatter,\n)\n```\n\n## Exceptions\n\nThe exception handling for `api-client` has been designed in a way so that all exceptions inherit from\none base exception type: `APIClientError`. From there, the exceptions have been broken down into the\nfollowing categories:\n\n### `ResponseParseError`\n\nSomething went wrong when trying to parse the successful response into the defined format. This could be due\nto a misuse of the ResponseHandler, i.e. configuring the client with an `XmlResponseHandler` instead of\na `JsonResponseHandler`\n\n### `APIRequestError`\n\nSomething went wrong when making the request. These are broken down further into the following categories to provide\ngreater granularity and control.\n\n#### `RedirectionError`\nA redirection status code (3xx) was returned as a final code when making the\nrequest. This means that no data can be returned to the client as we could\nnot find the requested resource as it had moved.\n\n\n### `ClientError`\nA clienterror status code (4xx) was returned when contacting the API. The most common cause of\nthese errors is misuse of the client, i.e. sending bad data to the API.\n\n\n### `ServerError`\nThe API was unreachable when making the request. I.e. a 5xx status code.\n\n\n### `UnexpectedError`\nAn unexpected error occurred when using the client. This will typically happen when attempting\nto make the request, for example, the client never receives a response. It can also occur to\nunexpected status codes (>= 600).\n\n\n## Endpoints\n\nThe apiclient also provides a convenient way of defining url endpoints with\nuse of the `@endpoint` decorator. In order to decorate a class with `@endpoint`\nthe decorated class must define a `base_url` attribute along with the required\nresources. The decorator will combine the base_url with the resource.\n\nExample:\n\n```\nfrom apiclient import endpoint\n\n@endpoint(base_url=\"http://foo.com\")\nclass Endpoint:\n resource = \"search\"\n\n>>> Endpoint.resource\n\"http://foo.com/search\n```\n\n\n## Extended Example\n```\nfrom apiclient import APIClient, endpoint, paginated, retry_request\n\n\n# Define endpoints, using the provided decorator.\n@endpoint(base_url=\"https://jsonplaceholder.typicode.com\")\nclass Endpoint:\n todos = \"todos\"\n todo = \"todos/{id}\"\n\n\ndef get_next_page(response):\n return {\n \"limit\": response[\"limit\"],\n \"offset\": response[\"offset\"] + response[\"limit\"],\n }\n\n\n# Extend the client for your API integration.\nclass JSONPlaceholderClient(APIClient):\n\n @paginated(by_query_params=get_next_page)\n def get_all_todos(self) -> dict:\n return self.get(Endpoint.todos)\n\n @retry_request\n def get_todo(self, todo_id: int) -> dict:\n url = Endpoint.todo.format(id=todo_id)\n return self.get(url)\n\n\n# Initialize the client with the correct authentication method,\n# response handler and request formatter.\n>>> client = JSONPlaceholderClient(\n authentication_method=HeaderAuthentication(token=\"\"),\n response_handler=JsonResponseHandler,\n request_formatter=JsonRequestFormatter,\n)\n\n\n# Call the client methods.\n>>> client.get_all_todos()\n[\n {\n 'userId': 1,\n 'id': 1,\n 'title': 'delectus aut autem',\n 'completed': False\n },\n ...,\n {\n 'userId': 10,\n 'id': 200,\n 'title': 'ipsam aperiam voluptates qui',\n 'completed': False\n }\n]\n\n\n>>> client.get_todo(45)\n{\n 'userId': 3,\n 'id': 45,\n 'title': 'velit soluta adipisci molestias reiciendis harum',\n 'completed': False\n}\n\n\n# REST APIs correctly adhering to the status codes to provide meaningful\n# responses will raise the appropriate exeptions.\n>>> client.get_todo(450)\nNotFound: 404 Error: Not Found for url: https://jsonplaceholder.typicode.com/todos/450\n\n>>> try:\n... client.get_todo(450)\n... except APIClientError:\n... print(\"All client exceptions inherit from APIClientError\")\n\"All client exceptions inherit from APIClientError\"\n\n```\n\n## APIClient Interface\nThe `APIClient` provides the following public interface:\n* `post(self, endpoint: str, data: dict, params: OptionalDict = None)`\n\n Delegate to POST method to send data and return response from endpoint.\n\n* `get(endpoint: str, params: OptionalDict = None)`\n\n Delegate to GET method to get response from endpoint.\n\n* `put(endpoint: str, data: dict, params: OptionalDict = None)`\n\n Delegate to PUT method to send and overwrite data and return response from endpoint.\n\n* `patch(endpoint: str, data: dict, params: OptionalDict = None)`\n\n Delegate to PATCH method to send and update data and return response from endpoint\n\n* `delete(endpoint: str, params: OptionalDict = None)`\n\n Delegate to DELETE method to remove resource located at endpoint.\n\n* `get_request_timeout() -> float`\n\n By default, all requests have been set to have a default timeout of 10.0 s. This\n is to avoid the request waiting forever for a response, and is recommended\n to always be set to a value in production applications. It is however possible to\n override this method to return the timeout required by your application.", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/MikeWooster/api-client", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "api-client", "package_url": "https://pypi.org/project/api-client/", "platform": "", "project_url": "https://pypi.org/project/api-client/", "project_urls": { "Homepage": "https://github.com/MikeWooster/api-client" }, "release_url": "https://pypi.org/project/api-client/1.1.7/", "requires_dist": null, "requires_python": ">=3.6", "summary": "Separate the high level client implementation from the underlying CRUD.", "version": "1.1.7" }, "last_serial": 5868668, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "f2d382e6ad3aeee2eb382d393412559b", "sha256": "ad1208fbda5f5641e206084d6f94be8dacf200f1726fddb2fc9d98b2176e01c2" }, "downloads": -1, "filename": "api_client-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "f2d382e6ad3aeee2eb382d393412559b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 9961, "upload_time": "2019-02-11T20:37:50", "url": "https://files.pythonhosted.org/packages/a0/34/aa60f8d3c13d3f0080348330cf7d3342839868c5e6bf0a7c2a1a6e0b265f/api_client-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3da2bed4c615afd1c97bca170ae03d8d", "sha256": "3a8c934e180c1c810315c4614830d9d2b219f23e1bb7d2fbcc835154152e0adc" }, "downloads": -1, "filename": "api_client-0.1.0.tar.gz", "has_sig": false, "md5_digest": "3da2bed4c615afd1c97bca170ae03d8d", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 9095, "upload_time": "2019-02-11T20:37:53", "url": "https://files.pythonhosted.org/packages/19/36/58cc7a99b5a78b76632ef3dfe3d689d7cb1adce584dfb807cb8e20392811/api_client-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "4acabd81930f817a34a9ace983f4d71b", "sha256": "f4e6fab90b17484bdb41e7b9b5b31e0d8f47f0dda5add8cb0bea9f64ba312ffe" }, "downloads": -1, "filename": "api-client-0.1.1.tar.gz", "has_sig": false, "md5_digest": "4acabd81930f817a34a9ace983f4d71b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 9096, "upload_time": "2019-02-12T10:42:37", "url": "https://files.pythonhosted.org/packages/22/b8/b59ae01aabca2e9a6a38cd05b34747cec246cdf36d9db5b93890fca5c12e/api-client-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "cbc35928ad2c1fe5fc5dd7d8215899e3", "sha256": "ab8734ee44c3ce8268a2b2e601430cc4c113275347baf819620363d2455742e1" }, "downloads": -1, "filename": "api-client-0.1.2.tar.gz", "has_sig": false, "md5_digest": "cbc35928ad2c1fe5fc5dd7d8215899e3", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 9075, "upload_time": "2019-02-12T10:46:47", "url": "https://files.pythonhosted.org/packages/60/bc/6e8d900a02d0e962c83ed0dbe30c2a42fc73dfd2453023781e9868e80fcb/api-client-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "8849bd2941b59e29d0f3b005e96d2cc7", "sha256": "29add1c5cf8ab4d6633ab1e83cde21c4cdb9f54900e14902b2e005ce0995dcac" }, "downloads": -1, "filename": "api-client-0.1.3.tar.gz", "has_sig": false, "md5_digest": "8849bd2941b59e29d0f3b005e96d2cc7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 9257, "upload_time": "2019-02-12T17:09:46", "url": "https://files.pythonhosted.org/packages/43/02/b9859c454445bba477b4a401a23284870f12c0ca10d4fe89e8fe5251b724/api-client-0.1.3.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "0dab09c91a19efb8d176e4714c3bc1b6", "sha256": "e9f8422c47f7794e6878a9ad1c9e3417f535b7d0b66614c8ef0b981361603db2" }, "downloads": -1, "filename": "api-client-0.2.0.tar.gz", "has_sig": false, "md5_digest": "0dab09c91a19efb8d176e4714c3bc1b6", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 9252, "upload_time": "2019-02-18T13:28:20", "url": "https://files.pythonhosted.org/packages/7e/00/858fef0e93fb36cb9facc34d489b1bc1de7403f7ec3a47aaeabc9f21da58/api-client-0.2.0.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "a72ab23864eac5b64b168fcc1ae1493c", "sha256": "6024dd9ac292220ac696fcc14819bcc0c09e8afd9b14ba2e91fc96526712345f" }, "downloads": -1, "filename": "api-client-1.0.0.tar.gz", "has_sig": false, "md5_digest": "a72ab23864eac5b64b168fcc1ae1493c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 13356, "upload_time": "2019-03-03T17:12:26", "url": "https://files.pythonhosted.org/packages/ec/0f/6efe718e7be86e09cbafdea3544932f3fbdf3643e824171fb71451f0c58b/api-client-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "b4f9ede41914f0c15a132e7a2b392c3b", "sha256": "a3bc504a12860f2a73d830077bb46310f435134d1a2cce56d475239ebe6bf01d" }, "downloads": -1, "filename": "api-client-1.0.1.tar.gz", "has_sig": false, "md5_digest": "b4f9ede41914f0c15a132e7a2b392c3b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 14277, "upload_time": "2019-03-09T15:18:55", "url": "https://files.pythonhosted.org/packages/78/27/19aeb81051c0438407ce57cdf3d4e10f9e5d22c71b2d965548249b657f56/api-client-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "90b0ce5330e43e67f8fdfcb7e0afed35", "sha256": "c60dd433a164a3fb1928dd2ca31def3f1d317dbd113bcea801b5af01f3e4d0e1" }, "downloads": -1, "filename": "api-client-1.0.2.tar.gz", "has_sig": false, "md5_digest": "90b0ce5330e43e67f8fdfcb7e0afed35", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 14592, "upload_time": "2019-03-17T16:24:18", "url": "https://files.pythonhosted.org/packages/d6/6d/d027e7dfcbea1abdf6b58f27edfd37e1fc0eec9dcfb2474567b267eb25d9/api-client-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "b0827ba696c5a8c336b91b4d645248e4", "sha256": "c91cc22877f2c54b64b9829a6e0553a53b5191f9094adf778162d6466f54973b" }, "downloads": -1, "filename": "api-client-1.0.3.tar.gz", "has_sig": false, "md5_digest": "b0827ba696c5a8c336b91b4d645248e4", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 14574, "upload_time": "2019-03-17T16:33:06", "url": "https://files.pythonhosted.org/packages/0f/02/e430f647319e22cc818034698ce344c0662b86ea3055e245ce6379d88115/api-client-1.0.3.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "c672b8fb3879fa6fa6d61d17ee77aecc", "sha256": "e55742ce4c1f19380094f85262419f94ba7ad9151cbeee6ab4dd062b2988ecad" }, "downloads": -1, "filename": "api-client-1.1.0.tar.gz", "has_sig": false, "md5_digest": "c672b8fb3879fa6fa6d61d17ee77aecc", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 15909, "upload_time": "2019-03-25T20:19:47", "url": "https://files.pythonhosted.org/packages/6e/6d/1d79c081ed8ebff944055c000b862a69fb30b9b575f3239dd31fafb116cc/api-client-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "8901f3087f6088fe1647016ce14fb297", "sha256": "15708d6648c8815047053f7c1edf540cf2df78ba7bf232dfa28173f76ba35bf2" }, "downloads": -1, "filename": "api-client-1.1.1.tar.gz", "has_sig": false, "md5_digest": "8901f3087f6088fe1647016ce14fb297", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 16199, "upload_time": "2019-04-06T16:08:48", "url": "https://files.pythonhosted.org/packages/28/8c/d525939ad6de57eb76cbda8a08846031c5fb68694861713eec8e9b7a7685/api-client-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "ca72f3fdcc177171e99a1039e91978b8", "sha256": "1cb2473e691b52a9c864eae58a32045dce836991e5f58bf0a79bc4e092b5079a" }, "downloads": -1, "filename": "api-client-1.1.2.tar.gz", "has_sig": false, "md5_digest": "ca72f3fdcc177171e99a1039e91978b8", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 17578, "upload_time": "2019-05-19T15:09:34", "url": "https://files.pythonhosted.org/packages/8a/b1/245c5aba3199efa7192dc0c72a2398c5de4487b334ef4c2ef94e2cb2b096/api-client-1.1.2.tar.gz" } ], "1.1.3": [ { "comment_text": "", "digests": { "md5": "309eb810c495574a830a0f7feecccf56", "sha256": "3b70271e134d170cf6cea3898a128a75e94895c9287241473b46359748f1844f" }, "downloads": -1, "filename": "api-client-1.1.3.tar.gz", "has_sig": false, "md5_digest": "309eb810c495574a830a0f7feecccf56", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 17752, "upload_time": "2019-07-29T13:42:03", "url": "https://files.pythonhosted.org/packages/10/7a/7d8cb3c506b8a168652b3349e679081a128e8b1008743306bb894e9f278a/api-client-1.1.3.tar.gz" } ], "1.1.4": [ { "comment_text": "", "digests": { "md5": "a38f38774a25be1acf8f404255fb1149", "sha256": "f798d8c364c04f9dd93b68db894b776618d8e550d3c0baf7a824e7a5e7bcad8e" }, "downloads": -1, "filename": "api-client-1.1.4.tar.gz", "has_sig": false, "md5_digest": "a38f38774a25be1acf8f404255fb1149", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 15007, "upload_time": "2019-08-08T11:53:44", "url": "https://files.pythonhosted.org/packages/7c/e7/2ab9e18914fb61a107c91b4f01eb214190ee4255c3322bc858cfa7b59ccf/api-client-1.1.4.tar.gz" } ], "1.1.5": [ { "comment_text": "", "digests": { "md5": "9f25313d7934761ca67a25158876bafb", "sha256": "82d0c19ade54338108f0bc8cf69a83153b08922733ac52c2774a2ff6ad5c5046" }, "downloads": -1, "filename": "api-client-1.1.5.tar.gz", "has_sig": false, "md5_digest": "9f25313d7934761ca67a25158876bafb", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 15465, "upload_time": "2019-08-12T08:57:27", "url": "https://files.pythonhosted.org/packages/e5/9d/65c12b590bf50c9afb33f392f20743877a578d544b1bcc5c8eba994e9df5/api-client-1.1.5.tar.gz" } ], "1.1.6": [ { "comment_text": "", "digests": { "md5": "caa6a96f84c211bb00ccd72acdf38fb8", "sha256": "b70be118307878cb08a10a6137157bb663d8ed9c68b502a74017a6496ee36d06" }, "downloads": -1, "filename": "api-client-1.1.6.tar.gz", "has_sig": false, "md5_digest": "caa6a96f84c211bb00ccd72acdf38fb8", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 15912, "upload_time": "2019-09-05T13:12:32", "url": "https://files.pythonhosted.org/packages/6d/9d/1ff94c0b404d6192fb5c91f68b113ff2610667bd263b22e40eb4f087c638/api-client-1.1.6.tar.gz" } ], "1.1.7": [ { "comment_text": "", "digests": { "md5": "5094198d4504f1729996f6b2410f1527", "sha256": "6becd76f924c1c793a366999ca93d842f01d2aa48a89681fdc75c50386a0aaea" }, "downloads": -1, "filename": "api-client-1.1.7.tar.gz", "has_sig": false, "md5_digest": "5094198d4504f1729996f6b2410f1527", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 16642, "upload_time": "2019-09-22T10:40:30", "url": "https://files.pythonhosted.org/packages/4b/f8/69f726c769b7e01fe52b871e6c361fd275651e5d6704d1be269445295330/api-client-1.1.7.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "5094198d4504f1729996f6b2410f1527", "sha256": "6becd76f924c1c793a366999ca93d842f01d2aa48a89681fdc75c50386a0aaea" }, "downloads": -1, "filename": "api-client-1.1.7.tar.gz", "has_sig": false, "md5_digest": "5094198d4504f1729996f6b2410f1527", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 16642, "upload_time": "2019-09-22T10:40:30", "url": "https://files.pythonhosted.org/packages/4b/f8/69f726c769b7e01fe52b871e6c361fd275651e5d6704d1be269445295330/api-client-1.1.7.tar.gz" } ] }