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,126 +0,0 @@
|
|
|
1
|
-
{# NOTE: this template renders ONE function. It must be valid standalone python. #}
|
|
2
|
-
|
|
3
|
-
{% if async_client %}async {% endif %}def {{ operation_id }}(
|
|
4
|
-
api_config_override: Optional[APIConfig] = None{% if params.strip() %}, *, {{ params.rstrip(', ') }}{% endif %}
|
|
5
|
-
) -> {% if is_sse %}{% if async_client %}AsyncGenerator[Any, None]{% else %}Iterator[Any]{% endif %}{% else %}{% if return_type.type is none or return_type.type.converted_type is none %}None{% else %}{{ return_type.type.converted_type }}{% endif %}{% endif %}:
|
|
6
|
-
api_config = api_config_override if api_config_override else APIConfig()
|
|
7
|
-
|
|
8
|
-
base_path = api_config.base_path
|
|
9
|
-
path = f'{{ path_name }}'
|
|
10
|
-
|
|
11
|
-
headers = {
|
|
12
|
-
"Content-Type": "application/json",
|
|
13
|
-
{% if is_sse %}
|
|
14
|
-
"Accept": "text/event-stream",
|
|
15
|
-
{% else %}
|
|
16
|
-
"Accept": "application/json",
|
|
17
|
-
{% endif %}
|
|
18
|
-
"Authorization": f"Bearer { api_config.get_access_token() }",
|
|
19
|
-
{{ header_params | join(',\n') | safe }}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
query_params: Dict[str, Any] = {}
|
|
23
|
-
query_params = {key: value for (key, value) in query_params.items() if value is not None}
|
|
24
|
-
|
|
25
|
-
{% if is_sse %}
|
|
26
|
-
import json
|
|
27
|
-
|
|
28
|
-
{% if async_client %}
|
|
29
|
-
async with httpx.AsyncClient(base_url=base_path, verify=api_config.verify) as client:
|
|
30
|
-
async with client.stream(
|
|
31
|
-
"{{ method }}",
|
|
32
|
-
httpx.URL(path),
|
|
33
|
-
headers=headers,
|
|
34
|
-
params=query_params,
|
|
35
|
-
{% if body_param %}
|
|
36
|
-
json={{ body_param }},
|
|
37
|
-
{% endif %}
|
|
38
|
-
) as response:
|
|
39
|
-
if response.status_code != {{ return_type.status_code }}:
|
|
40
|
-
raise HTTPException(
|
|
41
|
-
response.status_code,
|
|
42
|
-
f"{{ operation_id }} failed with status code: {response.status_code}",
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
async for line in response.aiter_lines():
|
|
46
|
-
if not line:
|
|
47
|
-
continue
|
|
48
|
-
if line.startswith("data:"):
|
|
49
|
-
payload = line[len("data:"):].strip()
|
|
50
|
-
if not payload:
|
|
51
|
-
continue
|
|
52
|
-
if payload == "[DONE]":
|
|
53
|
-
break
|
|
54
|
-
try:
|
|
55
|
-
yield json.loads(payload)
|
|
56
|
-
except Exception:
|
|
57
|
-
yield payload
|
|
58
|
-
{% else %}
|
|
59
|
-
with httpx.Client(base_url=base_path, verify=api_config.verify) as client:
|
|
60
|
-
with client.stream(
|
|
61
|
-
"{{ method }}",
|
|
62
|
-
httpx.URL(path),
|
|
63
|
-
headers=headers,
|
|
64
|
-
params=query_params,
|
|
65
|
-
{% if body_param %}
|
|
66
|
-
json={{ body_param }},
|
|
67
|
-
{% endif %}
|
|
68
|
-
) as response:
|
|
69
|
-
if response.status_code != {{ return_type.status_code }}:
|
|
70
|
-
raise HTTPException(
|
|
71
|
-
response.status_code,
|
|
72
|
-
f"{{ operation_id }} failed with status code: {response.status_code}",
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
for line in response.iter_lines():
|
|
76
|
-
if not line:
|
|
77
|
-
continue
|
|
78
|
-
if line.startswith("data:"):
|
|
79
|
-
payload = line[len("data:"):].strip()
|
|
80
|
-
if not payload:
|
|
81
|
-
continue
|
|
82
|
-
if payload == "[DONE]":
|
|
83
|
-
break
|
|
84
|
-
try:
|
|
85
|
-
yield json.loads(payload)
|
|
86
|
-
except Exception:
|
|
87
|
-
yield payload
|
|
88
|
-
{% endif %}
|
|
89
|
-
|
|
90
|
-
{% else %}
|
|
91
|
-
{% if async_client %}
|
|
92
|
-
async with httpx.AsyncClient(base_url=base_path, verify=api_config.verify) as client:
|
|
93
|
-
response = await client.request(
|
|
94
|
-
{% else %}
|
|
95
|
-
with httpx.Client(base_url=base_path, verify=api_config.verify) as client:
|
|
96
|
-
response = client.request(
|
|
97
|
-
{% endif %}
|
|
98
|
-
"{{ method }}",
|
|
99
|
-
httpx.URL(path),
|
|
100
|
-
headers=headers,
|
|
101
|
-
params=query_params,
|
|
102
|
-
{% if body_param %}
|
|
103
|
-
json={{ body_param }},
|
|
104
|
-
{% endif %}
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
if response.status_code != {{ return_type.status_code }}:
|
|
108
|
-
raise HTTPException(
|
|
109
|
-
response.status_code,
|
|
110
|
-
f"{{ operation_id }} failed with status code: {response.status_code}",
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
body = None if {{ return_type.status_code }} == 204 else response.json()
|
|
114
|
-
|
|
115
|
-
{% if return_type.type is none or return_type.type.converted_type is none %}
|
|
116
|
-
return None
|
|
117
|
-
{% elif return_type.complex_type %}
|
|
118
|
-
{% if return_type.list_type is none %}
|
|
119
|
-
return {{ return_type.type.converted_type }}(**body) if body is not None else {{ return_type.type.converted_type }}()
|
|
120
|
-
{% else %}
|
|
121
|
-
return [{{ return_type.list_type }}(**item) for item in body]
|
|
122
|
-
{% endif %}
|
|
123
|
-
{% else %}
|
|
124
|
-
return body
|
|
125
|
-
{% endif %}
|
|
126
|
-
{% endif %}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from typing import *
|
|
2
|
-
from pydantic import BaseModel, Field
|
|
3
|
-
{% for property in properties %}
|
|
4
|
-
{% if property.type.import_types is not none %}
|
|
5
|
-
{% for import_type in property.type.import_types %}
|
|
6
|
-
{{ import_type }}
|
|
7
|
-
{% endfor %}
|
|
8
|
-
{% endif %}
|
|
9
|
-
{% endfor %}
|
|
10
|
-
|
|
11
|
-
class {{ schema_name }}(BaseModel):
|
|
12
|
-
"""
|
|
13
|
-
{% if schema.title %}{{ schema.title }}{% else %}{{ schema_name }}{% endif %} model
|
|
14
|
-
{% if schema.description %}
|
|
15
|
-
{{ schema.description }}
|
|
16
|
-
{% endif %}
|
|
17
|
-
{% if parent_comment %}
|
|
18
|
-
{{ parent_comment }}
|
|
19
|
-
{% endif %}
|
|
20
|
-
"""
|
|
21
|
-
{% for property in properties %}
|
|
22
|
-
|
|
23
|
-
{{ property.name | normalize_symbol }} : {{ property.type.converted_type | safe }} = Field(alias="{{ property.name }}"{% if property.default is not none %}, default = {{ property.default }}{% endif %})
|
|
24
|
-
{% endfor %}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
from typing import *
|
|
2
|
-
from pydantic import BaseModel, Field
|
|
3
|
-
{% for property in properties %}
|
|
4
|
-
{% if property.type.import_types is not none %}
|
|
5
|
-
{% for import_type in property.type.import_types %}
|
|
6
|
-
{{ import_type }}
|
|
7
|
-
{% endfor %}
|
|
8
|
-
{% endif %}
|
|
9
|
-
{% endfor %}
|
|
10
|
-
|
|
11
|
-
class {{ schema_name }}(BaseModel):
|
|
12
|
-
"""
|
|
13
|
-
{% if schema.title %}{{ schema.title }}{% else %}{{ schema_name }}{% endif %} model
|
|
14
|
-
{% if schema.description %}
|
|
15
|
-
{{ schema.description }}
|
|
16
|
-
{% endif %}
|
|
17
|
-
{% if parent_comment %}
|
|
18
|
-
{{ parent_comment }}
|
|
19
|
-
{% endif %}
|
|
20
|
-
"""
|
|
21
|
-
model_config = {
|
|
22
|
-
"populate_by_name": True,
|
|
23
|
-
"validate_assignment": True
|
|
24
|
-
}
|
|
25
|
-
{% for property in properties %}
|
|
26
|
-
|
|
27
|
-
{{ property.name | normalize_symbol }} : {{ property.type.converted_type | safe }} = Field(validation_alias="{{ property.name }}"{% if property.default is not none %}, default = {{ property.default }}{% endif %})
|
|
28
|
-
{% endfor %}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
def {{ operation_id }}(api_config_override : Optional[APIConfig] = None{% if params.strip() %}, *, {{ params.rstrip(', ') }}{% endif %}) -> {% if return_type.type is none or return_type.type.converted_type is none %}None{% else %}{{ return_type.type.converted_type}}{% endif %}:
|
|
2
|
-
api_config = api_config_override if api_config_override else APIConfig()
|
|
3
|
-
|
|
4
|
-
base_path = api_config.base_path
|
|
5
|
-
path = f'{{ path_name }}'
|
|
6
|
-
headers = {
|
|
7
|
-
'Content-Type': 'application/json',
|
|
8
|
-
'Accept': 'application/json',
|
|
9
|
-
'Authorization': f'Bearer { api_config.get_access_token() }',
|
|
10
|
-
{{ header_params | join(',\n') | safe }}
|
|
11
|
-
}
|
|
12
|
-
query_params : Dict[str,Any] = {
|
|
13
|
-
{% if query_params|length > 0 %}
|
|
14
|
-
{{ query_params | join(',\n') | safe }}
|
|
15
|
-
{% endif %}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
query_params = {key:value for (key,value) in query_params.items() if value is not None}
|
|
19
|
-
|
|
20
|
-
response = requests.request(
|
|
21
|
-
'{{ method }}',
|
|
22
|
-
f'{base_path}{path}',
|
|
23
|
-
headers=headers,
|
|
24
|
-
params=query_params,
|
|
25
|
-
verify=api_config.verify,
|
|
26
|
-
{% if body_param %}
|
|
27
|
-
{% if use_orjson %}
|
|
28
|
-
content=orjson.dumps({{ body_param }})
|
|
29
|
-
{% else %}
|
|
30
|
-
json = {{ body_param }}
|
|
31
|
-
{% endif %}
|
|
32
|
-
{% endif %}
|
|
33
|
-
)
|
|
34
|
-
if response.status_code != {{ return_type.status_code }}:
|
|
35
|
-
raise HTTPException(response.status_code, f'{{ operation_id }} failed with status code: {response.status_code}')
|
|
36
|
-
else:
|
|
37
|
-
{# Conditional body parsing: avoid calling .json() for 204 #}
|
|
38
|
-
body = None if {{ return_type.status_code }} == 204 else response.json()
|
|
39
|
-
|
|
40
|
-
{% if return_type.type is none or return_type.type.converted_type is none %}
|
|
41
|
-
return None
|
|
42
|
-
{% elif return_type.complex_type %}
|
|
43
|
-
{%- if return_type.list_type is none %}
|
|
44
|
-
return {{ return_type.type.converted_type }}(**body) if body is not None else {{ return_type.type.converted_type }}()
|
|
45
|
-
{%- else %}
|
|
46
|
-
return [{{ return_type.list_type }}(**item) for item in body]
|
|
47
|
-
{%- endif %}
|
|
48
|
-
{% else %}
|
|
49
|
-
return body
|
|
50
|
-
{% endif %}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
from typing import List, Optional, Union
|
|
2
|
-
|
|
3
|
-
from openapi_pydantic.v3.v3_0 import (
|
|
4
|
-
Operation as Operation30,
|
|
5
|
-
)
|
|
6
|
-
from openapi_pydantic.v3.v3_0 import (
|
|
7
|
-
PathItem as PathItem30,
|
|
8
|
-
)
|
|
9
|
-
from openapi_pydantic.v3.v3_0 import (
|
|
10
|
-
Schema as Schema30,
|
|
11
|
-
)
|
|
12
|
-
from openapi_pydantic.v3.v3_1 import (
|
|
13
|
-
Operation as Operation31,
|
|
14
|
-
)
|
|
15
|
-
from openapi_pydantic.v3.v3_1 import (
|
|
16
|
-
PathItem as PathItem31,
|
|
17
|
-
)
|
|
18
|
-
from openapi_pydantic.v3.v3_1 import (
|
|
19
|
-
Schema as Schema31,
|
|
20
|
-
)
|
|
21
|
-
from pydantic import BaseModel
|
|
22
|
-
|
|
23
|
-
# Type unions for compatibility with both OpenAPI 3.0 and 3.1
|
|
24
|
-
Operation = Union[Operation30, Operation31]
|
|
25
|
-
PathItem = Union[PathItem30, PathItem31]
|
|
26
|
-
Schema = Union[Schema30, Schema31]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class LibraryConfig(BaseModel):
|
|
30
|
-
name: str
|
|
31
|
-
library_name: str
|
|
32
|
-
template_name: str
|
|
33
|
-
include_async: bool
|
|
34
|
-
include_sync: bool
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class TypeConversion(BaseModel):
|
|
38
|
-
original_type: str
|
|
39
|
-
converted_type: str
|
|
40
|
-
import_types: Optional[List[str]] = None
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class OpReturnType(BaseModel):
|
|
44
|
-
type: Optional[TypeConversion] = None
|
|
45
|
-
status_code: int
|
|
46
|
-
complex_type: bool = False
|
|
47
|
-
list_type: Optional[str] = None
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class ServiceOperation(BaseModel):
|
|
51
|
-
params: str
|
|
52
|
-
operation_id: str
|
|
53
|
-
query_params: List[str]
|
|
54
|
-
header_params: List[str]
|
|
55
|
-
return_type: OpReturnType
|
|
56
|
-
operation: Operation
|
|
57
|
-
pathItem: PathItem
|
|
58
|
-
content: str
|
|
59
|
-
async_client: Optional[bool] = False
|
|
60
|
-
tag: Optional[str] = None
|
|
61
|
-
path_name: str
|
|
62
|
-
body_param: Optional[str] = None
|
|
63
|
-
method: str
|
|
64
|
-
is_sse: bool = False
|
|
65
|
-
use_orjson: bool = False
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class Property(BaseModel):
|
|
69
|
-
name: str
|
|
70
|
-
type: TypeConversion
|
|
71
|
-
required: bool
|
|
72
|
-
default: Optional[str]
|
|
73
|
-
import_type: Optional[List[str]] = None
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class Model(BaseModel):
|
|
77
|
-
file_name: str
|
|
78
|
-
content: str
|
|
79
|
-
openapi_object: Schema
|
|
80
|
-
properties: List[Property] = []
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
class Service(BaseModel):
|
|
84
|
-
file_name: str
|
|
85
|
-
operations: List[ServiceOperation]
|
|
86
|
-
content: str
|
|
87
|
-
async_client: Optional[bool] = False
|
|
88
|
-
library_import: str
|
|
89
|
-
use_orjson: bool = False
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
class APIConfig(BaseModel):
|
|
93
|
-
file_name: str
|
|
94
|
-
base_url: str
|
|
95
|
-
content: str
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
class ConversionResult(BaseModel):
|
|
99
|
-
models: List[Model]
|
|
100
|
-
services: List[Service]
|
|
101
|
-
api_config: APIConfig
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
OpenAPI parsers for different specification versions.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from .openapi_30 import generate_code_3_0, parse_openapi_3_0
|
|
6
|
-
from .openapi_31 import generate_code_3_1, parse_openapi_3_1
|
|
7
|
-
|
|
8
|
-
__all__ = [
|
|
9
|
-
"parse_openapi_3_0",
|
|
10
|
-
"generate_code_3_0",
|
|
11
|
-
"parse_openapi_3_1",
|
|
12
|
-
"generate_code_3_1",
|
|
13
|
-
]
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
OpenAPI 3.0 specific parsing and generation.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from typing import Optional
|
|
6
|
-
|
|
7
|
-
from openapi_pydantic.v3.v3_0 import OpenAPI
|
|
8
|
-
|
|
9
|
-
from ab_openapi_python_generator.common import HTTPLibrary, PydanticVersion
|
|
10
|
-
from ab_openapi_python_generator.language_converters.python.generator import (
|
|
11
|
-
generator as base_generator,
|
|
12
|
-
)
|
|
13
|
-
from ab_openapi_python_generator.models import ConversionResult
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def parse_openapi_3_0(spec_data: dict) -> OpenAPI:
|
|
17
|
-
"""
|
|
18
|
-
Parse OpenAPI 3.0 specification data.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
spec_data: Dictionary containing OpenAPI 3.0 specification
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
OpenAPI: Parsed OpenAPI 3.0 specification object
|
|
25
|
-
|
|
26
|
-
Raises:
|
|
27
|
-
ValidationError: If the specification is invalid
|
|
28
|
-
"""
|
|
29
|
-
return OpenAPI(**spec_data) # type: ignore - pydantic issue with extra fields
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def generate_code_3_0(
|
|
33
|
-
data: OpenAPI,
|
|
34
|
-
library: HTTPLibrary = HTTPLibrary.httpx,
|
|
35
|
-
env_token_name: Optional[str] = None,
|
|
36
|
-
use_orjson: bool = False,
|
|
37
|
-
custom_template_path: Optional[str] = None,
|
|
38
|
-
pydantic_version: PydanticVersion = PydanticVersion.V2,
|
|
39
|
-
) -> ConversionResult:
|
|
40
|
-
"""
|
|
41
|
-
Generate Python code from OpenAPI 3.0 specification.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
data: OpenAPI 3.0 specification object
|
|
45
|
-
library: HTTP library to use
|
|
46
|
-
env_token_name: Environment variable name for token
|
|
47
|
-
use_orjson: Whether to use orjson for serialization
|
|
48
|
-
custom_template_path: Custom template path
|
|
49
|
-
pydantic_version: Pydantic version to use
|
|
50
|
-
|
|
51
|
-
Returns:
|
|
52
|
-
ConversionResult: Generated code and metadata
|
|
53
|
-
"""
|
|
54
|
-
from ab_openapi_python_generator.common import library_config_dict
|
|
55
|
-
|
|
56
|
-
library_config = library_config_dict[library]
|
|
57
|
-
|
|
58
|
-
return base_generator(
|
|
59
|
-
data=data,
|
|
60
|
-
library_config=library_config,
|
|
61
|
-
env_token_name=env_token_name,
|
|
62
|
-
use_orjson=use_orjson,
|
|
63
|
-
custom_template_path=custom_template_path,
|
|
64
|
-
pydantic_version=pydantic_version,
|
|
65
|
-
)
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
OpenAPI 3.1 specific parsing and generation.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from typing import Optional
|
|
6
|
-
|
|
7
|
-
from openapi_pydantic.v3.v3_1 import OpenAPI
|
|
8
|
-
|
|
9
|
-
from ab_openapi_python_generator.common import HTTPLibrary, PydanticVersion
|
|
10
|
-
from ab_openapi_python_generator.language_converters.python.generator import (
|
|
11
|
-
generator as base_generator,
|
|
12
|
-
)
|
|
13
|
-
from ab_openapi_python_generator.models import ConversionResult
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def parse_openapi_3_1(spec_data: dict) -> OpenAPI:
|
|
17
|
-
"""
|
|
18
|
-
Parse OpenAPI 3.1 specification data.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
spec_data: Dictionary containing OpenAPI 3.1 specification
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
OpenAPI: Parsed OpenAPI 3.1 specification object
|
|
25
|
-
|
|
26
|
-
Raises:
|
|
27
|
-
ValidationError: If the specification is invalid
|
|
28
|
-
"""
|
|
29
|
-
return OpenAPI(**spec_data) # type: ignore - pydantic issue with extra fields
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def generate_code_3_1(
|
|
33
|
-
data: OpenAPI,
|
|
34
|
-
library: HTTPLibrary = HTTPLibrary.httpx,
|
|
35
|
-
env_token_name: Optional[str] = None,
|
|
36
|
-
use_orjson: bool = False,
|
|
37
|
-
custom_template_path: Optional[str] = None,
|
|
38
|
-
pydantic_version: PydanticVersion = PydanticVersion.V2,
|
|
39
|
-
) -> ConversionResult:
|
|
40
|
-
"""
|
|
41
|
-
Generate Python code from OpenAPI 3.1 specification.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
data: OpenAPI 3.1 specification object
|
|
45
|
-
library: HTTP library to use
|
|
46
|
-
env_token_name: Environment variable name for token
|
|
47
|
-
use_orjson: Whether to use orjson for serialization
|
|
48
|
-
custom_template_path: Custom template path
|
|
49
|
-
pydantic_version: Pydantic version to use
|
|
50
|
-
|
|
51
|
-
Returns:
|
|
52
|
-
ConversionResult: Generated code and metadata
|
|
53
|
-
"""
|
|
54
|
-
from ab_openapi_python_generator.common import library_config_dict
|
|
55
|
-
|
|
56
|
-
library_config = library_config_dict[library]
|
|
57
|
-
|
|
58
|
-
return base_generator(
|
|
59
|
-
data=data,
|
|
60
|
-
library_config=library_config,
|
|
61
|
-
env_token_name=env_token_name,
|
|
62
|
-
use_orjson=use_orjson,
|
|
63
|
-
custom_template_path=custom_template_path,
|
|
64
|
-
pydantic_version=pydantic_version,
|
|
65
|
-
)
|
|
File without changes
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
OpenAPI version detection utilities.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from typing import Any, Dict, Literal
|
|
6
|
-
|
|
7
|
-
OpenAPIVersion = Literal["3.0", "3.1"]
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def detect_openapi_version(spec_data: Dict[str, Any]) -> OpenAPIVersion:
|
|
11
|
-
"""
|
|
12
|
-
Detect the OpenAPI version from specification data.
|
|
13
|
-
|
|
14
|
-
Performs basic validation to ensure the specification is well-formed enough
|
|
15
|
-
to route to the appropriate parser. The actual parser will handle detailed
|
|
16
|
-
validation of the specification content.
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
spec_data: Dictionary containing OpenAPI specification
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
OpenAPIVersion: Either "3.0" or "3.1"
|
|
23
|
-
|
|
24
|
-
Raises:
|
|
25
|
-
ValueError: If the specification is malformed or has unsupported version
|
|
26
|
-
"""
|
|
27
|
-
# Basic validation: must be a dictionary
|
|
28
|
-
if not isinstance(spec_data, dict):
|
|
29
|
-
raise ValueError("OpenAPI specification must be a dictionary/object")
|
|
30
|
-
|
|
31
|
-
# Basic validation: must have openapi field
|
|
32
|
-
if "openapi" not in spec_data:
|
|
33
|
-
raise ValueError("Missing required 'openapi' field in specification")
|
|
34
|
-
|
|
35
|
-
openapi_version = spec_data.get("openapi")
|
|
36
|
-
|
|
37
|
-
# Basic validation: openapi field must be a string
|
|
38
|
-
if not isinstance(openapi_version, str):
|
|
39
|
-
raise ValueError("'openapi' field must be a string")
|
|
40
|
-
|
|
41
|
-
# Basic validation: must not be empty
|
|
42
|
-
if not openapi_version.strip():
|
|
43
|
-
raise ValueError("'openapi' field cannot be empty")
|
|
44
|
-
|
|
45
|
-
# Version detection
|
|
46
|
-
if openapi_version.startswith("3.0"):
|
|
47
|
-
return "3.0"
|
|
48
|
-
elif openapi_version.startswith("3.1"):
|
|
49
|
-
return "3.1"
|
|
50
|
-
else:
|
|
51
|
-
raise ValueError(
|
|
52
|
-
f"Unsupported OpenAPI version: {openapi_version}. "
|
|
53
|
-
f"Only OpenAPI 3.0.x and 3.1.x are supported."
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def is_openapi_30(spec_data: Dict[str, Any]) -> bool:
|
|
58
|
-
"""Check if the specification is OpenAPI 3.0.x"""
|
|
59
|
-
try:
|
|
60
|
-
return detect_openapi_version(spec_data) == "3.0"
|
|
61
|
-
except ValueError:
|
|
62
|
-
return False
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def is_openapi_31(spec_data: Dict[str, Any]) -> bool:
|
|
66
|
-
"""Check if the specification is OpenAPI 3.1.x"""
|
|
67
|
-
try:
|
|
68
|
-
return detect_openapi_version(spec_data) == "3.1"
|
|
69
|
-
except ValueError:
|
|
70
|
-
return False
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
ab_openapi_python_generator/__init__.py,sha256=ph2EznAu4nkHFruaG542m4TQ_xAvDlf36uKxtAkVP1s,460
|
|
2
|
-
ab_openapi_python_generator/__main__.py,sha256=Vz0FsBvxQtyozKkZZFK_ag8XgJcx-5obRm44ogjD0Wk,2500
|
|
3
|
-
ab_openapi_python_generator/common.py,sha256=rh6AxoWyL-jb5WbeLhiohvFUBUnevn4N7ixHPKTIiDM,1221
|
|
4
|
-
ab_openapi_python_generator/generate_data.py,sha256=Av7nXE6BvsE9eHWF6eOBhPCZWWC9pi-lvksAJGB6MA8,8022
|
|
5
|
-
ab_openapi_python_generator/models.py,sha256=FDnO9hKa3w05quxuQRe4bJDvRJte-uS1GwjPp4VSrBo,2199
|
|
6
|
-
ab_openapi_python_generator/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
ab_openapi_python_generator/version_detector.py,sha256=Cn7iG6_EnBdmjGmefJQ1cIBzbT28F0BeCEpoqksBGyQ,2142
|
|
8
|
-
ab_openapi_python_generator/language_converters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
ab_openapi_python_generator/language_converters/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
ab_openapi_python_generator/language_converters/python/api_config_generator.py,sha256=SSA-jac7SpP_tpzwc37Vw0VYvSfx0KlsOFeF9Bioe0g,1029
|
|
11
|
-
ab_openapi_python_generator/language_converters/python/common.py,sha256=gZHNhVC4OwGBlmr3h9Ffz3iILO1ykva9CrqhBfeYQXU,1616
|
|
12
|
-
ab_openapi_python_generator/language_converters/python/generator.py,sha256=VX8glNSu-pHo7GKYCVaP3k6JZKKGluYZq7zRZL0fDVE,1699
|
|
13
|
-
ab_openapi_python_generator/language_converters/python/jinja_config.py,sha256=1xhwJAkk2BBaEzlOI-rgPWY6xjvhjrVEUqrl3rLU1uI,1144
|
|
14
|
-
ab_openapi_python_generator/language_converters/python/model_generator.py,sha256=jvtMwV-SKjvXUn5sv-xULsBrsQmSj9UrK4zZ4r5n4oI,35347
|
|
15
|
-
ab_openapi_python_generator/language_converters/python/service_generator.py,sha256=uVdFyAmVPfCbWwhSW7G-9Z0qEBpqpgKr8af2MbdoQhg,20107
|
|
16
|
-
ab_openapi_python_generator/language_converters/python/templates/aiohttp.jinja2,sha256=Ym3-KX8JH9-V75GqkfcXVgh7rnqlkOQU5DLaNd1Wma0,2135
|
|
17
|
-
ab_openapi_python_generator/language_converters/python/templates/alias_union.jinja2,sha256=tndnrDky4srNRyB_HnUMZFW7HtoJvEfwIFteYIu0ELc,468
|
|
18
|
-
ab_openapi_python_generator/language_converters/python/templates/apiconfig.jinja2,sha256=bvYMJV6YB2NZLLcqwozISdS86XLH8J6D8o3ZpfgJCjc,1256
|
|
19
|
-
ab_openapi_python_generator/language_converters/python/templates/apiconfig_pydantic_2.jinja2,sha256=_MzcHYEq9aoqSUEhZ-5SLbO80zgBeJnx5d7q3Q_usSo,1319
|
|
20
|
-
ab_openapi_python_generator/language_converters/python/templates/discriminator_enum.jinja2,sha256=GJZ_ih1eURnJRPZq5P_3aKhYB5iJHymj2fpuNSdM90Y,163
|
|
21
|
-
ab_openapi_python_generator/language_converters/python/templates/enum.jinja2,sha256=7dewyMLifUTe9xjCTE62deuncPiNkp7eGkIaY-IWsLo,263
|
|
22
|
-
ab_openapi_python_generator/language_converters/python/templates/httpx.jinja2,sha256=zblT5_shg0vvggL0dJRz4GhGZs2bfRoeQb9YSyHLjrs,4586
|
|
23
|
-
ab_openapi_python_generator/language_converters/python/templates/models.jinja2,sha256=s5PllbxiEENsZ5_fKSh1gKMJWhcLfBdH5744nKnAZsQ,797
|
|
24
|
-
ab_openapi_python_generator/language_converters/python/templates/models_pydantic_2.jinja2,sha256=o6-OlV5upoZOAVx6aQbsaigPMTCi4Uuec-oP1FNDJ60,904
|
|
25
|
-
ab_openapi_python_generator/language_converters/python/templates/requests.jinja2,sha256=8R3S5M26vQol-rXHuNVYuG4qc_skC9-G8x-6AZlqTXg,2043
|
|
26
|
-
ab_openapi_python_generator/language_converters/python/templates/service.jinja2,sha256=tt0Iu_JLaAlMPU73KTM7yntQjnkdvve53ShjgAGcmqY,213
|
|
27
|
-
ab_openapi_python_generator/parsers/__init__.py,sha256=UITDIV7rp3HAA1M8kQc_9cNG6e1tS2vDa4mTKua6aMY,300
|
|
28
|
-
ab_openapi_python_generator/parsers/openapi_30.py,sha256=2Hq5Vl3V4NUHKol-OO2nmt8-Fh6Lqk-hJUys8s-4UNI,1924
|
|
29
|
-
ab_openapi_python_generator/parsers/openapi_31.py,sha256=jyKYAKDRkqKUPSeP6xZuM6ZCE0JjuowAe_pbm01iqWE,1924
|
|
30
|
-
ab_openapi_python_generator-2.1.4.dist-info/METADATA,sha256=Ej6Sl2TNph4vKnfEixyxqRy6O1jENT7JxGSX0CXfswU,5186
|
|
31
|
-
ab_openapi_python_generator-2.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
32
|
-
ab_openapi_python_generator-2.1.4.dist-info/entry_points.txt,sha256=zezbCs2qP8lSUbcjL6cN5XHoHxf9JTe15CZYH8Yuzo0,90
|
|
33
|
-
ab_openapi_python_generator-2.1.4.dist-info/licenses/LICENSE,sha256=0myanGwJ2vUOZN12aN95o0My6XEysnnVlbKikYw3pHg,1070
|
|
34
|
-
ab_openapi_python_generator-2.1.4.dist-info/RECORD,,
|