ab-openapi-python-generator 2.1.4__py3-none-any.whl → 2.1.4.dev1768280320__py3-none-any.whl
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.
- ab_openapi_python_generator/__init__.py +10 -14
- {ab_openapi_python_generator-2.1.4.dist-info → ab_openapi_python_generator-2.1.4.dev1768280320.dist-info}/METADATA +27 -21
- ab_openapi_python_generator-2.1.4.dev1768280320.dist-info/RECORD +6 -0
- {ab_openapi_python_generator-2.1.4.dist-info → ab_openapi_python_generator-2.1.4.dev1768280320.dist-info}/WHEEL +1 -1
- ab_openapi_python_generator-2.1.4.dev1768280320.dist-info/entry_points.txt +3 -0
- ab_openapi_python_generator/__main__.py +0 -85
- ab_openapi_python_generator/common.py +0 -58
- ab_openapi_python_generator/generate_data.py +0 -235
- ab_openapi_python_generator/language_converters/__init__.py +0 -0
- ab_openapi_python_generator/language_converters/python/__init__.py +0 -0
- ab_openapi_python_generator/language_converters/python/api_config_generator.py +0 -35
- ab_openapi_python_generator/language_converters/python/common.py +0 -58
- ab_openapi_python_generator/language_converters/python/generator.py +0 -54
- ab_openapi_python_generator/language_converters/python/jinja_config.py +0 -38
- ab_openapi_python_generator/language_converters/python/model_generator.py +0 -896
- ab_openapi_python_generator/language_converters/python/service_generator.py +0 -540
- ab_openapi_python_generator/language_converters/python/templates/aiohttp.jinja2 +0 -49
- ab_openapi_python_generator/language_converters/python/templates/alias_union.jinja2 +0 -17
- ab_openapi_python_generator/language_converters/python/templates/apiconfig.jinja2 +0 -38
- ab_openapi_python_generator/language_converters/python/templates/apiconfig_pydantic_2.jinja2 +0 -42
- ab_openapi_python_generator/language_converters/python/templates/discriminator_enum.jinja2 +0 -7
- ab_openapi_python_generator/language_converters/python/templates/enum.jinja2 +0 -11
- ab_openapi_python_generator/language_converters/python/templates/httpx.jinja2 +0 -126
- ab_openapi_python_generator/language_converters/python/templates/models.jinja2 +0 -24
- ab_openapi_python_generator/language_converters/python/templates/models_pydantic_2.jinja2 +0 -28
- ab_openapi_python_generator/language_converters/python/templates/requests.jinja2 +0 -50
- ab_openapi_python_generator/language_converters/python/templates/service.jinja2 +0 -12
- ab_openapi_python_generator/models.py +0 -101
- ab_openapi_python_generator/parsers/__init__.py +0 -13
- ab_openapi_python_generator/parsers/openapi_30.py +0 -65
- ab_openapi_python_generator/parsers/openapi_31.py +0 -65
- ab_openapi_python_generator/py.typed +0 -0
- ab_openapi_python_generator/version_detector.py +0 -70
- ab_openapi_python_generator-2.1.4.dist-info/RECORD +0 -34
- ab_openapi_python_generator-2.1.4.dist-info/entry_points.txt +0 -2
- {ab_openapi_python_generator-2.1.4.dist-info/licenses → ab_openapi_python_generator-2.1.4.dev1768280320.dist-info}/LICENSE +0 -0
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Alias package to preserve imports when distribution renamed.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
except ImportError: # pragma: no cover
|
|
9
|
-
from importlib_metadata import (
|
|
10
|
-
PackageNotFoundError, # type: ignore
|
|
11
|
-
version, # type: ignore
|
|
12
|
-
)
|
|
3
|
+
This package re-exports the real package located at
|
|
4
|
+
`openapi_python_generator` so existing imports like
|
|
5
|
+
`import ab_openapi_python_generator` continue to work.
|
|
6
|
+
"""
|
|
7
|
+
from openapi_python_generator import * # noqa: F401,F403
|
|
13
8
|
|
|
9
|
+
# Preserve __all__ if present on the real package
|
|
14
10
|
try:
|
|
15
|
-
|
|
16
|
-
except
|
|
17
|
-
|
|
11
|
+
__all__ = getattr(__import__("openapi_python_generator"), "__all__")
|
|
12
|
+
except Exception:
|
|
13
|
+
__all__ = []
|
|
@@ -1,27 +1,32 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: ab-openapi-python-generator
|
|
3
|
-
Version: 2.1.4
|
|
3
|
+
Version: 2.1.4.dev1768280320
|
|
4
4
|
Summary: Openapi Python Generator
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Author-email:
|
|
10
|
-
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Keywords: Generator,OpenAPI,Python,async
|
|
5
|
+
Home-page: https://github.com/auth-broker/openapi-python-generator
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: OpenAPI,Generator,Python,async
|
|
8
|
+
Author: Marco Müllner
|
|
9
|
+
Author-email: muellnermarco@gmail.com
|
|
10
|
+
Requires-Python: >=3.9,<4.0
|
|
13
11
|
Classifier: Development Status :: 3 - Alpha
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Requires-Dist: Jinja2 (>=3.1.2,<4.0.0)
|
|
18
|
+
Requires-Dist: black (>=21.10b0)
|
|
19
|
+
Requires-Dist: click (>=8.1.3,<9.0.0)
|
|
20
|
+
Requires-Dist: httpx[all] (>=0.28.0,<0.29.0)
|
|
21
|
+
Requires-Dist: importlib-metadata (>=8.7.0,<9.0.0)
|
|
22
|
+
Requires-Dist: isort (>=5.10.1)
|
|
23
|
+
Requires-Dist: openapi-pydantic (>=0.5.1,<0.6.0)
|
|
24
|
+
Requires-Dist: orjson (>=3.9.15,<4.0.0)
|
|
25
|
+
Requires-Dist: pydantic (>=2.10.2,<3.0.0)
|
|
26
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
27
|
+
Project-URL: Changelog, https://github.com/auth-broker/openapi-python-generator/releases
|
|
28
|
+
Project-URL: Documentation, https://openapi-python-generator.readthedocs.io
|
|
29
|
+
Project-URL: Repository, https://github.com/auth-broker/openapi-python-generator
|
|
25
30
|
Description-Content-Type: text/markdown
|
|
26
31
|
|
|
27
32
|
# Openapi Python Generator
|
|
@@ -123,3 +128,4 @@ This project was generated from [@cjolowicz]'s [Hypermodern Python Cookiecutter]
|
|
|
123
128
|
[license]: https://github.com/MarcoMuellner/openapi-python-generator/blob/main/LICENSE
|
|
124
129
|
[contributor guide]: https://github.com/MarcoMuellner/openapi-python-generator/blob/main/CONTRIBUTING.md
|
|
125
130
|
[Quick start page]: https://marcomuellner.github.io/openapi-python-generator/quick_start/
|
|
131
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
ab_openapi_python_generator/__init__.py,sha256=E3Rg8oVmW0_-8KPCxfvgdo8n6w205qFYfUyrgnjilVc,450
|
|
2
|
+
ab_openapi_python_generator-2.1.4.dev1768280320.dist-info/LICENSE,sha256=0myanGwJ2vUOZN12aN95o0My6XEysnnVlbKikYw3pHg,1070
|
|
3
|
+
ab_openapi_python_generator-2.1.4.dev1768280320.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
|
|
4
|
+
ab_openapi_python_generator-2.1.4.dev1768280320.dist-info/entry_points.txt,sha256=hqE8T8yX5Pkeb2g__7yQrEazkBPwNgTbEHmoOkaYh80,83
|
|
5
|
+
ab_openapi_python_generator-2.1.4.dev1768280320.dist-info/METADATA,sha256=7yWKoxTDuK7aoN18VpfxM6w0-1411GpUuEhBlYZe0Dc,5437
|
|
6
|
+
ab_openapi_python_generator-2.1.4.dev1768280320.dist-info/RECORD,,
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
import click
|
|
4
|
-
|
|
5
|
-
from ab_openapi_python_generator import __version__
|
|
6
|
-
from ab_openapi_python_generator.common import Formatter, HTTPLibrary, PydanticVersion
|
|
7
|
-
from ab_openapi_python_generator.generate_data import generate_data
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@click.command()
|
|
11
|
-
@click.argument("source")
|
|
12
|
-
@click.argument("output")
|
|
13
|
-
@click.option(
|
|
14
|
-
"--library",
|
|
15
|
-
default=HTTPLibrary.httpx,
|
|
16
|
-
type=HTTPLibrary,
|
|
17
|
-
show_default=True,
|
|
18
|
-
help="HTTP library to use in the generation of the client.",
|
|
19
|
-
)
|
|
20
|
-
@click.option(
|
|
21
|
-
"--env-token-name",
|
|
22
|
-
default=None,
|
|
23
|
-
show_default=True,
|
|
24
|
-
help="Name of the environment variable that contains the token. If you set this, the code expects this environment "
|
|
25
|
-
"variable to be set and will raise an error if it is not.",
|
|
26
|
-
)
|
|
27
|
-
@click.option(
|
|
28
|
-
"--use-orjson",
|
|
29
|
-
is_flag=True,
|
|
30
|
-
show_default=True,
|
|
31
|
-
default=False,
|
|
32
|
-
help="Use the orjson library to serialize the data. This is faster than the default json library and provides "
|
|
33
|
-
"serialization of datetimes and other types that are not supported by the default json library.",
|
|
34
|
-
)
|
|
35
|
-
@click.option(
|
|
36
|
-
"--custom-template-path",
|
|
37
|
-
type=str,
|
|
38
|
-
default=None,
|
|
39
|
-
help="Custom template path to use. Allows overriding of the built in templates",
|
|
40
|
-
)
|
|
41
|
-
@click.option(
|
|
42
|
-
"--pydantic-version",
|
|
43
|
-
type=click.Choice(["v1", "v2"]),
|
|
44
|
-
default="v2",
|
|
45
|
-
show_default=True,
|
|
46
|
-
help="Pydantic version to use for generated models.",
|
|
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
|
-
)
|
|
55
|
-
@click.version_option(version=__version__)
|
|
56
|
-
def main(
|
|
57
|
-
source: str,
|
|
58
|
-
output: str,
|
|
59
|
-
library: Optional[HTTPLibrary] = HTTPLibrary.httpx,
|
|
60
|
-
env_token_name: Optional[str] = None,
|
|
61
|
-
use_orjson: bool = False,
|
|
62
|
-
custom_template_path: Optional[str] = None,
|
|
63
|
-
pydantic_version: PydanticVersion = PydanticVersion.V2,
|
|
64
|
-
formatter: Formatter = Formatter.BLACK,
|
|
65
|
-
) -> None:
|
|
66
|
-
"""
|
|
67
|
-
Generate Python code from an OpenAPI 3.0+ specification.
|
|
68
|
-
|
|
69
|
-
Provide a SOURCE (file or URL) containing the OpenAPI 3.0+ specification and
|
|
70
|
-
an OUTPUT path, where the resulting client is created.
|
|
71
|
-
"""
|
|
72
|
-
generate_data(
|
|
73
|
-
source,
|
|
74
|
-
output,
|
|
75
|
-
library if library is not None else HTTPLibrary.httpx,
|
|
76
|
-
env_token_name,
|
|
77
|
-
use_orjson,
|
|
78
|
-
custom_template_path,
|
|
79
|
-
pydantic_version,
|
|
80
|
-
formatter,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if __name__ == "__main__": # pragma: no cover
|
|
85
|
-
main()
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
|
-
from typing import Dict, Optional
|
|
3
|
-
|
|
4
|
-
from ab_openapi_python_generator.models import LibraryConfig
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class HTTPLibrary(str, Enum):
|
|
8
|
-
"""
|
|
9
|
-
Enum for the available HTTP libraries.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
httpx = "httpx"
|
|
13
|
-
requests = "requests"
|
|
14
|
-
aiohttp = "aiohttp"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class PydanticVersion(str, Enum):
|
|
18
|
-
V1 = "v1"
|
|
19
|
-
V2 = "v2"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class Formatter(str, Enum):
|
|
23
|
-
"""
|
|
24
|
-
Enum for the available code formatters.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
BLACK = "black"
|
|
28
|
-
NONE = "none"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class FormatOptions:
|
|
32
|
-
skip_validation: bool = False
|
|
33
|
-
line_length: int = 120
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
library_config_dict: Dict[Optional[HTTPLibrary], LibraryConfig] = {
|
|
37
|
-
HTTPLibrary.httpx: LibraryConfig(
|
|
38
|
-
name="httpx",
|
|
39
|
-
library_name="httpx",
|
|
40
|
-
template_name="httpx.jinja2",
|
|
41
|
-
include_async=True,
|
|
42
|
-
include_sync=True,
|
|
43
|
-
),
|
|
44
|
-
HTTPLibrary.requests: LibraryConfig(
|
|
45
|
-
name="requests",
|
|
46
|
-
library_name="requests",
|
|
47
|
-
template_name="requests.jinja2",
|
|
48
|
-
include_async=False,
|
|
49
|
-
include_sync=True,
|
|
50
|
-
),
|
|
51
|
-
HTTPLibrary.aiohttp: LibraryConfig(
|
|
52
|
-
name="aiohttp",
|
|
53
|
-
library_name="aiohttp",
|
|
54
|
-
template_name="aiohttp.jinja2",
|
|
55
|
-
include_async=True,
|
|
56
|
-
include_sync=False,
|
|
57
|
-
),
|
|
58
|
-
}
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import List, Optional, Union
|
|
3
|
-
|
|
4
|
-
import black
|
|
5
|
-
import click
|
|
6
|
-
import httpx
|
|
7
|
-
import isort
|
|
8
|
-
import orjson
|
|
9
|
-
import yaml # type: ignore
|
|
10
|
-
from black.report import NothingChanged # type: ignore
|
|
11
|
-
from httpx import ConnectError, ConnectTimeout
|
|
12
|
-
from pydantic import ValidationError
|
|
13
|
-
|
|
14
|
-
from .common import FormatOptions, Formatter, HTTPLibrary, PydanticVersion
|
|
15
|
-
from .language_converters.python.jinja_config import SERVICE_TEMPLATE, create_jinja_env
|
|
16
|
-
from .models import ConversionResult
|
|
17
|
-
from .parsers import (
|
|
18
|
-
generate_code_3_0,
|
|
19
|
-
generate_code_3_1,
|
|
20
|
-
parse_openapi_3_0,
|
|
21
|
-
parse_openapi_3_1,
|
|
22
|
-
)
|
|
23
|
-
from .version_detector import detect_openapi_version
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def write_code(path: Path, content: str, formatter: Formatter) -> None:
|
|
27
|
-
"""
|
|
28
|
-
Write the content to the file at the given path.
|
|
29
|
-
:param path: The path to the file.
|
|
30
|
-
:param content: The content to write.
|
|
31
|
-
:param formatter: The formatter applied to the code written.
|
|
32
|
-
"""
|
|
33
|
-
if formatter == Formatter.BLACK:
|
|
34
|
-
formatted_contend = format_using_black(content)
|
|
35
|
-
elif formatter == Formatter.NONE:
|
|
36
|
-
formatted_contend = content
|
|
37
|
-
else:
|
|
38
|
-
raise NotImplementedError(
|
|
39
|
-
f"Missing implementation for formatter {formatter!r}."
|
|
40
|
-
)
|
|
41
|
-
with open(path, "w") as f:
|
|
42
|
-
f.write(formatted_contend)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def format_using_black(content: str) -> str:
|
|
46
|
-
try:
|
|
47
|
-
formatted_contend = black.format_file_contents(
|
|
48
|
-
content,
|
|
49
|
-
fast=FormatOptions.skip_validation,
|
|
50
|
-
mode=black.FileMode(line_length=FormatOptions.line_length),
|
|
51
|
-
)
|
|
52
|
-
except NothingChanged:
|
|
53
|
-
return content
|
|
54
|
-
return isort.code(formatted_contend, line_length=FormatOptions.line_length)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def get_open_api(source: Union[str, Path]):
|
|
58
|
-
"""
|
|
59
|
-
Tries to fetch the openapi specification file from the web or load from a local file.
|
|
60
|
-
Supports both JSON and YAML formats. Returns the according OpenAPI object.
|
|
61
|
-
Automatically supports OpenAPI 3.0 and 3.1 specifications with intelligent version detection.
|
|
62
|
-
|
|
63
|
-
Args:
|
|
64
|
-
source: URL or file path to the OpenAPI specification
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
tuple: (OpenAPI object, version) where version is "3.0" or "3.1"
|
|
68
|
-
|
|
69
|
-
Raises:
|
|
70
|
-
FileNotFoundError: If the specified file cannot be found
|
|
71
|
-
ConnectError: If the URL cannot be accessed
|
|
72
|
-
ValidationError: If the specification is invalid
|
|
73
|
-
JSONDecodeError/YAMLError: If the file cannot be parsed
|
|
74
|
-
"""
|
|
75
|
-
try:
|
|
76
|
-
# Handle remote files
|
|
77
|
-
if not isinstance(source, Path) and (
|
|
78
|
-
source.startswith("http://") or source.startswith("https://")
|
|
79
|
-
):
|
|
80
|
-
content = httpx.get(source).text
|
|
81
|
-
# Try JSON first, then YAML for remote files
|
|
82
|
-
try:
|
|
83
|
-
data = orjson.loads(content)
|
|
84
|
-
except orjson.JSONDecodeError:
|
|
85
|
-
data = yaml.safe_load(content)
|
|
86
|
-
else:
|
|
87
|
-
# Handle local files
|
|
88
|
-
with open(source, "r") as f:
|
|
89
|
-
file_content = f.read()
|
|
90
|
-
|
|
91
|
-
# Try JSON first
|
|
92
|
-
try:
|
|
93
|
-
data = orjson.loads(file_content)
|
|
94
|
-
except orjson.JSONDecodeError:
|
|
95
|
-
# If JSON fails, try YAML
|
|
96
|
-
try:
|
|
97
|
-
data = yaml.safe_load(file_content)
|
|
98
|
-
except yaml.YAMLError as e:
|
|
99
|
-
click.echo(
|
|
100
|
-
f"File {source} is neither a valid JSON nor YAML file: {str(e)}"
|
|
101
|
-
)
|
|
102
|
-
raise
|
|
103
|
-
|
|
104
|
-
# Detect version and parse with appropriate parser
|
|
105
|
-
version = detect_openapi_version(data)
|
|
106
|
-
|
|
107
|
-
if version == "3.0":
|
|
108
|
-
openapi_obj = parse_openapi_3_0(data) # type: ignore[assignment]
|
|
109
|
-
elif version == "3.1":
|
|
110
|
-
openapi_obj = parse_openapi_3_1(data) # type: ignore[assignment]
|
|
111
|
-
else:
|
|
112
|
-
# Unsupported version detected (version detection already limited to 3.0 / 3.1)
|
|
113
|
-
raise ValueError(
|
|
114
|
-
f"Unsupported OpenAPI version: {version}. Only 3.0.x and 3.1.x are supported."
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
return openapi_obj, version
|
|
118
|
-
|
|
119
|
-
except FileNotFoundError:
|
|
120
|
-
click.echo(
|
|
121
|
-
f"File {source} not found. Please make sure to pass the path to the OpenAPI specification."
|
|
122
|
-
)
|
|
123
|
-
raise
|
|
124
|
-
except (ConnectError, ConnectTimeout):
|
|
125
|
-
click.echo(f"Could not connect to {source}.")
|
|
126
|
-
raise ConnectError(f"Could not connect to {source}.") from None
|
|
127
|
-
except ValidationError:
|
|
128
|
-
click.echo(f"File {source} is not a valid OpenAPI 3.0+ specification.")
|
|
129
|
-
raise
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def write_data(
|
|
133
|
-
data: ConversionResult, output: Union[str, Path], formatter: Formatter
|
|
134
|
-
) -> None:
|
|
135
|
-
"""
|
|
136
|
-
This function will firstly create the folder structure of output, if it doesn't exist. Then it will create the
|
|
137
|
-
models from data.models into the models sub module of the output folder. After this, the services will be created
|
|
138
|
-
into the services sub module of the output folder.
|
|
139
|
-
:param data: The data to write.
|
|
140
|
-
:param output: The path to the output folder.
|
|
141
|
-
:param formatter: The formatter applied to the code written.
|
|
142
|
-
"""
|
|
143
|
-
|
|
144
|
-
# Create the folder structure of the output folder.
|
|
145
|
-
Path(output).mkdir(parents=True, exist_ok=True)
|
|
146
|
-
|
|
147
|
-
# Create the models module.
|
|
148
|
-
models_path = Path(output) / "models"
|
|
149
|
-
models_path.mkdir(parents=True, exist_ok=True)
|
|
150
|
-
|
|
151
|
-
# Create the services module.
|
|
152
|
-
services_path = Path(output) / "services"
|
|
153
|
-
services_path.mkdir(parents=True, exist_ok=True)
|
|
154
|
-
|
|
155
|
-
files: List[str] = []
|
|
156
|
-
|
|
157
|
-
# Write the models.
|
|
158
|
-
for model in data.models:
|
|
159
|
-
files.append(model.file_name)
|
|
160
|
-
write_code(models_path / f"{model.file_name}.py", model.content, formatter)
|
|
161
|
-
|
|
162
|
-
# Create models.__init__.py file containing imports to all models.
|
|
163
|
-
write_code(
|
|
164
|
-
models_path / "__init__.py",
|
|
165
|
-
"\n".join([f"from .{file} import *" for file in files]),
|
|
166
|
-
formatter,
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
files = []
|
|
170
|
-
|
|
171
|
-
# Write the services.
|
|
172
|
-
jinja_env = create_jinja_env()
|
|
173
|
-
for service in data.services:
|
|
174
|
-
if len(service.operations) == 0:
|
|
175
|
-
continue
|
|
176
|
-
files.append(service.file_name)
|
|
177
|
-
write_code(
|
|
178
|
-
services_path / f"{service.file_name}.py",
|
|
179
|
-
jinja_env.get_template(SERVICE_TEMPLATE).render(**service.model_dump()),
|
|
180
|
-
formatter,
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
# Create services.__init__.py file containing imports to all services.
|
|
184
|
-
write_code(services_path / "__init__.py", "", formatter)
|
|
185
|
-
|
|
186
|
-
# Write the api_config.py file.
|
|
187
|
-
write_code(Path(output) / "api_config.py", data.api_config.content, formatter)
|
|
188
|
-
|
|
189
|
-
# Write the __init__.py file.
|
|
190
|
-
write_code(
|
|
191
|
-
Path(output) / "__init__.py",
|
|
192
|
-
"from .models import *\nfrom .services import *\nfrom .api_config import *",
|
|
193
|
-
formatter,
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def generate_data(
|
|
198
|
-
source: Union[str, Path],
|
|
199
|
-
output: Union[str, Path],
|
|
200
|
-
library: HTTPLibrary = HTTPLibrary.httpx,
|
|
201
|
-
env_token_name: Optional[str] = None,
|
|
202
|
-
use_orjson: bool = False,
|
|
203
|
-
custom_template_path: Optional[str] = None,
|
|
204
|
-
pydantic_version: PydanticVersion = PydanticVersion.V2,
|
|
205
|
-
formatter: Formatter = Formatter.BLACK,
|
|
206
|
-
) -> None:
|
|
207
|
-
"""
|
|
208
|
-
Generate Python code from an OpenAPI 3.0+ specification.
|
|
209
|
-
"""
|
|
210
|
-
openapi_obj, version = get_open_api(source)
|
|
211
|
-
click.echo(f"Generating data from {source} (OpenAPI {version})")
|
|
212
|
-
|
|
213
|
-
# Use version-specific generator
|
|
214
|
-
if version == "3.0":
|
|
215
|
-
result = generate_code_3_0(
|
|
216
|
-
openapi_obj, # type: ignore
|
|
217
|
-
library,
|
|
218
|
-
env_token_name,
|
|
219
|
-
use_orjson,
|
|
220
|
-
custom_template_path,
|
|
221
|
-
pydantic_version,
|
|
222
|
-
)
|
|
223
|
-
elif version == "3.1":
|
|
224
|
-
result = generate_code_3_1(
|
|
225
|
-
openapi_obj, # type: ignore
|
|
226
|
-
library,
|
|
227
|
-
env_token_name,
|
|
228
|
-
use_orjson,
|
|
229
|
-
custom_template_path,
|
|
230
|
-
pydantic_version,
|
|
231
|
-
)
|
|
232
|
-
else:
|
|
233
|
-
raise ValueError(f"Unsupported OpenAPI version: {version}")
|
|
234
|
-
|
|
235
|
-
write_data(result, output, formatter)
|
|
File without changes
|
|
File without changes
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
from openapi_pydantic.v3 import OpenAPI
|
|
4
|
-
|
|
5
|
-
from ab_openapi_python_generator.common import PydanticVersion
|
|
6
|
-
from ab_openapi_python_generator.language_converters.python.jinja_config import (
|
|
7
|
-
API_CONFIG_TEMPLATE,
|
|
8
|
-
API_CONFIG_TEMPLATE_PYDANTIC_V2,
|
|
9
|
-
create_jinja_env,
|
|
10
|
-
)
|
|
11
|
-
from ab_openapi_python_generator.models import APIConfig
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def generate_api_config(
|
|
15
|
-
data: OpenAPI,
|
|
16
|
-
env_token_name: Optional[str] = None,
|
|
17
|
-
pydantic_version: PydanticVersion = PydanticVersion.V2,
|
|
18
|
-
) -> APIConfig:
|
|
19
|
-
"""
|
|
20
|
-
Generate the API model.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
template_name = (
|
|
24
|
-
API_CONFIG_TEMPLATE_PYDANTIC_V2
|
|
25
|
-
if pydantic_version == PydanticVersion.V2
|
|
26
|
-
else API_CONFIG_TEMPLATE
|
|
27
|
-
)
|
|
28
|
-
jinja_env = create_jinja_env()
|
|
29
|
-
return APIConfig(
|
|
30
|
-
file_name="api_config",
|
|
31
|
-
content=jinja_env.get_template(template_name).render(
|
|
32
|
-
env_token_name=env_token_name, **data.model_dump()
|
|
33
|
-
),
|
|
34
|
-
base_url=data.servers[0].url if len(data.servers) > 0 else "NO SERVER",
|
|
35
|
-
)
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import keyword
|
|
2
|
-
import re
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
_use_orjson: bool = False
|
|
6
|
-
_custom_template_path: Optional[str] = None
|
|
7
|
-
_symbol_ascii_strip_re = re.compile(r"[^A-Za-z0-9_]")
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def set_use_orjson(value: bool) -> None:
|
|
11
|
-
"""
|
|
12
|
-
Set the value of the global variable _use_orjson.
|
|
13
|
-
:param value: value of the variable
|
|
14
|
-
"""
|
|
15
|
-
global _use_orjson
|
|
16
|
-
_use_orjson = value
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def get_use_orjson() -> bool:
|
|
20
|
-
"""
|
|
21
|
-
Get the value of the global variable _use_orjson.
|
|
22
|
-
:return: value of the variable
|
|
23
|
-
"""
|
|
24
|
-
global _use_orjson
|
|
25
|
-
return _use_orjson
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def set_custom_template_path(value: Optional[str]) -> None:
|
|
29
|
-
"""
|
|
30
|
-
Set the value of the global variable _custom_template_path.
|
|
31
|
-
:param value: value of the variable
|
|
32
|
-
"""
|
|
33
|
-
global _custom_template_path
|
|
34
|
-
_custom_template_path = value
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def get_custom_template_path() -> Optional[str]:
|
|
38
|
-
"""
|
|
39
|
-
Get the value of the global variable _custom_template_path.
|
|
40
|
-
:return: value of the variable
|
|
41
|
-
"""
|
|
42
|
-
global _custom_template_path
|
|
43
|
-
return _custom_template_path
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def normalize_symbol(symbol: str) -> str:
|
|
47
|
-
"""
|
|
48
|
-
Remove invalid characters & keywords in Python symbol names
|
|
49
|
-
:param symbol: name of the identifier
|
|
50
|
-
:return: normalized identifier name
|
|
51
|
-
"""
|
|
52
|
-
symbol = symbol.replace("-", "_").replace(" ", "_")
|
|
53
|
-
normalized_symbol = _symbol_ascii_strip_re.sub("", symbol)
|
|
54
|
-
if normalized_symbol in keyword.kwlist:
|
|
55
|
-
normalized_symbol = normalized_symbol + "_"
|
|
56
|
-
if len(normalized_symbol) > 0 and normalized_symbol[0].isnumeric():
|
|
57
|
-
normalized_symbol = "_" + normalized_symbol
|
|
58
|
-
return normalized_symbol
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
from typing import Optional, Union
|
|
2
|
-
|
|
3
|
-
from openapi_pydantic.v3.v3_0 import OpenAPI as OpenAPI30
|
|
4
|
-
from openapi_pydantic.v3.v3_1 import OpenAPI as OpenAPI31
|
|
5
|
-
|
|
6
|
-
from ab_openapi_python_generator.common import PydanticVersion
|
|
7
|
-
from ab_openapi_python_generator.language_converters.python import common
|
|
8
|
-
from ab_openapi_python_generator.language_converters.python.api_config_generator import (
|
|
9
|
-
generate_api_config,
|
|
10
|
-
)
|
|
11
|
-
from ab_openapi_python_generator.language_converters.python.model_generator import (
|
|
12
|
-
generate_models,
|
|
13
|
-
)
|
|
14
|
-
from ab_openapi_python_generator.language_converters.python.service_generator import (
|
|
15
|
-
generate_services,
|
|
16
|
-
)
|
|
17
|
-
from ab_openapi_python_generator.models import ConversionResult, LibraryConfig
|
|
18
|
-
|
|
19
|
-
# Type alias for both OpenAPI versions
|
|
20
|
-
OpenAPISpec = Union[OpenAPI30, OpenAPI31]
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def generator(
|
|
24
|
-
data: OpenAPISpec,
|
|
25
|
-
library_config: LibraryConfig,
|
|
26
|
-
env_token_name: Optional[str] = None,
|
|
27
|
-
use_orjson: bool = False,
|
|
28
|
-
custom_template_path: Optional[str] = None,
|
|
29
|
-
pydantic_version: PydanticVersion = PydanticVersion.V2,
|
|
30
|
-
) -> ConversionResult:
|
|
31
|
-
"""
|
|
32
|
-
Generate Python code from an OpenAPI 3.0+ specification.
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
common.set_use_orjson(use_orjson)
|
|
36
|
-
common.set_custom_template_path(custom_template_path)
|
|
37
|
-
|
|
38
|
-
if data.components is not None:
|
|
39
|
-
models = generate_models(data.components, pydantic_version)
|
|
40
|
-
else:
|
|
41
|
-
models = []
|
|
42
|
-
|
|
43
|
-
if data.paths is not None:
|
|
44
|
-
services = generate_services(data.paths, library_config)
|
|
45
|
-
else:
|
|
46
|
-
services = []
|
|
47
|
-
|
|
48
|
-
api_config = generate_api_config(data, env_token_name, pydantic_version)
|
|
49
|
-
|
|
50
|
-
return ConversionResult(
|
|
51
|
-
models=models,
|
|
52
|
-
services=services,
|
|
53
|
-
api_config=api_config,
|
|
54
|
-
)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
from jinja2 import ChoiceLoader, Environment, FileSystemLoader
|
|
4
|
-
|
|
5
|
-
from . import common
|
|
6
|
-
|
|
7
|
-
ENUM_TEMPLATE = "enum.jinja2"
|
|
8
|
-
MODELS_TEMPLATE = "models.jinja2"
|
|
9
|
-
MODELS_TEMPLATE_PYDANTIC_V2 = "models_pydantic_2.jinja2"
|
|
10
|
-
SERVICE_TEMPLATE = "service.jinja2"
|
|
11
|
-
HTTPX_TEMPLATE = "httpx.jinja2"
|
|
12
|
-
API_CONFIG_TEMPLATE = "apiconfig.jinja2"
|
|
13
|
-
API_CONFIG_TEMPLATE_PYDANTIC_V2 = "apiconfig_pydantic_2.jinja2"
|
|
14
|
-
ALIAS_UNION_TEMPLATE = "alias_union.jinja2"
|
|
15
|
-
DISCRIMINATOR_ENUM_TEMPLATE = "discriminator_enum.jinja2"
|
|
16
|
-
TEMPLATE_PATH = Path(__file__).parent / "templates"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def create_jinja_env():
|
|
20
|
-
custom_template_path = common.get_custom_template_path()
|
|
21
|
-
environment = Environment(
|
|
22
|
-
loader=(
|
|
23
|
-
ChoiceLoader(
|
|
24
|
-
[
|
|
25
|
-
FileSystemLoader(custom_template_path),
|
|
26
|
-
FileSystemLoader(TEMPLATE_PATH),
|
|
27
|
-
]
|
|
28
|
-
)
|
|
29
|
-
if custom_template_path is not None
|
|
30
|
-
else FileSystemLoader(TEMPLATE_PATH)
|
|
31
|
-
),
|
|
32
|
-
autoescape=True,
|
|
33
|
-
trim_blocks=True,
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
environment.filters["normalize_symbol"] = common.normalize_symbol
|
|
37
|
-
|
|
38
|
-
return environment
|