# wsgirouter3
Small opinionated WSGI request dispatcher. Influenced by Flask.
Works using path segments instead of more common regex matching (RFC 3986 path segment parameters are not supported) https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
Path variables are by default defined using RFC 6570 level 1 https://datatracker.ietf.org/doc/html/rfc6570#section-1.2 Start and optional end markers are customizable.
Path variable types are defined using python typing information. Customizable, types supported out-of-box: bool, int, str, uuid.UUID.
WSGI environ wrapper can passed to handler if there is parameter with type Request. No global variables/threadlocals.
Request body and query string binding is also supported, using generic types Body and Query.
Supports overlapping path segments: zero or more literal segments can overlap with one parameter definition. Parameters of different type and/or name in same position are not supported. Literal segment takes precedence.
Route decorators for HTTP methods DELETE, GET, PATCH, POST, PUT.
Response compression. By default enabled for application/json. Configurable compression level.
```python
@router.get('/abc/literal')
def literal():
pass
@router.get('/abc/{variable}')
def parameterized(variable: str):
pass
```
Multiple routes can point to same handler:
```python
@router.get('/abc', defaults={'variable': None})
@router.get('/abc/{variable}')
def with_optional_parameter(variable: Optional[str]):
pass
```
Content negotiation:
```python
@router.get('/get', produces='application/json')
def get() -> dict:
return {'field': 'value'}
@router.post('/post', consumes='application/json')
def post_with_json(req: Request) -> Tuple[int]:
data = req.json
return 204,
```
Query string and request body binding:
```python
@router.get('/get', produces='application/json')
def get(query: Query[dict]) -> dict:
return query
@router.post('/post', consumes='application/json')
def post_with_json(data: Body[dict]) -> Tuple[int]:
# do something with data
return 204,
```
Handler return type handling:
| Type | Description |
| ---- | ----------- |
| tuple | shortcut for returning status code and optional result + headers |
| None | allowed for status codes which have no content |
| dict | application/json |
| str | defined by optional Content-Type header. When header is missing, taken from config.default_str_content_type, by default text/plain;charset=utf-8 |
| bytes | defined by required Content-Type header |
| dataclass | application/json, but overridable by custom result handler |
| typing.GeneratorType | passed as is |
Cache control for responses:
```python
@router.get('/no-store', cache_control=CacheControl.no_store)
def no_store() -> dict:
return {'a': 1}
@router.get('/store', cache_control=CacheControl.of(max_age=600, private=False))
def store() -> dict:
return {'a': 1}
```
## Configuration checklist
WsgiAppConfig class
| Task | Action |
| ----------- | ----------- |
| Want to use another json library or configure value types serialized / deserialized | Override json_serializer / json_deserializer |
| Change maximum length of accepted request body | Set value of max_request_content_length |
| Change default content type for str returned | Change value of default_str_content_type |
| Add authorization | Set before_request hook handler, use route options to define roles. See [sample](https://github.com/andruskutt/wsgirouter3/tree/main/examples/authorization/application.py) |
| Handle more return types | Add entry of tuple[matcher, handler] to result_converters or override custom_result_handler |
| Validate/convert query string and request body | Use Query and Body generics with value class in handler and override binder |
| Customize error handling | Override error_handler |
| Disable response compression | set compress_level to 0 |
| Enable response compression for more cases | Update compress_content_types and/or override can_compress_result |
| Configure compression level | Set value of compress_level (0-9 or -1, see zlib documentation) |
PathRouter class
| Task | Action |
| ----------- | ----------- |
| Change parameter markers | Change value of path_parameter_start and path_parameter_end |
| Add new path parameter type | Add new class inherited from PathParameter into parameter_types |
## Installation
```shell
$ pip install wsgirouter3
```
Raw data
{
"_id": null,
"home_page": "https://github.com/andruskutt/wsgirouter3",
"name": "wsgirouter3",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "",
"keywords": "web services",
"author": "Andrus K\u00fctt",
"author_email": "andrus.kuett@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/f7/c6/93c27287cfb554b1c2e45f8b36ba0312d3f1dde234598dfd800bfdfed7e9/wsgirouter3-0.8.4.tar.gz",
"platform": null,
"description": "# wsgirouter3\n\nSmall opinionated WSGI request dispatcher. Influenced by Flask.\n\nWorks using path segments instead of more common regex matching (RFC 3986 path segment parameters are not supported) https://datatracker.ietf.org/doc/html/rfc3986#section-3.3\n\nPath variables are by default defined using RFC 6570 level 1 https://datatracker.ietf.org/doc/html/rfc6570#section-1.2 Start and optional end markers are customizable.\nPath variable types are defined using python typing information. Customizable, types supported out-of-box: bool, int, str, uuid.UUID.\n\nWSGI environ wrapper can passed to handler if there is parameter with type Request. No global variables/threadlocals.\nRequest body and query string binding is also supported, using generic types Body and Query.\n\nSupports overlapping path segments: zero or more literal segments can overlap with one parameter definition. Parameters of different type and/or name in same position are not supported. Literal segment takes precedence.\n\nRoute decorators for HTTP methods DELETE, GET, PATCH, POST, PUT.\n\nResponse compression. By default enabled for application/json. Configurable compression level.\n\n\n```python\n@router.get('/abc/literal')\ndef literal():\n pass\n\n@router.get('/abc/{variable}')\ndef parameterized(variable: str):\n pass\n```\n\nMultiple routes can point to same handler:\n\n```python\n@router.get('/abc', defaults={'variable': None})\n@router.get('/abc/{variable}')\ndef with_optional_parameter(variable: Optional[str]):\n pass\n```\n\nContent negotiation:\n\n```python\n@router.get('/get', produces='application/json')\ndef get() -> dict:\n return {'field': 'value'}\n\n@router.post('/post', consumes='application/json')\ndef post_with_json(req: Request) -> Tuple[int]:\n data = req.json\n return 204,\n```\n\nQuery string and request body binding:\n\n```python\n@router.get('/get', produces='application/json')\ndef get(query: Query[dict]) -> dict:\n return query\n\n@router.post('/post', consumes='application/json')\ndef post_with_json(data: Body[dict]) -> Tuple[int]:\n # do something with data\n return 204,\n```\n\nHandler return type handling:\n\n| Type | Description |\n| ---- | ----------- |\n| tuple | shortcut for returning status code and optional result + headers |\n| None | allowed for status codes which have no content |\n| dict | application/json |\n| str | defined by optional Content-Type header. When header is missing, taken from config.default_str_content_type, by default text/plain;charset=utf-8 |\n| bytes | defined by required Content-Type header |\n| dataclass | application/json, but overridable by custom result handler |\n| typing.GeneratorType | passed as is |\n\nCache control for responses:\n\n```python\n@router.get('/no-store', cache_control=CacheControl.no_store)\ndef no_store() -> dict:\n return {'a': 1}\n\n@router.get('/store', cache_control=CacheControl.of(max_age=600, private=False))\ndef store() -> dict:\n return {'a': 1}\n```\n\n## Configuration checklist\n\nWsgiAppConfig class\n\n| Task | Action |\n| ----------- | ----------- |\n| Want to use another json library or configure value types serialized / deserialized | Override json_serializer / json_deserializer |\n| Change maximum length of accepted request body | Set value of max_request_content_length |\n| Change default content type for str returned | Change value of default_str_content_type |\n| Add authorization | Set before_request hook handler, use route options to define roles. See [sample](https://github.com/andruskutt/wsgirouter3/tree/main/examples/authorization/application.py) |\n| Handle more return types | Add entry of tuple[matcher, handler] to result_converters or override custom_result_handler |\n| Validate/convert query string and request body | Use Query and Body generics with value class in handler and override binder |\n| Customize error handling | Override error_handler |\n| Disable response compression | set compress_level to 0 |\n| Enable response compression for more cases | Update compress_content_types and/or override can_compress_result |\n| Configure compression level | Set value of compress_level (0-9 or -1, see zlib documentation) |\n\nPathRouter class\n\n| Task | Action |\n| ----------- | ----------- |\n| Change parameter markers | Change value of path_parameter_start and path_parameter_end |\n| Add new path parameter type | Add new class inherited from PathParameter into parameter_types |\n\n## Installation\n\n```shell\n$ pip install wsgirouter3\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "WSGI routing library",
"version": "0.8.4",
"split_keywords": [
"web",
"services"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "59971d2811fd5367e4828c9cfe968318c9d4613d58e08e0a7417d5fc9e00aa2a",
"md5": "ec235c08317fdf354ffab3972094ef0e",
"sha256": "1e296c8b03b9a85360be193cb325cc5f74ed179f80198552304c59f89c6a0de2"
},
"downloads": -1,
"filename": "wsgirouter3-0.8.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "ec235c08317fdf354ffab3972094ef0e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 13122,
"upload_time": "2023-01-07T14:39:09",
"upload_time_iso_8601": "2023-01-07T14:39:09.413095Z",
"url": "https://files.pythonhosted.org/packages/59/97/1d2811fd5367e4828c9cfe968318c9d4613d58e08e0a7417d5fc9e00aa2a/wsgirouter3-0.8.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "f7c693c27287cfb554b1c2e45f8b36ba0312d3f1dde234598dfd800bfdfed7e9",
"md5": "803fbcd9359cf0cd813430d8f5db1b81",
"sha256": "fe14cf5a83ab6153d98c5cc7008b7c0a8c4982119d2fa12c0a7952b21db15f13"
},
"downloads": -1,
"filename": "wsgirouter3-0.8.4.tar.gz",
"has_sig": false,
"md5_digest": "803fbcd9359cf0cd813430d8f5db1b81",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 12709,
"upload_time": "2023-01-07T14:39:11",
"upload_time_iso_8601": "2023-01-07T14:39:11.011401Z",
"url": "https://files.pythonhosted.org/packages/f7/c6/93c27287cfb554b1c2e45f8b36ba0312d3f1dde234598dfd800bfdfed7e9/wsgirouter3-0.8.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-01-07 14:39:11",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "andruskutt",
"github_project": "wsgirouter3",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"tox": true,
"lcname": "wsgirouter3"
}