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),