openapi-python-generator 1.1.2.dev1734089718__tar.gz → 1.2.1.dev1757310194__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/PKG-INFO +3 -3
  2. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/pyproject.toml +5 -5
  3. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/setup.py +5 -4
  4. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/__init__.py +2 -1
  5. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/__main__.py +21 -6
  6. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/common.py +15 -0
  7. openapi_python_generator-1.2.1.dev1757310194/src/openapi_python_generator/generate_data.py +239 -0
  8. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/api_config_generator.py +12 -6
  9. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/generator.py +8 -4
  10. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/model_generator.py +191 -54
  11. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/service_generator.py +159 -62
  12. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/aiohttp.jinja2 +10 -9
  13. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/httpx.jinja2 +8 -5
  14. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/models.jinja2 +5 -3
  15. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/models_pydantic_2.jinja2 +5 -3
  16. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/requests.jinja2 +8 -5
  17. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/service.jinja2 +2 -1
  18. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/models.py +16 -2
  19. openapi_python_generator-1.2.1.dev1757310194/src/openapi_python_generator/parsers/__init__.py +13 -0
  20. openapi_python_generator-1.2.1.dev1757310194/src/openapi_python_generator/parsers/openapi_30.py +65 -0
  21. openapi_python_generator-1.2.1.dev1757310194/src/openapi_python_generator/parsers/openapi_31.py +65 -0
  22. openapi_python_generator-1.2.1.dev1757310194/src/openapi_python_generator/py.typed +0 -0
  23. openapi_python_generator-1.2.1.dev1757310194/src/openapi_python_generator/version_detector.py +70 -0
  24. openapi_python_generator-1.1.2.dev1734089718/src/openapi_python_generator/generate_data.py +0 -192
  25. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/LICENSE +0 -0
  26. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/README.md +0 -0
  27. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/__init__.py +0 -0
  28. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/__init__.py +0 -0
  29. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/common.py +0 -0
  30. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/jinja_config.py +0 -0
  31. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/apiconfig.jinja2 +0 -0
  32. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/apiconfig_pydantic_2.jinja2 +0 -0
  33. {openapi_python_generator-1.1.2.dev1734089718 → openapi_python_generator-1.2.1.dev1757310194}/src/openapi_python_generator/language_converters/python/templates/enum.jinja2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openapi-python-generator
3
- Version: 1.1.2.dev1734089718
3
+ Version: 1.2.1.dev1757310194
4
4
  Summary: Openapi Python Generator
5
5
  Home-page: https://github.com/MarcoMuellner/openapi-python-generator
6
6
  License: MIT
@@ -18,9 +18,9 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Requires-Dist: Jinja2 (>=3.1.2,<4.0.0)
19
19
  Requires-Dist: black (>=21.10b0)
20
20
  Requires-Dist: click (>=8.1.3,<9.0.0)
21
- Requires-Dist: httpx[all] (>=0.23.0,<0.24.0)
21
+ Requires-Dist: httpx[all] (>=0.28.0,<0.29.0)
22
22
  Requires-Dist: isort (>=5.10.1)
23
- Requires-Dist: openapi-pydantic (>=0.5.0,<0.6.0)
23
+ Requires-Dist: openapi-pydantic (>=0.5.1,<0.6.0)
24
24
  Requires-Dist: orjson (>=3.9.15,<4.0.0)
25
25
  Requires-Dist: pydantic (>=2.10.2,<3.0.0)
26
26
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "openapi-python-generator"
3
- version = "1.1.2.dev.1734089718"
3
+ version = "1.2.1.dev.1757310194"
4
4
  description = "Openapi Python Generator"
5
5
  authors = ["Marco Müllner <muellnermarco@gmail.com>"]
6
6
  license = "MIT"
@@ -18,17 +18,17 @@ Changelog = "https://github.com/MarcoMuellner/openapi-python-generator/releases"
18
18
 
19
19
  [tool.poetry.dependencies]
20
20
  python = "^3.8"
21
- httpx = {extras = ["all"], version = "^0.23.0"}
21
+ httpx = {extras = ["all"], version = "^0.28.0"}
22
22
  pydantic = "^2.10.2"
23
23
  orjson = "^3.9.15"
24
24
  Jinja2 = "^3.1.2"
25
25
  click = "^8.1.3"
26
26
  black = ">=21.10b0"
27
27
  isort = ">=5.10.1"
28
- openapi-pydantic = "^0.5.0"
28
+ openapi-pydantic = "^0.5.1"
29
29
  pyyaml = "^6.0.2"
30
30
 
31
- [tool.poetry.dev-dependencies]
31
+ [tool.poetry.group.dev.dependencies]
32
32
  Pygments = ">=2.10.0"
33
33
  coverage = {extras = ["toml"], version = "^6.4.1"}
34
34
  darglint = ">=1.8.1"
@@ -51,7 +51,7 @@ myst-parser = {version = ">=0.16.1"}
51
51
  pytest-cov = "^3.0.0"
52
52
  fastapi = "^0.115.5"
53
53
  uvicorn = "^0.18.1"
54
- respx = "^0.20.1"
54
+ respx = "^0.22.0"
55
55
  aiohttp = "^3.8.3"
56
56
 
57
57
  [tool.poetry.scripts]
@@ -7,7 +7,8 @@ package_dir = \
7
7
  packages = \
8
8
  ['openapi_python_generator',
9
9
  'openapi_python_generator.language_converters',
10
- 'openapi_python_generator.language_converters.python']
10
+ 'openapi_python_generator.language_converters.python',
11
+ 'openapi_python_generator.parsers']
11
12
 
12
13
  package_data = \
13
14
  {'': ['*'],
@@ -17,9 +18,9 @@ install_requires = \
17
18
  ['Jinja2>=3.1.2,<4.0.0',
18
19
  'black>=21.10b0',
19
20
  'click>=8.1.3,<9.0.0',
20
- 'httpx[all]>=0.23.0,<0.24.0',
21
+ 'httpx[all]>=0.28.0,<0.29.0',
21
22
  'isort>=5.10.1',
22
- 'openapi-pydantic>=0.5.0,<0.6.0',
23
+ 'openapi-pydantic>=0.5.1,<0.6.0',
23
24
  'orjson>=3.9.15,<4.0.0',
24
25
  'pydantic>=2.10.2,<3.0.0',
25
26
  'pyyaml>=6.0.2,<7.0.0']
@@ -30,7 +31,7 @@ entry_points = \
30
31
 
31
32
  setup_kwargs = {
32
33
  'name': 'openapi-python-generator',
33
- 'version': '1.1.2.dev1734089718',
34
+ 'version': '1.2.1.dev1757310194',
34
35
  'description': 'Openapi Python Generator',
35
36
  'long_description': "# Openapi Python Generator\n\n[![PyPI](https://img.shields.io/pypi/v/openapi-python-generator.svg)][pypi_]\n[![Status](https://img.shields.io/pypi/status/openapi-python-generator.svg)][status]\n[![Python Version](https://img.shields.io/pypi/pyversions/openapi-python-generator)][python version]\n[![License](https://img.shields.io/pypi/l/openapi-python-generator)][license]\n\n[![](https://img.shields.io/static/v1?label=documentation&message=enabled&color=<COLOR>)][documentation]\n[![Tests](https://github.com/MarcoMuellner/openapi-python-generator/workflows/Tests/badge.svg)][tests]\n[![Codecov](https://codecov.io/gh/MarcoMuellner/openapi-python-generator/branch/main/graph/badge.svg)][codecov]\n\n[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)][pre-commit]\n[![Black](https://img.shields.io/badge/code%20style-black-000000.svg)][black]\n\n[pypi_]: https://pypi.org/project/openapi-python-generator/\n[status]: https://pypi.org/project/openapi-python-generator/\n[python version]: https://pypi.org/project/openapi-python-generator\n[documentation]: https://marcomuellner.github.io/openapi-python-generator/\n[tests]: https://github.com/MarcoMuellner/openapi-python-generator/actions?workflow=Tests\n[codecov]: https://app.codecov.io/gh/MarcoMuellner/openapi-python-generator\n[pre-commit]: https://github.com/pre-commit/pre-commit\n[black]: https://github.com/psf/black\n\n![](logo.png)\n\n---\n__Documentation:__ [here][documentation]\n\n---\n\n## Features\n\n- __Ease of use__. Provide input, output and the library, and the generator will do the rest.\n- __Type safety and type hinting.__ __OpenAPI python generator__ makes heavy use of pydantic models to provide type-safe data structures.\n- __Support for multiple rest frameworks.__ __OpenAPI python generator__ currently supports the following:\n - [httpx](https://pypi.org/project/httpx/)\n - [requests](https://pypi.org/project/requests/)\n - [aiohttp](https://pypi.org/project/aiohttp/)\n- __Async and sync code generation support__, depending on the framework. It will automatically create both for frameworks that support both.\n- __Easily extendable using Jinja2 templates__. The code is designed to be easily extendable and should support even more languages and frameworks in the future.\n- __Fully tested__. Every generated code is automatically tested against the OpenAPI spec and we have 100% coverage.\n- __Usage as CLI or as library__.\n\n## Requirements\n\n- Python 3.7+\n\n## Installation\n\nYou can install _Openapi Python Generator_ via [pip] from [PyPI]:\n\n```console\n$ pip install openapi-python-generator\n```\n\n## Usage\n\nPlease see the [Quick start page] for details.\n\n## Roadmap\n\n- Support for all commonly used http libraries in the python ecosystem (~~requests~~, urllib, ...)\n- Support for multiple languages\n- Support for multiple authentication schemes\n- Support custom themes\n\n## Contributing\n\nContributions are very welcome.\nTo learn more, see the [Contributor Guide].\n\n## License\n\nDistributed under the terms of the [MIT license][license],\n_Openapi Python Generator_ is free and open source software.\n\n## Issues\n\nIf you encounter any problems,\nplease [file an issue] along with a detailed description.\n\n## Credits\n\nSpecial thanks to the peeps from [openapi-schema-pydantic](https://github.com/kuimono/openapi-schema-pydantic),\nwhich already did a lot of the legwork by providing a pydantic schema for the OpenAPI 3.0.0+ specification.\n\nThis project was generated from [@cjolowicz]'s [Hypermodern Python Cookiecutter] template.\n\n[@cjolowicz]: https://github.com/cjolowicz\n[pypi]: https://pypi.org/\n[hypermodern python cookiecutter]: https://github.com/cjolowicz/cookiecutter-hypermodern-python\n[file an issue]: https://github.com/MarcoMuellner/openapi-python-generator/issues\n[pip]: https://pip.pypa.io/\n\n<!-- github-only -->\n\n[license]: https://github.com/MarcoMuellner/openapi-python-generator/blob/main/LICENSE\n[contributor guide]: https://github.com/MarcoMuellner/openapi-python-generator/blob/main/CONTRIBUTING.md\n[Quick start page]: https://marcomuellner.github.io/openapi-python-generator/quick_start/\n",
36
37
  'author': 'Marco Müllner',
@@ -1,4 +1,5 @@
1
- """Python client from an OPENAPI 3.0 specification in seconds."""
1
+ """Python client from an OPENAPI 3.0+ specification in seconds."""
2
+
2
3
  try:
3
4
  from importlib.metadata import PackageNotFoundError # type: ignore
4
5
  from importlib.metadata import version
@@ -1,12 +1,12 @@
1
1
  from typing import Optional
2
- from enum import Enum
3
2
 
4
3
  import click
5
4
 
6
5
  from openapi_python_generator import __version__
7
- from openapi_python_generator.common import HTTPLibrary, PydanticVersion
6
+ from openapi_python_generator.common import Formatter, HTTPLibrary, PydanticVersion
8
7
  from openapi_python_generator.generate_data import generate_data
9
8
 
9
+
10
10
  @click.command()
11
11
  @click.argument("source")
12
12
  @click.argument("output")
@@ -45,6 +45,13 @@ from openapi_python_generator.generate_data import generate_data
45
45
  show_default=True,
46
46
  help="Pydantic version to use for generated models.",
47
47
  )
48
+ @click.option(
49
+ "--formatter",
50
+ type=click.Choice(["black", "none"]),
51
+ default="black",
52
+ show_default=True,
53
+ help="Option to choose which auto formatter is applied.",
54
+ )
48
55
  @click.version_option(version=__version__)
49
56
  def main(
50
57
  source: str,
@@ -54,17 +61,25 @@ def main(
54
61
  use_orjson: bool = False,
55
62
  custom_template_path: Optional[str] = None,
56
63
  pydantic_version: PydanticVersion = PydanticVersion.V2,
64
+ formatter: Formatter = Formatter.BLACK,
57
65
  ) -> None:
58
66
  """
59
- Generate Python code from an OpenAPI 3.0 specification.
67
+ Generate Python code from an OpenAPI 3.0+ specification.
60
68
 
61
- Provide a SOURCE (file or URL) containing the OpenAPI 3 specification and
69
+ Provide a SOURCE (file or URL) containing the OpenAPI 3.0+ specification and
62
70
  an OUTPUT path, where the resulting client is created.
63
71
  """
64
72
  generate_data(
65
- source, output, library, env_token_name, use_orjson, custom_template_path,pydantic_version
73
+ source,
74
+ output,
75
+ library,
76
+ env_token_name,
77
+ use_orjson,
78
+ custom_template_path,
79
+ pydantic_version,
80
+ formatter,
66
81
  )
67
82
 
68
83
 
69
84
  if __name__ == "__main__": # pragma: no cover
70
- main()
85
+ main()
@@ -14,11 +14,26 @@ class HTTPLibrary(str, Enum):
14
14
  requests = "requests"
15
15
  aiohttp = "aiohttp"
16
16
 
17
+
17
18
  class PydanticVersion(str, Enum):
18
19
  V1 = "v1"
19
20
  V2 = "v2"
20
21
 
21
22
 
23
+ class Formatter(str, Enum):
24
+ """
25
+ Enum for the available code formatters.
26
+ """
27
+
28
+ BLACK = "black"
29
+ NONE = "none"
30
+
31
+
32
+ class FormatOptions:
33
+ skip_validation: bool = False
34
+ line_length: int = 120
35
+
36
+
22
37
  library_config_dict: Dict[Optional[HTTPLibrary], LibraryConfig] = {
23
38
  HTTPLibrary.httpx: LibraryConfig(
24
39
  name="httpx",
@@ -0,0 +1,239 @@
1
+ from pathlib import Path
2
+ from typing import List
3
+ from typing import Optional
4
+ from typing import Union
5
+
6
+ import black
7
+ from black.report import NothingChanged # type: ignore
8
+ import click
9
+ import httpx
10
+ import isort
11
+ import orjson
12
+ import yaml # type: ignore
13
+ from httpx import ConnectError
14
+ from httpx import ConnectTimeout
15
+ from pydantic import ValidationError
16
+
17
+ from .common import FormatOptions, Formatter, HTTPLibrary, PydanticVersion
18
+ from .language_converters.python.jinja_config import SERVICE_TEMPLATE
19
+ from .language_converters.python.jinja_config import create_jinja_env
20
+ from .models import ConversionResult
21
+ from .version_detector import detect_openapi_version
22
+ from .parsers import (
23
+ parse_openapi_3_0,
24
+ parse_openapi_3_1,
25
+ generate_code_3_0,
26
+ generate_code_3_1,
27
+ )
28
+
29
+
30
+ def write_code(path: Path, content: str, formatter: Formatter) -> None:
31
+ """
32
+ Write the content to the file at the given path.
33
+ :param path: The path to the file.
34
+ :param content: The content to write.
35
+ :param formatter: The formatter applied to the code written.
36
+ """
37
+ if formatter == Formatter.BLACK:
38
+ formatted_contend = format_using_black(content)
39
+ elif formatter == Formatter.NONE:
40
+ formatted_contend = content
41
+ else:
42
+ raise NotImplementedError(
43
+ f"Missing implementation for formatter {formatter!r}."
44
+ )
45
+ with open(path, "w") as f:
46
+ f.write(formatted_contend)
47
+
48
+
49
+ def format_using_black(content: str) -> str:
50
+ try:
51
+ formatted_contend = black.format_file_contents(
52
+ content,
53
+ fast=FormatOptions.skip_validation,
54
+ mode=black.FileMode(line_length=FormatOptions.line_length),
55
+ )
56
+ except NothingChanged:
57
+ return content
58
+ return isort.code(formatted_contend, line_length=FormatOptions.line_length)
59
+
60
+
61
+ def get_open_api(source: Union[str, Path]):
62
+ """
63
+ Tries to fetch the openapi specification file from the web or load from a local file.
64
+ Supports both JSON and YAML formats. Returns the according OpenAPI object.
65
+ Automatically supports OpenAPI 3.0 and 3.1 specifications with intelligent version detection.
66
+
67
+ Args:
68
+ source: URL or file path to the OpenAPI specification
69
+
70
+ Returns:
71
+ tuple: (OpenAPI object, version) where version is "3.0" or "3.1"
72
+
73
+ Raises:
74
+ FileNotFoundError: If the specified file cannot be found
75
+ ConnectError: If the URL cannot be accessed
76
+ ValidationError: If the specification is invalid
77
+ JSONDecodeError/YAMLError: If the file cannot be parsed
78
+ """
79
+ try:
80
+ # Handle remote files
81
+ if not isinstance(source, Path) and (
82
+ source.startswith("http://") or source.startswith("https://")
83
+ ):
84
+ content = httpx.get(source).text
85
+ # Try JSON first, then YAML for remote files
86
+ try:
87
+ data = orjson.loads(content)
88
+ except orjson.JSONDecodeError:
89
+ data = yaml.safe_load(content)
90
+ else:
91
+ # Handle local files
92
+ with open(source, "r") as f:
93
+ file_content = f.read()
94
+
95
+ # Try JSON first
96
+ try:
97
+ data = orjson.loads(file_content)
98
+ except orjson.JSONDecodeError:
99
+ # If JSON fails, try YAML
100
+ try:
101
+ data = yaml.safe_load(file_content)
102
+ except yaml.YAMLError as e:
103
+ click.echo(
104
+ f"File {source} is neither a valid JSON nor YAML file: {str(e)}"
105
+ )
106
+ raise
107
+
108
+ # Detect version and parse with appropriate parser
109
+ version = detect_openapi_version(data)
110
+
111
+ if version == "3.0":
112
+ openapi_obj = parse_openapi_3_0(data) # type: ignore[assignment]
113
+ elif version == "3.1":
114
+ openapi_obj = parse_openapi_3_1(data) # type: ignore[assignment]
115
+ else:
116
+ # Unsupported version detected (version detection already limited to 3.0 / 3.1)
117
+ raise ValueError(
118
+ f"Unsupported OpenAPI version: {version}. Only 3.0.x and 3.1.x are supported."
119
+ )
120
+
121
+ return openapi_obj, version
122
+
123
+ except FileNotFoundError:
124
+ click.echo(
125
+ f"File {source} not found. Please make sure to pass the path to the OpenAPI specification."
126
+ )
127
+ raise
128
+ except (ConnectError, ConnectTimeout):
129
+ click.echo(f"Could not connect to {source}.")
130
+ raise ConnectError(f"Could not connect to {source}.") from None
131
+ except ValidationError:
132
+ click.echo(f"File {source} is not a valid OpenAPI 3.0+ specification.")
133
+ raise
134
+
135
+
136
+ def write_data(
137
+ data: ConversionResult, output: Union[str, Path], formatter: Formatter
138
+ ) -> None:
139
+ """
140
+ This function will firstly create the folder structure of output, if it doesn't exist. Then it will create the
141
+ models from data.models into the models sub module of the output folder. After this, the services will be created
142
+ into the services sub module of the output folder.
143
+ :param data: The data to write.
144
+ :param output: The path to the output folder.
145
+ :param formatter: The formatter applied to the code written.
146
+ """
147
+
148
+ # Create the folder structure of the output folder.
149
+ Path(output).mkdir(parents=True, exist_ok=True)
150
+
151
+ # Create the models module.
152
+ models_path = Path(output) / "models"
153
+ models_path.mkdir(parents=True, exist_ok=True)
154
+
155
+ # Create the services module.
156
+ services_path = Path(output) / "services"
157
+ services_path.mkdir(parents=True, exist_ok=True)
158
+
159
+ files: List[str] = []
160
+
161
+ # Write the models.
162
+ for model in data.models:
163
+ files.append(model.file_name)
164
+ write_code(models_path / f"{model.file_name}.py", model.content, formatter)
165
+
166
+ # Create models.__init__.py file containing imports to all models.
167
+ write_code(
168
+ models_path / "__init__.py",
169
+ "\n".join([f"from .{file} import *" for file in files]),
170
+ formatter,
171
+ )
172
+
173
+ files = []
174
+
175
+ # Write the services.
176
+ jinja_env = create_jinja_env()
177
+ for service in data.services:
178
+ if len(service.operations) == 0:
179
+ continue
180
+ files.append(service.file_name)
181
+ write_code(
182
+ services_path / f"{service.file_name}.py",
183
+ jinja_env.get_template(SERVICE_TEMPLATE).render(**service.model_dump()),
184
+ formatter,
185
+ )
186
+
187
+ # Create services.__init__.py file containing imports to all services.
188
+ write_code(services_path / "__init__.py", "", formatter)
189
+
190
+ # Write the api_config.py file.
191
+ write_code(Path(output) / "api_config.py", data.api_config.content, formatter)
192
+
193
+ # Write the __init__.py file.
194
+ write_code(
195
+ Path(output) / "__init__.py",
196
+ "from .models import *\nfrom .services import *\nfrom .api_config import *",
197
+ formatter,
198
+ )
199
+
200
+
201
+ def generate_data(
202
+ source: Union[str, Path],
203
+ output: Union[str, Path],
204
+ library: HTTPLibrary = HTTPLibrary.httpx,
205
+ env_token_name: Optional[str] = None,
206
+ use_orjson: bool = False,
207
+ custom_template_path: Optional[str] = None,
208
+ pydantic_version: PydanticVersion = PydanticVersion.V2,
209
+ formatter: Formatter = Formatter.BLACK,
210
+ ) -> None:
211
+ """
212
+ Generate Python code from an OpenAPI 3.0+ specification.
213
+ """
214
+ openapi_obj, version = get_open_api(source)
215
+ click.echo(f"Generating data from {source} (OpenAPI {version})")
216
+
217
+ # Use version-specific generator
218
+ if version == "3.0":
219
+ result = generate_code_3_0(
220
+ openapi_obj, # type: ignore
221
+ library,
222
+ env_token_name,
223
+ use_orjson,
224
+ custom_template_path,
225
+ pydantic_version,
226
+ )
227
+ elif version == "3.1":
228
+ result = generate_code_3_1(
229
+ openapi_obj, # type: ignore
230
+ library,
231
+ env_token_name,
232
+ use_orjson,
233
+ custom_template_path,
234
+ pydantic_version,
235
+ )
236
+ else:
237
+ raise ValueError(f"Unsupported OpenAPI version: {version}")
238
+
239
+ write_data(result, output, formatter)
@@ -1,10 +1,11 @@
1
1
  from typing import Optional
2
2
 
3
- from openapi_pydantic.v3.v3_0 import OpenAPI
3
+ from openapi_pydantic.v3 import OpenAPI
4
4
 
5
5
  from openapi_python_generator.common import PydanticVersion
6
6
  from openapi_python_generator.language_converters.python.jinja_config import (
7
- API_CONFIG_TEMPLATE, API_CONFIG_TEMPLATE_PYDANTIC_V2,
7
+ API_CONFIG_TEMPLATE,
8
+ API_CONFIG_TEMPLATE_PYDANTIC_V2,
8
9
  )
9
10
  from openapi_python_generator.language_converters.python.jinja_config import (
10
11
  create_jinja_env,
@@ -13,19 +14,24 @@ from openapi_python_generator.models import APIConfig
13
14
 
14
15
 
15
16
  def generate_api_config(
16
- data: OpenAPI, env_token_name: Optional[str] = None,
17
- pydantic_version: PydanticVersion = PydanticVersion.V2,
17
+ data: OpenAPI,
18
+ env_token_name: Optional[str] = None,
19
+ pydantic_version: PydanticVersion = PydanticVersion.V2,
18
20
  ) -> APIConfig:
19
21
  """
20
22
  Generate the API model.
21
23
  """
22
24
 
23
- template_name = API_CONFIG_TEMPLATE_PYDANTIC_V2 if pydantic_version == PydanticVersion.V2 else API_CONFIG_TEMPLATE
25
+ template_name = (
26
+ API_CONFIG_TEMPLATE_PYDANTIC_V2
27
+ if pydantic_version == PydanticVersion.V2
28
+ else API_CONFIG_TEMPLATE
29
+ )
24
30
  jinja_env = create_jinja_env()
25
31
  return APIConfig(
26
32
  file_name="api_config",
27
33
  content=jinja_env.get_template(template_name).render(
28
- env_token_name=env_token_name, **data.dict()
34
+ env_token_name=env_token_name, **data.model_dump()
29
35
  ),
30
36
  base_url=data.servers[0].url if len(data.servers) > 0 else "NO SERVER",
31
37
  )
@@ -1,6 +1,7 @@
1
- from typing import Optional
1
+ from typing import Optional, Union
2
2
 
3
- from openapi_pydantic.v3.v3_0 import OpenAPI
3
+ from openapi_pydantic.v3.v3_0 import OpenAPI as OpenAPI30
4
+ from openapi_pydantic.v3.v3_1 import OpenAPI as OpenAPI31
4
5
 
5
6
  from openapi_python_generator.common import PydanticVersion
6
7
  from openapi_python_generator.language_converters.python import common
@@ -16,9 +17,12 @@ from openapi_python_generator.language_converters.python.service_generator impor
16
17
  from openapi_python_generator.models import ConversionResult
17
18
  from openapi_python_generator.models import LibraryConfig
18
19
 
20
+ # Type alias for both OpenAPI versions
21
+ OpenAPISpec = Union[OpenAPI30, OpenAPI31]
22
+
19
23
 
20
24
  def generator(
21
- data: OpenAPI,
25
+ data: OpenAPISpec,
22
26
  library_config: LibraryConfig,
23
27
  env_token_name: Optional[str] = None,
24
28
  use_orjson: bool = False,
@@ -26,7 +30,7 @@ def generator(
26
30
  pydantic_version: PydanticVersion = PydanticVersion.V2,
27
31
  ) -> ConversionResult:
28
32
  """
29
- Generate Python code from an OpenAPI 3.0 specification.
33
+ Generate Python code from an OpenAPI 3.0+ specification.
30
34
  """
31
35
 
32
36
  common.set_use_orjson(use_orjson)