diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d877899..6e96013 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v1 @@ -109,7 +109,7 @@ jobs: path: dist - name: Use Python 3.11 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: '3.11' diff --git a/.gitignore b/.gitignore index a470096..c8496ae 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __pycache__ *.tar.gz _test_files/ dist/ +venv312/ diff --git a/CHANGELOG.md b/CHANGELOG.md index a41d390..5387ada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.9] - 2023-11-20 +- Adds support for Python 3.12. +- Adds `MarkupSafe` among required dependencies (and not optional). +- Adds support for latest function `model_dump` in Pydantic 2 (for examples + defined using Pydantic models). +- Upgrades development dependencies. +- Fix bug with missing items entry #36 by @mh7d and @mh-at-fujitsu + ## [1.0.8] - 2023-07-19 :cat: - Fixes example generation breaks on explicitly enumerated array elements #31, by @jjedele. diff --git a/openapidocs/__init__.py b/openapidocs/__init__.py index 0686169..38cedf9 100644 --- a/openapidocs/__init__.py +++ b/openapidocs/__init__.py @@ -1,2 +1,2 @@ -__version__ = "1.0.8" +__version__ = "1.0.9" VERSION = __version__ diff --git a/openapidocs/common.py b/openapidocs/common.py index 81f7ee1..33ebcfb 100644 --- a/openapidocs/common.py +++ b/openapidocs/common.py @@ -101,8 +101,6 @@ def regular_dict_factory(items: List[Tuple[Any, Any]]) -> Any: # bypassing "asdict" on child properties when they implement a `to_obj` # method: some entities require a specific shape when represented def _asdict_inner(obj, dict_factory): - if hasattr(obj, "dict") and callable(obj.dict): - return obj.dict() if hasattr(obj, "to_obj"): return obj.to_obj() if isinstance(obj, OpenAPIElement): @@ -111,6 +109,12 @@ def _asdict_inner(obj, dict_factory): value = _asdict_inner(getattr(obj, f.name), dict_factory) result.append((f.name, value)) return dict_factory(result) + if hasattr(obj, "model_dump") and callable(obj.model_dump): + # For Pydantic 2 + return obj.model_dump() + if hasattr(obj, "dict") and callable(obj.dict): + # For Pydantic 1 + return obj.dict() if is_dataclass(obj): return asdict(obj, dict_factory=regular_dict_factory) elif isinstance(obj, (list, tuple)): diff --git a/openapidocs/mk/v3/views_markdown/partial/path-items.html b/openapidocs/mk/v3/views_markdown/partial/path-items.html index 25388af..f309b34 100644 --- a/openapidocs/mk/v3/views_markdown/partial/path-items.html +++ b/openapidocs/mk/v3/views_markdown/partial/path-items.html @@ -6,9 +6,6 @@ {%- for http_method, operation in definition.items() %} ### {{http_method.upper()}} {{path | safe}} -{%- if "operationId" in operation -%} - -{%- endif -%} {% if "summary" in operation -%} {{operation.summary | wordwrap(80)}} {%- endif -%} diff --git a/openapidocs/mk/v3/views_mkdocs/partial/path-items.html b/openapidocs/mk/v3/views_mkdocs/partial/path-items.html index fed1c24..3f47cca 100644 --- a/openapidocs/mk/v3/views_mkdocs/partial/path-items.html +++ b/openapidocs/mk/v3/views_mkdocs/partial/path-items.html @@ -6,9 +6,6 @@
### {{http_method.upper()}} {{path | route | safe}} -{%- if "operationId" in operation -%} - -{%- endif -%} {% if "summary" in operation -%} {{operation.summary | wordwrap(80)}} {%- endif -%} diff --git a/openapidocs/utils/source.py b/openapidocs/utils/source.py index e7c95a1..5f3bf61 100644 --- a/openapidocs/utils/source.py +++ b/openapidocs/utils/source.py @@ -86,7 +86,6 @@ def read_from_source(source: str): raise ValueError("Unsupported source file.") else: - source_lower = source.lower() if source_lower.startswith("http://") or source_lower.startswith("https://"): diff --git a/pyproject.toml b/pyproject.toml index e702775..6aaa7d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] authors = [{ name = "Roberto Prevato", email = "roberto.prevato@gmail.com" }] description = "Classes to generate OpenAPI Documentation v3 and v2, in JSON and YAML." readme = "README.md" -requires-python = ">=3.7" +requires-python = ">=3.8" classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", @@ -17,17 +17,29 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Operating System :: OS Independent", ] -keywords = ["openapi", "docs", "swagger", "api", "documentation", "v3", "v2", "json", "yaml", "Markdown"] +keywords = [ + "openapi", + "docs", + "swagger", + "api", + "documentation", + "v3", + "v2", + "json", + "yaml", + "Markdown", +] -dependencies = ["PyYAML>=6", "essentials>=1.1.5"] +dependencies = ["PyYAML>=6", "essentials>=1.1.5", "MarkupSafe~=2.1.2"] [tool.hatch.version] path = "openapidocs/__init__.py" [project.optional-dependencies] -full = ["click~=8.1.3", "Jinja2~=3.1.2", "MarkupSafe==2.1.2", "rich~=12.6.0", "httpx<1"] +full = ["click~=8.1.3", "Jinja2~=3.1.2", "rich~=12.6.0", "httpx<1"] [project.scripts] openapidocs = "openapidocs.main:main" @@ -42,12 +54,12 @@ exclude = ["tests"] [tool.hatch.build] only-packages = false include = [ - "LICENSE", - "README.md", - "CHANGELOG.md", - "openapidocs/**/*.py", - "openapidocs/**/*.html", - "openapidocs/**/*.md", + "LICENSE", + "README.md", + "CHANGELOG.md", + "openapidocs/**/*.py", + "openapidocs/**/*.html", + "openapidocs/**/*.md", ] [project.urls] diff --git a/requirements.txt b/requirements.txt index 1a28bc8..6105e18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,47 +1,39 @@ -anyio==3.6.2 -attrs==22.1.0 -black==22.10.0 -build==0.10.0 -certifi==2022.9.24 -charset-normalizer==3.0.0 -click==8.1.3 -commonmark==0.9.1 -coverage==6.5.0 +annotated-types==0.6.0 +anyio==4.0.0 +black==23.11.0 +blinker==1.7.0 +certifi==2023.11.17 +click==8.1.7 +coverage==7.3.2 essentials==1.1.5 -flake8==5.0.4 -Flask==2.2.2 -h11==0.12.0 -httpcore==0.15.0 -httpx==0.24.0 +flake8==6.1.0 +Flask==3.0.0 +h11==0.14.0 +httpcore==1.0.2 +httpx==0.25.1 idna==3.4 -iniconfig==1.1.1 -isort==5.10.1 +iniconfig==2.0.0 +isort==5.12.0 itsdangerous==2.1.2 Jinja2==3.1.2 -markdown-it-py==2.2.0 -MarkupSafe==2.1.2 +markdown-it-py==3.0.0 +MarkupSafe==2.1.3 mccabe==0.7.0 mdurl==0.1.2 -mypy-extensions==0.4.3 -packaging==21.3 -pathspec==0.10.1 -platformdirs==2.5.2 -pluggy==1.0.0 -py==1.11.0 -pycodestyle==2.9.1 -pydantic==1.10.2 -pyflakes==2.5.0 -Pygments==2.13.0 -pyparsing==3.0.9 -pyproject_hooks==1.0.0 -pytest==7.2.0 -pytest-cov==4.0.0 -PyYAML==6.0 -regex==2022.10.31 -rfc3986==1.5.0 -rich==13.3.5 +mypy-extensions==1.0.0 +packaging==23.2 +pathspec==0.11.2 +platformdirs==4.0.0 +pluggy==1.3.0 +pycodestyle==2.11.1 +pydantic==2.5.1 +pydantic_core==2.14.3 +pyflakes==3.1.0 +Pygments==2.17.1 +pytest==7.4.3 +pytest-cov==4.1.0 +PyYAML==6.0.1 +rich==13.7.0 sniffio==1.3.0 -toml==0.10.2 -tomli==2.0.1 -typing_extensions==4.4.0 -Werkzeug==2.2.2 +typing_extensions==4.8.0 +Werkzeug==3.0.1 diff --git a/tests/common.py b/tests/common.py index f96e5ca..35b1ce0 100644 --- a/tests/common.py +++ b/tests/common.py @@ -3,11 +3,25 @@ import re from typing import Any -import pkg_resources import yaml from openapidocs.common import Format +try: + from importlib.resources import files + + def get_resource_file_path(file_name: str) -> str: + return str(files("tests") / "res" / file_name) + +except ImportError: + # Python 3.8 + import pkg_resources # type: ignore + + def get_resource_file_path(file_name: str) -> str: + return pkg_resources.resource_filename( + __name__, os.path.join(".", "res", file_name) + ) + def debug() -> bool: return bool(os.environ.get("DEBUG", "1")) @@ -26,12 +40,6 @@ def debug_result(version: str, instance: Any, result: str, format: Format) -> No debug_file.write(result) -def get_resource_file_path(file_name: str) -> str: - return pkg_resources.resource_filename( - __name__, os.path.join(".", "res", file_name) - ) - - def get_resource_file_content(file_name: str) -> str: with open( get_resource_file_path(file_name),