otterapi 0.0.4__tar.gz → 0.0.5__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.
- {otterapi-0.0.4 → otterapi-0.0.5}/.gitignore +3 -0
- {otterapi-0.0.4 → otterapi-0.0.5}/PKG-INFO +1 -1
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/cli.py +1 -1
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/codegen/ast_utils.py +3 -0
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/codegen/endpoints.py +8 -6
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/codegen/generator.py +7 -1
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/codegen/type_generator.py +29 -8
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/codegen/utils.py +27 -0
- {otterapi-0.0.4 → otterapi-0.0.5}/pyproject.toml +3 -3
- {otterapi-0.0.4 → otterapi-0.0.5}/README.md +0 -0
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/__init__.py +0 -0
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/__main__.py +0 -0
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/codegen/__init__.py +0 -0
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/codegen/openapi_processor.py +0 -0
- {otterapi-0.0.4 → otterapi-0.0.5}/otterapi/config.py +0 -0
|
@@ -244,14 +244,16 @@ def base_async_request_fn():
|
|
|
244
244
|
|
|
245
245
|
def get_parameters(
|
|
246
246
|
parameters: list[Parameter],
|
|
247
|
-
) -> tuple[list[ast.arg], list[ast.arg], list[ast.expr]]:
|
|
247
|
+
) -> tuple[list[ast.arg], list[ast.arg], list[ast.expr], dict[str, set[str]]]:
|
|
248
248
|
args = []
|
|
249
249
|
kwonlyargs = []
|
|
250
250
|
kw_defaults = []
|
|
251
251
|
imports = {}
|
|
252
252
|
|
|
253
253
|
for param in parameters:
|
|
254
|
-
|
|
254
|
+
if param.name == 'user-id':
|
|
255
|
+
print()
|
|
256
|
+
param_name = param.name_sanitized
|
|
255
257
|
param_type = param.type.annotation_ast if param.type else None
|
|
256
258
|
param_required = param.required
|
|
257
259
|
|
|
@@ -294,7 +296,7 @@ def build_header_params(headers: list[Parameter]) -> ast.Dict | None:
|
|
|
294
296
|
|
|
295
297
|
return ast.Dict(
|
|
296
298
|
keys=[ast.Constant(value=header.name) for header in headers],
|
|
297
|
-
values=[_name(header.
|
|
299
|
+
values=[_name(header.name_sanitized) for header in headers],
|
|
298
300
|
)
|
|
299
301
|
|
|
300
302
|
|
|
@@ -304,7 +306,7 @@ def build_query_params(queries: list[Parameter]) -> ast.Dict | None:
|
|
|
304
306
|
|
|
305
307
|
return ast.Dict(
|
|
306
308
|
keys=[ast.Constant(value=query.name) for query in queries],
|
|
307
|
-
values=[_name(query.
|
|
309
|
+
values=[_name(query.name_sanitized) for query in queries],
|
|
308
310
|
)
|
|
309
311
|
|
|
310
312
|
|
|
@@ -332,7 +334,7 @@ def build_path_params(
|
|
|
332
334
|
# Add the formatted value for the parameter
|
|
333
335
|
values.append(
|
|
334
336
|
ast.FormattedValue(
|
|
335
|
-
value=_name(path_param.
|
|
337
|
+
value=_name(path_param.name_sanitized),
|
|
336
338
|
conversion=-1, # No conversion (default)
|
|
337
339
|
)
|
|
338
340
|
)
|
|
@@ -353,7 +355,7 @@ def build_body_params(body: Parameter | None) -> ast.expr | None:
|
|
|
353
355
|
|
|
354
356
|
if body.type.type == 'model' or body.type.type == 'root_model':
|
|
355
357
|
return _call(
|
|
356
|
-
func=_attr(_name(body.
|
|
358
|
+
func=_attr(_name(body.name_sanitized), 'model_dump'),
|
|
357
359
|
args=[],
|
|
358
360
|
)
|
|
359
361
|
|
|
@@ -16,7 +16,11 @@ from otterapi.codegen.ast_utils import _all, _assign, _call, _name, _union_expr
|
|
|
16
16
|
from otterapi.codegen.endpoints import async_request_fn, request_fn
|
|
17
17
|
from otterapi.codegen.openapi_processor import OpenAPIProcessor
|
|
18
18
|
from otterapi.codegen.type_generator import Endpoint, Parameter, Type, TypeGen
|
|
19
|
-
from otterapi.codegen.utils import
|
|
19
|
+
from otterapi.codegen.utils import (
|
|
20
|
+
is_url,
|
|
21
|
+
sanitize_identifier,
|
|
22
|
+
sanitize_parameter_field_name,
|
|
23
|
+
)
|
|
20
24
|
from otterapi.config import DocumentConfig
|
|
21
25
|
|
|
22
26
|
HTTP_METHODS = [method.value.lower() for method in http.HTTPMethod]
|
|
@@ -89,6 +93,7 @@ class Codegen(OpenAPIProcessor):
|
|
|
89
93
|
params.append(
|
|
90
94
|
Parameter(
|
|
91
95
|
name=param.name,
|
|
96
|
+
name_sanitized=sanitize_parameter_field_name(param.name),
|
|
92
97
|
location=param.param_in, # query, path, header, cookie
|
|
93
98
|
required=param.required or False,
|
|
94
99
|
type=param_type,
|
|
@@ -114,6 +119,7 @@ class Codegen(OpenAPIProcessor):
|
|
|
114
119
|
params.append(
|
|
115
120
|
Parameter(
|
|
116
121
|
name='body',
|
|
122
|
+
name_sanitized='body',
|
|
117
123
|
location='body',
|
|
118
124
|
required=body.required or False,
|
|
119
125
|
type=body_type,
|
|
@@ -10,7 +10,7 @@ from pydantic import BaseModel, Field, RootModel
|
|
|
10
10
|
|
|
11
11
|
from otterapi.codegen.ast_utils import _call, _name, _subscript, _union_expr
|
|
12
12
|
from otterapi.codegen.openapi_processor import OpenAPIProcessor
|
|
13
|
-
from otterapi.codegen.utils import sanitize_identifier
|
|
13
|
+
from otterapi.codegen.utils import sanitize_identifier, sanitize_parameter_field_name
|
|
14
14
|
|
|
15
15
|
_PRIMITIVE_TYPE_MAP = {
|
|
16
16
|
('string', None): str,
|
|
@@ -126,6 +126,7 @@ class Type:
|
|
|
126
126
|
@dataclasses.dataclass
|
|
127
127
|
class Parameter:
|
|
128
128
|
name: str
|
|
129
|
+
name_sanitized: str
|
|
129
130
|
location: str # query, path, header, cookie, body
|
|
130
131
|
required: bool
|
|
131
132
|
type: Type | None = None
|
|
@@ -261,18 +262,38 @@ class TypeGen(OpenAPIProcessor):
|
|
|
261
262
|
if hasattr(field_schema, 'ref'):
|
|
262
263
|
field_schema, _ = self._resolve_reference(field_schema)
|
|
263
264
|
|
|
265
|
+
field_keywords = list()
|
|
266
|
+
|
|
267
|
+
sanitized_field_name = sanitize_parameter_field_name(field_name)
|
|
268
|
+
|
|
264
269
|
value = None
|
|
265
270
|
if field_schema.default is not None and isinstance(
|
|
266
271
|
field_schema.default, (str, int, float, bool)
|
|
267
272
|
):
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
keywords=[
|
|
271
|
-
ast.keyword(arg='default', value=ast.Constant(field_schema.default))
|
|
272
|
-
],
|
|
273
|
+
field_keywords.append(
|
|
274
|
+
ast.keyword(arg='default', value=ast.Constant(field_schema.default))
|
|
273
275
|
)
|
|
274
276
|
elif field_schema.default is None and not field_schema.required:
|
|
275
|
-
value
|
|
277
|
+
field_keywords.append(ast.keyword(arg='default', value=ast.Constant(None)))
|
|
278
|
+
|
|
279
|
+
if sanitized_field_name != field_name:
|
|
280
|
+
field_keywords.append(
|
|
281
|
+
ast.keyword(
|
|
282
|
+
arg='alias',
|
|
283
|
+
value=ast.Constant(field_name), # original name before adding _
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
field_name = sanitized_field_name
|
|
287
|
+
|
|
288
|
+
if field_keywords:
|
|
289
|
+
value = _call(
|
|
290
|
+
func=_name(Field.__name__),
|
|
291
|
+
keywords=field_keywords,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
field_type.add_implementation_import(
|
|
295
|
+
module=Field.__module__, name=Field.__name__
|
|
296
|
+
)
|
|
276
297
|
|
|
277
298
|
return ast.AnnAssign(
|
|
278
299
|
target=_name(field_name),
|
|
@@ -498,7 +519,7 @@ class TypeGen(OpenAPIProcessor):
|
|
|
498
519
|
type_ = self._create_array_type(
|
|
499
520
|
schema=schema, name=schema_name, base_name=base_name
|
|
500
521
|
)
|
|
501
|
-
elif schema.type == DataType.OBJECT or schema.type
|
|
522
|
+
elif schema.type == DataType.OBJECT or schema.type is None:
|
|
502
523
|
type_ = self._create_object_type(
|
|
503
524
|
schema, name=schema_name, base_name=base_name
|
|
504
525
|
)
|
|
@@ -24,6 +24,33 @@ def remove_accents(input_str):
|
|
|
24
24
|
return ''.join(c for c in nfkd_form if not unicodedata.combining(c))
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
def sanitize_name_python_keywords(name: str) -> str:
|
|
28
|
+
import keyword
|
|
29
|
+
|
|
30
|
+
if name in keyword.kwlist:
|
|
31
|
+
return f'{name}_'
|
|
32
|
+
return name
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def sanitize_parameter_field_name(name: str) -> str:
|
|
36
|
+
"""Sanitize parameter or field names to be valid Python identifiers.
|
|
37
|
+
|
|
38
|
+
- Replace spaces and hyphens with underscores
|
|
39
|
+
- Remove other invalid characters
|
|
40
|
+
- Ensure it doesn't start with a digit
|
|
41
|
+
"""
|
|
42
|
+
if not name:
|
|
43
|
+
raise ValueError('Name cannot be empty')
|
|
44
|
+
|
|
45
|
+
sanitized = sanitize_name_python_keywords(name)
|
|
46
|
+
sanitized = re.sub(r'[-\s]+', '_', remove_accents(sanitized))
|
|
47
|
+
sanitized = re.sub(r'[^A-Za-z0-9_]', '', sanitized)
|
|
48
|
+
|
|
49
|
+
if sanitized and sanitized[0].isdigit():
|
|
50
|
+
sanitized = '_' + sanitized
|
|
51
|
+
return sanitized
|
|
52
|
+
|
|
53
|
+
|
|
27
54
|
def sanitize_identifier(name: str) -> str:
|
|
28
55
|
"""Convert a string into a valid Python identifier.
|
|
29
56
|
|
|
@@ -76,17 +76,17 @@ exclude = [".venv", "venv", "build", "dist", "__pycache__", ".eggs"]
|
|
|
76
76
|
extend-select = [
|
|
77
77
|
"Q",
|
|
78
78
|
"RUF100",
|
|
79
|
-
"RUF018", # https://docs.astral.sh/ruff/rules/assignment-in-assert
|
|
80
|
-
"C90",
|
|
79
|
+
"RUF018", # https://docs.astral.sh/ruff/rules/assignment-in-assert,
|
|
81
80
|
"UP",
|
|
82
81
|
"I",
|
|
83
|
-
"D",
|
|
84
82
|
"TID251",
|
|
85
83
|
]
|
|
86
84
|
flake8-quotes = { inline-quotes = "single", multiline-quotes = "double" }
|
|
87
85
|
mccabe = { max-complexity = 15 }
|
|
88
86
|
ignore = [
|
|
89
87
|
"D100", # ignore missing docstring in module
|
|
88
|
+
"D101", # ignore missing in public class TODO: fix later
|
|
89
|
+
"D103", # ignore missing in public function TODO: fix later
|
|
90
90
|
"D102", # ignore missing docstring in public method
|
|
91
91
|
"D104", # ignore missing docstring in public package
|
|
92
92
|
"D105", # ignore missing docstring in magic methods
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|