<!--- Top of README Badges (automated) --->
[![PyPI](https://img.shields.io/pypi/v/wipac-rest-tools)](https://pypi.org/project/wipac-rest-tools/) [![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/WIPACrepo/rest-tools?include_prereleases)](https://github.com/WIPACrepo/rest-tools/) [![PyPI - License](https://img.shields.io/pypi/l/wipac-rest-tools)](https://github.com/WIPACrepo/rest-tools/blob/master/LICENSE) [![Lines of code](https://img.shields.io/tokei/lines/github/WIPACrepo/rest-tools)](https://github.com/WIPACrepo/rest-tools/) [![GitHub issues](https://img.shields.io/github/issues/WIPACrepo/rest-tools)](https://github.com/WIPACrepo/rest-tools/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aopen) [![GitHub pull requests](https://img.shields.io/github/issues-pr/WIPACrepo/rest-tools)](https://github.com/WIPACrepo/rest-tools/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Aopen)
<!--- End of README Badges (automated) --->
# rest-tools
This project contains REST tools in python, as common code for multiple other
projects under https://github.com/WIPACrepo.
All code uses python [asyncio](https://docs.python.org/3/library/asyncio.html),
so is fully asyncronous.
Note that both the client and server assume starting the asyncio loop
happens elsewhere - they do not start the loop themselves.
## Client
A REST API client exists under `rest_tools.client`. Use as:
```python
from rest_tools.client import RestClient
api = RestClient('http://my.site.here/api', token='XXXX')
ret = await api.request('GET', '/fruits/apple')
ret = await api.request('POST', '/fruits', {'name': 'banana'})
```
There are several variations of the client for OAuth2/OpenID support:
* [`OpenIDRestClient`](rest_tools/client/openid_client.py#L19) : A child of
`RestClient` that supports OAuth2 token refresh using the OpenID Connect
Discovery protocol for an authentication server.
* [`ClientCredentialsAuth`](rest_tools/client/client_credentials.py#L11) : Uses
`OpenIDRestClient` in combination with OAuth2 client credentials (client ID
and secret) for service-based auth. Use this for long-lived services that
need to perform REST API calls.
* [`DeviceGrantAuth`](rest_tools/client/device_client.py#L125) /
[`SavedDeviceGrantAuth`](rest_tools/client/device_client.py#L162) : Uses
`OpenIDRestClient` to perform a "device" login for a user. Use this for
user-based terminal applications that need to perform REST API calls.
The `SavedDeviceGrantAuth` can save the refresh token to disk, allowing
repeated application sessions without having to log in again.
## Server
A REST API server exists under `rest_tools.server`. Use as:
```python
import asyncio
from rest_tools.server import RestServer, RestHandler
class Fruits(RestHandler):
def post(self):
# handle a new fruit
self.write({})
server = RestServer()
server.add_route('/fruits', Fruits)
server.startup(address='my.site.here', port=8080)
asyncio.get_event_loop().run_forever()
```
The server uses [Tornado](https://tornado.readthedocs.io) to handle HTTP
connections. It is recommended to use Apache or Nginx as a front-facing proxy,
to handle TLS sessions and non-standard HTTP requests in production.
### Handling Arguments Server-side
`server.ArgumentHandler` is a robust wrapper around `argparse.ArgumentParser`, extended for use in handling REST arguments, both query arguments and JSON-encoded body arguments. The intended design of this class is to follow the `argparse` pattern as closely as possible.
```python
from rest_tools.server import RestHandler, ArgumentHandler, ArgumentSource
class Fruits(RestHandler):
def get(self):
argo = ArgumentHandler(ArgumentSource.QUERY_ARGUMENTS, self)
argo.add_argument('name', type=str) # de-facto required
argo.add_argument('alias', dest='other_names', type=str, nargs='*', default=[]) # list
argo.add_argument('is-citrus', type=bool, default=False)
argo.add_argument('amount', type=float, required=True)
args = argo.parse_args()
fruit = get_fruit(args.name, args.other_names, args.is_citrus, args.amount)
...
def post(self):
argo = ArgumentHandler(ArgumentSource.JSON_BODY_ARGUMENTS, self)
argo.add_argument('name', type=str) # de-facto required
argo.add_argument('other-names', type=list, default=[])
argo.add_argument('supply', type=dict, required=True)
def _origin(val):
try:
return {'USA': 'United States of America', 'MEX': 'Mexico'}[val]
except KeyError:
# raise a ValueError or TypeError to propagate a 400 Error
raise ValueError('Invalid origin')
argo.add_argument('country_code', dest='origin', type=_origin, required=True)
args = argo.parse_args()
add_to_basket(args.name, args.other_names, args.supply, args.origin)
...
```
Raw data
{
"_id": null,
"home_page": "https://github.com/WIPACrepo/rest-tools",
"name": "wipac-rest-tools",
"maintainer": null,
"docs_url": null,
"requires_python": "<3.14,>=3.8",
"maintainer_email": null,
"keywords": "python, REST, tools, utilities, OpenTelemetry, tracing, telemetry, WIPAC, IceCube",
"author": "WIPAC Developers",
"author_email": "developers@icecube.wisc.edu",
"download_url": "https://files.pythonhosted.org/packages/87/32/bdf8db7a9db90d786ffeebb74a33727691a844f0b2193e3627b8ee720913/wipac-rest-tools-1.8.2.tar.gz",
"platform": null,
"description": "<!--- Top of README Badges (automated) --->\n[![PyPI](https://img.shields.io/pypi/v/wipac-rest-tools)](https://pypi.org/project/wipac-rest-tools/) [![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/WIPACrepo/rest-tools?include_prereleases)](https://github.com/WIPACrepo/rest-tools/) [![PyPI - License](https://img.shields.io/pypi/l/wipac-rest-tools)](https://github.com/WIPACrepo/rest-tools/blob/master/LICENSE) [![Lines of code](https://img.shields.io/tokei/lines/github/WIPACrepo/rest-tools)](https://github.com/WIPACrepo/rest-tools/) [![GitHub issues](https://img.shields.io/github/issues/WIPACrepo/rest-tools)](https://github.com/WIPACrepo/rest-tools/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aopen) [![GitHub pull requests](https://img.shields.io/github/issues-pr/WIPACrepo/rest-tools)](https://github.com/WIPACrepo/rest-tools/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Aopen) \n<!--- End of README Badges (automated) --->\n# rest-tools\n\nThis project contains REST tools in python, as common code for multiple other\nprojects under https://github.com/WIPACrepo.\n\nAll code uses python [asyncio](https://docs.python.org/3/library/asyncio.html),\nso is fully asyncronous.\n\nNote that both the client and server assume starting the asyncio loop\nhappens elsewhere - they do not start the loop themselves.\n\n## Client\n\nA REST API client exists under `rest_tools.client`. Use as:\n\n```python\nfrom rest_tools.client import RestClient\n\napi = RestClient('http://my.site.here/api', token='XXXX')\nret = await api.request('GET', '/fruits/apple')\nret = await api.request('POST', '/fruits', {'name': 'banana'})\n```\n\nThere are several variations of the client for OAuth2/OpenID support:\n\n* [`OpenIDRestClient`](rest_tools/client/openid_client.py#L19) : A child of\n `RestClient` that supports OAuth2 token refresh using the OpenID Connect\n Discovery protocol for an authentication server.\n\n* [`ClientCredentialsAuth`](rest_tools/client/client_credentials.py#L11) : Uses\n `OpenIDRestClient` in combination with OAuth2 client credentials (client ID\n and secret) for service-based auth. Use this for long-lived services that\n need to perform REST API calls.\n\n* [`DeviceGrantAuth`](rest_tools/client/device_client.py#L125) /\n [`SavedDeviceGrantAuth`](rest_tools/client/device_client.py#L162) : Uses\n `OpenIDRestClient` to perform a \"device\" login for a user. Use this for\n user-based terminal applications that need to perform REST API calls.\n The `SavedDeviceGrantAuth` can save the refresh token to disk, allowing\n repeated application sessions without having to log in again.\n\n## Server\n\nA REST API server exists under `rest_tools.server`. Use as:\n\n```python\nimport asyncio\nfrom rest_tools.server import RestServer, RestHandler\n\nclass Fruits(RestHandler):\n def post(self):\n # handle a new fruit\n self.write({})\n\nserver = RestServer()\nserver.add_route('/fruits', Fruits)\nserver.startup(address='my.site.here', port=8080)\nasyncio.get_event_loop().run_forever()\n```\n\nThe server uses [Tornado](https://tornado.readthedocs.io) to handle HTTP\nconnections. It is recommended to use Apache or Nginx as a front-facing proxy,\nto handle TLS sessions and non-standard HTTP requests in production.\n\n### Handling Arguments Server-side\n\n`server.ArgumentHandler` is a robust wrapper around `argparse.ArgumentParser`, extended for use in handling REST arguments, both query arguments and JSON-encoded body arguments. The intended design of this class is to follow the `argparse` pattern as closely as possible.\n\n\n```python\nfrom rest_tools.server import RestHandler, ArgumentHandler, ArgumentSource\n\nclass Fruits(RestHandler):\n\n def get(self):\n argo = ArgumentHandler(ArgumentSource.QUERY_ARGUMENTS, self)\n\n argo.add_argument('name', type=str) # de-facto required\n argo.add_argument('alias', dest='other_names', type=str, nargs='*', default=[]) # list\n\n argo.add_argument('is-citrus', type=bool, default=False)\n argo.add_argument('amount', type=float, required=True)\n\n args = argo.parse_args()\n\n fruit = get_fruit(args.name, args.other_names, args.is_citrus, args.amount)\n\n ...\n\n def post(self):\n argo = ArgumentHandler(ArgumentSource.JSON_BODY_ARGUMENTS, self)\n\n argo.add_argument('name', type=str) # de-facto required\n argo.add_argument('other-names', type=list, default=[])\n\n argo.add_argument('supply', type=dict, required=True)\n\n def _origin(val):\n try:\n return {'USA': 'United States of America', 'MEX': 'Mexico'}[val]\n except KeyError:\n # raise a ValueError or TypeError to propagate a 400 Error\n raise ValueError('Invalid origin')\n\n argo.add_argument('country_code', dest='origin', type=_origin, required=True)\n\n args = argo.parse_args()\n\n add_to_basket(args.name, args.other_names, args.supply, args.origin)\n\n ...\n\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "REST tools in python - common code for client and server",
"version": "1.8.2",
"project_urls": {
"Download": "https://pypi.org/project/wipac-rest-tools/",
"Homepage": "https://github.com/WIPACrepo/rest-tools",
"Source": "https://github.com/WIPACrepo/rest-tools",
"Tracker": "https://github.com/WIPACrepo/rest-tools/issues"
},
"split_keywords": [
"python",
" rest",
" tools",
" utilities",
" opentelemetry",
" tracing",
" telemetry",
" wipac",
" icecube"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "76661b50eed263aeb55e3af49449fd80fd401b41fa4655d3a6314c5d4c758057",
"md5": "03eb6cf4f9d622be8d2e97e4f81c796a",
"sha256": "70990ebf8d1be6e2c17278d116f7ea2f7fa37f0034b144dc0e2fd1ce0acb057e"
},
"downloads": -1,
"filename": "wipac_rest_tools-1.8.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "03eb6cf4f9d622be8d2e97e4f81c796a",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<3.14,>=3.8",
"size": 38100,
"upload_time": "2024-10-18T21:24:51",
"upload_time_iso_8601": "2024-10-18T21:24:51.777939Z",
"url": "https://files.pythonhosted.org/packages/76/66/1b50eed263aeb55e3af49449fd80fd401b41fa4655d3a6314c5d4c758057/wipac_rest_tools-1.8.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "8732bdf8db7a9db90d786ffeebb74a33727691a844f0b2193e3627b8ee720913",
"md5": "1cc1bf488c249e532d9bdbe850301a18",
"sha256": "f21221565d06db9aaacb2cea0056ac2b0a79c5feefe2ff67db82898590fec850"
},
"downloads": -1,
"filename": "wipac-rest-tools-1.8.2.tar.gz",
"has_sig": false,
"md5_digest": "1cc1bf488c249e532d9bdbe850301a18",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<3.14,>=3.8",
"size": 32696,
"upload_time": "2024-10-18T21:24:52",
"upload_time_iso_8601": "2024-10-18T21:24:52.922232Z",
"url": "https://files.pythonhosted.org/packages/87/32/bdf8db7a9db90d786ffeebb74a33727691a844f0b2193e3627b8ee720913/wipac-rest-tools-1.8.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-18 21:24:52",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "WIPACrepo",
"github_project": "rest-tools",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "wipac-rest-tools"
}