wipac-rest-tools


Namewipac-rest-tools JSON
Version 1.11.1 PyPI version JSON
download
home_pageNone
SummaryREST tools in python - common code for client and server
upload_time2025-08-14 22:26:00
maintainerNone
docs_urlNone
authorNone
requires_python<3.14,>=3.9
licenseNone
keywords wipac icecube rest tools utilities opentelemetry tracing telemetry
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <!--- 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/) [![Versions](https://img.shields.io/pypi/pyversions/wipac-rest-tools.svg)](https://pypi.org/project/wipac-rest-tools) [![PyPI - License](https://img.shields.io/pypi/l/wipac-rest-tools)](https://github.com/WIPACrepo/rest-tools/blob/master/LICENSE) [![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": null,
    "name": "wipac-rest-tools",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<3.14,>=3.9",
    "maintainer_email": null,
    "keywords": "WIPAC, IceCube, REST, tools, utilities, OpenTelemetry, tracing, telemetry",
    "author": null,
    "author_email": "WIPAC Developers <developers@icecube.wisc.edu>",
    "download_url": "https://files.pythonhosted.org/packages/68/0c/2258bf259efb6843b81aaef820af00556f3fcb1d58e0627d01e805bcede1/wipac_rest_tools-1.11.1.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/) [![Versions](https://img.shields.io/pypi/pyversions/wipac-rest-tools.svg)](https://pypi.org/project/wipac-rest-tools) [![PyPI - License](https://img.shields.io/pypi/l/wipac-rest-tools)](https://github.com/WIPACrepo/rest-tools/blob/master/LICENSE) [![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": null,
    "summary": "REST tools in python - common code for client and server",
    "version": "1.11.1",
    "project_urls": {
        "Homepage": "https://pypi.org/project/wipac-rest-tools/",
        "Source": "https://github.com/WIPACrepo/rest-tools",
        "Tracker": "https://github.com/WIPACrepo/rest-tools/issues"
    },
    "split_keywords": [
        "wipac",
        " icecube",
        " rest",
        " tools",
        " utilities",
        " opentelemetry",
        " tracing",
        " telemetry"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "acb66c320f68435b2f547cc55671d4480fe49aa73fee89d773bea78d83a99d78",
                "md5": "58f6e8b20eb93991fee97debcfb03b77",
                "sha256": "fab491cb3afbfaf9e5031876c845bb7b9d008b411a548309e5879e176ac072ea"
            },
            "downloads": -1,
            "filename": "wipac_rest_tools-1.11.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "58f6e8b20eb93991fee97debcfb03b77",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<3.14,>=3.9",
            "size": 39310,
            "upload_time": "2025-08-14T22:25:59",
            "upload_time_iso_8601": "2025-08-14T22:25:59.735019Z",
            "url": "https://files.pythonhosted.org/packages/ac/b6/6c320f68435b2f547cc55671d4480fe49aa73fee89d773bea78d83a99d78/wipac_rest_tools-1.11.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "680c2258bf259efb6843b81aaef820af00556f3fcb1d58e0627d01e805bcede1",
                "md5": "9bc0e88fe23c37ee615a180abc0ee26b",
                "sha256": "a514bb384425ee98408d23abc21d3854172f6c4ab246cb722d0f6668d40614ef"
            },
            "downloads": -1,
            "filename": "wipac_rest_tools-1.11.1.tar.gz",
            "has_sig": false,
            "md5_digest": "9bc0e88fe23c37ee615a180abc0ee26b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<3.14,>=3.9",
            "size": 68278,
            "upload_time": "2025-08-14T22:26:00",
            "upload_time_iso_8601": "2025-08-14T22:26:00.715046Z",
            "url": "https://files.pythonhosted.org/packages/68/0c/2258bf259efb6843b81aaef820af00556f3fcb1d58e0627d01e805bcede1/wipac_rest_tools-1.11.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-14 22:26:00",
    "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"
}
        
Elapsed time: 4.00355s