From ac4564a70204cf8a81f235be27ecdfcc03a7830a Mon Sep 17 00:00:00 2001 From: Alexander Tikhonov Date: Mon, 25 Nov 2024 14:59:51 +0300 Subject: [PATCH] Add ability to use mashumaro plugins --- .github/workflows/main.yml | 2 +- README.md | 36 ++++++++++++++++++++++++++++++---- openapify/core/base_plugins.py | 9 ++++++++- pyproject.toml | 8 +++++++- setup.py | 4 ++-- 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0520de4..b2ebf29 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} diff --git a/README.md b/README.md index 8a2ce70..85c2717 100644 --- a/README.md +++ b/README.md @@ -1089,10 +1089,10 @@ following openapify dataclasses defined in `openapify.core.models`: * `QueryParam` Out of the box, the schema is generated by using -[`mashumaro`](https://github.com/Fatal1ty/mashumaro) library, but support -for third-party entity schema generators can be achieved through -`schema_helper` method. For example, here's what a plugin for pydantic models -might look like: +[`mashumaro`](https://github.com/Fatal1ty/mashumaro) library (see the note +below), but support for third-party entity schema generators can be achieved +through `schema_helper` method. For example, here's what a plugin for pydantic +models might look like: ```python from typing import Any @@ -1116,6 +1116,34 @@ class PydanticSchemaPlugin(BasePlugin): return schema ``` +> [!NOTE]\ +> The [`BaseSchemaPlugin`](https://github.com/Fatal1ty/openapify/blob/master/openapify/core/base_plugins.py#L41-L64), +> which is enabled by default and has the lowest priority, is responsible for +> generating the schema. This plugin utilizes the mashumaro library for schema +> generation, which in turn incorporates its own [plugin system](https://github.com/Fatal1ty/mashumaro?tab=readme-ov-file#json-schema-plugins), +> enabling customization of JSON Schema generation and support for additional +> data types. For more nuanced modifications, particularly within nested data +> models, you can employ `BaseSchemaPlugin` with a specified list of mashumaro JSON +> Schema plugins. This approach allows for finer control over schema generation +> when needed: +> ```python +> from mashumaro.jsonschema.plugins import DocstringDescriptionPlugin +> +> from openapify import build_spec +> from openapify.core.base_plugins import BaseSchemaPlugin +> +> spec = build_spec( +> routes=[...], +> plugins=[ +> BaseSchemaPlugin( +> plugins=[ +> DocstringDescriptionPlugin(), +> ] +> ), +> ], +> ) +> ``` + ### media_type_helper A media type is used in OpenAPI Request diff --git a/openapify/core/base_plugins.py b/openapify/core/base_plugins.py index 4e87b15..a91d4b2 100644 --- a/openapify/core/base_plugins.py +++ b/openapify/core/base_plugins.py @@ -1,6 +1,8 @@ +from collections.abc import Sequence from typing import Any, Dict, Optional, Union from mashumaro.jsonschema import OPEN_API_3_1, JSONSchemaBuilder +from mashumaro.jsonschema.plugins import BasePlugin as BaseJSONSchemaPlugin from openapify.core.models import Body, Cookie, Header, QueryParam from openapify.core.utils import get_value_type @@ -37,13 +39,18 @@ def media_type_helper( class BaseSchemaPlugin(BasePlugin): + def __init__(self, plugins: Sequence[BaseJSONSchemaPlugin] = ()): + self.json_schema_plugins = plugins + def schema_helper( self, obj: Union[Body, Cookie, Header, QueryParam], name: Optional[str] = None, ) -> Optional[Dict[str, Any]]: builder = JSONSchemaBuilder( - dialect=OPEN_API_3_1, ref_prefix="#/components/schemas" + dialect=OPEN_API_3_1, + ref_prefix="#/components/schemas", + plugins=self.json_schema_plugins, ) try: json_schema = builder.build(obj.value_type) diff --git a/pyproject.toml b/pyproject.toml index 4c13fb4..1d2771d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,12 @@ ignore_missing_imports = true disallow_untyped_defs = true disallow_incomplete_defs = true +[[tool.mypy.overrides]] +module = [ + 'openapify.core.openapi.models', +] +disable_error_code = 'override' + [flake8] max-line-length = 79 @@ -15,4 +21,4 @@ ensure_newline_before_comments = true [tool.black] line-length = 79 -target-version = ['py37', 'py38', 'py39', 'py310', 'py311'] +target-version = ['py39', 'py310', 'py311', 'py312'] diff --git a/setup.py b/setup.py index 9a7298e..fc3c9c4 100644 --- a/setup.py +++ b/setup.py @@ -15,11 +15,11 @@ "License :: OSI Approved :: Apache Software License", "Intended Audience :: Developers", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Development Status :: 3 - Alpha", ], license="Apache License, Version 2.0", @@ -31,7 +31,7 @@ python_requires=">=3.8", install_requires=[ "apispec", - "mashumaro>=3.7", + "mashumaro>=3.15", ], extras_require={ "aiohttp": ["aiohttp"],