Skip to content

Commit

Permalink
Support msgspec alongside Pydantic
Browse files Browse the repository at this point in the history
This allos the msgspec library to be used for validation and data
structures. This should make quart-schema more useful.

This requires a choice to be made at install time either pydantic or
msgspec or both with a preference set in the config. Otherwise it
should be backwards compatible.
  • Loading branch information
pgjones committed Jan 22, 2024
1 parent 40edaeb commit 791183e
Show file tree
Hide file tree
Showing 31 changed files with 1,128 additions and 374 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ jobs:
pip install -U setuptools
python -m pip install -U pip
- run: pip install openapi-spec-validator .
- run: pip install openapi-spec-validator pydantic .

- run: cd compliance && QUART_APP=todo:app quart schema | openapi-spec-validator -
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Quart-Schema is a Quart extension that provides schema validation and
auto-generated API documentation. This is particularly useful when
writing RESTful APIs.

Quart-Schema can use either `msgspec
<https://jcristharif.com/msgspec>`_ or `pydantic
<https://docs.pydantic.dev>`_ to validate.

Quickstart
----------

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx_tabs.tabs']

source_suffix = '.rst'

Expand Down
46 changes: 0 additions & 46 deletions docs/discussion/dataclass_or_basemodel.rst

This file was deleted.

1 change: 0 additions & 1 deletion docs/discussion/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ Discussions

casing.rst
class_or_function.rst
dataclass_or_basemodel.rst
json_encoding.rst
19 changes: 10 additions & 9 deletions docs/how_to_guides/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ The following configuration options are used by Quart-Schema. They
should be set as part of the standard `Quart configuration
<https://pgjones.gitlab.io/quart/how_to_guides/configuration.html>`_.

============================= =====
Configuration key type
----------------------------- -----
QUART_SCHEMA_SWAGGER_JS_URL str
QUART_SCHEMA_SWAGGER_CSS_URL str
QUART_SCHEMA_REDOC_JS_URL str
QUART_SCHEMA_BY_ALIAS bool
QUART_SCHEMA_CONVERT_CASING bool
============================= =====
================================== =====
Configuration key type
---------------------------------- -----
QUART_SCHEMA_CONVERSION_PREFERENCE str
QUART_SCHEMA_SWAGGER_JS_URL str
QUART_SCHEMA_SWAGGER_CSS_URL str
QUART_SCHEMA_REDOC_JS_URL str
QUART_SCHEMA_BY_ALIAS bool
QUART_SCHEMA_CONVERT_CASING bool
================================== =====

which allow the js and css for the documentation UI to be changed and
configured and specifies that responses that are Pydantic models
Expand Down
14 changes: 5 additions & 9 deletions docs/how_to_guides/error_handling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,15 @@ an error handler, for example for a JSON error response,
or if you prefer to let the requestor know exactly why the validation
failed you can utilise the ``validation_error`` attribute which is a
either Pydantic ``ValidationError`` or a ``TypeError``,
either Pydantic ``ValidationError``, a msgspec ``ValidationError`` or
a ``TypeError``,

.. code-block:: python
from quart_schema import RequestSchemaValidationError
@app.errorhandler(RequestSchemaValidationError)
async def handle_request_validation_error(error):
if isinstance(error.validation_error, TypeError):
return {
"errors": str(error.validation_error),
}, 400
else:
return {
"errors": error.validation_error.json(),
}, 400
return {
"errors": str(error.validation_error),
}, 400
155 changes: 132 additions & 23 deletions docs/how_to_guides/headers_validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,74 @@ Request headers
Request headers can be validated against a schema you define by
decorating the route handler, as so,

.. code-block:: python
.. tabs::

.. tab:: attrs

.. code-block:: python
from attrs import define
from quart_schema import validate_headers
@define
class Headers:
x_required: str
x_optional: int | None = None
@app.route("/")
@validate_headers(Headers)
async def index(headers: Headers):
...
.. tab:: dataclasses

.. code-block:: python
from dataclasses import dataclass
from quart_schema import validate_headers
@dataclass
class Headers:
x_required: str
x_optional: int | None = None
@app.route("/")
@validate_headers(Headers)
async def index(headers: Headers):
...
.. tab:: msgspec

.. code-block:: python
from msgspec import Struct
from quart_schema import validate_headers
class Headers(Struct):
x_required: str
x_optional: int | None = None
@app.route("/")
@validate_headers(Headers)
async def index(headers: Headers):
...
.. tab:: pydantic

from dataclasses import dataclass
.. code-block:: python
from quart_schema import validate_headers
from pydantic import BaseModel
from quart_schema import validate_headers
@dataclass
class Headers:
x_required: str
x_optional: int | None = None
class Headers(BaseModel):
x_required: str
x_optional: int | None = None
@app.route("/")
@validate_headers(Headers)
async def index(headers: Headers):
...
@app.route("/")
@validate_headers(Headers)
async def index(headers: Headers):
...
this will require the client adds a ``X-Required`` header to the
request and optionally ``X-Optional`` of type int.
Expand Down Expand Up @@ -65,22 +118,78 @@ Response headers
Request headers can be validated alongside the response body bt
decorating the route handler with a relevant schema, as so,

.. code-block:: python
.. tabs::

.. tab:: attrs

.. code-block:: python
from attrs import define
from quart_schema import validate_response
@define
class Headers:
x_required: str
x_optional: int | None = None
@app.route("/")
@validate_response(Body, 200, Headers)
async def index():
...
return body, 200, headers
.. tab:: dataclasses

.. code-block:: python
from dataclasses import dataclass
from quart_schema import validate_response
@dataclass
class Headers:
x_required: str
x_optional: int | None = None
@app.route("/")
@validate_response(Body, 200, Headers)
async def index():
...
return body, 200, headers
.. tab:: msgspec

.. code-block:: python
from msgspec import Struct
from quart_schema import validate_response
class Headers(Struct):
x_required: str
x_optional: int | None = None
@app.route("/")
@validate_response(Body, 200, Headers)
async def index():
...
return body, 200, headers
.. tab:: pydantic

from dataclasses import dataclass
.. code-block:: python
from quart_schema import validate_response
from pydantic import BaseModel
from quart_schema import validate_response
@dataclass
class Headers:
x_required: str
x_optional: int | None = None
class Headers(BaseModel):
x_required: str
x_optional: int | None = None
@app.route("/")
@validate_response(Body, 200, Headers)
async def index():
...
return body, 200, headers
@app.route("/")
@validate_response(Body, 200, Headers)
async def index():
...
return body, 200, headers
this will require that the headers variable adds a ``X-Required``
header to the response and optionally ``X-Optional`` of type int. The
Expand Down
Loading

0 comments on commit 791183e

Please sign in to comment.