otterapi 0.0.0__tar.gz → 0.0.2__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.0 → otterapi-0.0.2}/PKG-INFO +1 -1
- otterapi-0.0.2/otterapi/__init__.py +0 -0
- otterapi-0.0.2/otterapi/__main__.py +4 -0
- otterapi-0.0.2/otterapi/cli.py +84 -0
- otterapi-0.0.2/otterapi/codegen/__init__.py +0 -0
- otterapi-0.0.2/otterapi/codegen/ast_utils.py +118 -0
- otterapi-0.0.2/otterapi/codegen/endpoints.py +499 -0
- otterapi-0.0.2/otterapi/codegen/generator.py +352 -0
- otterapi-0.0.2/otterapi/codegen/openapi_processor.py +27 -0
- otterapi-0.0.2/otterapi/codegen/type_generator.py +538 -0
- otterapi-0.0.2/otterapi/codegen/utils.py +55 -0
- otterapi-0.0.2/otterapi/config.py +74 -0
- {otterapi-0.0.0 → otterapi-0.0.2}/pyproject.toml +4 -4
- {otterapi-0.0.0 → otterapi-0.0.2}/.gitignore +0 -0
- {otterapi-0.0.0 → otterapi-0.0.2}/README.md +0 -0
|
File without changes
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
6
|
+
|
|
7
|
+
from otterapi.codegen.generator import Codegen
|
|
8
|
+
from otterapi.config import get_config
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
app = typer.Typer(
|
|
12
|
+
name='otterapi',
|
|
13
|
+
help='Generate Python client code from OpenAPI specifications',
|
|
14
|
+
no_args_is_help=True,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def generate(
|
|
20
|
+
config: Annotated[
|
|
21
|
+
str | None,
|
|
22
|
+
typer.Option(
|
|
23
|
+
'--config', '-c', help='Path to configuration file (YAML or JSON)'
|
|
24
|
+
),
|
|
25
|
+
] = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Generate Python client code from configuration.
|
|
28
|
+
|
|
29
|
+
If no config file is specified, will look for default config files
|
|
30
|
+
in the current directory or use environment variables.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
otterapi generate
|
|
34
|
+
otterapi generate --config my-config.yaml
|
|
35
|
+
otterapi generate -c config.json
|
|
36
|
+
"""
|
|
37
|
+
config = get_config(config)
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
with Progress(
|
|
41
|
+
SpinnerColumn(),
|
|
42
|
+
TextColumn('[progress.description]{task.description}'),
|
|
43
|
+
console=console,
|
|
44
|
+
) as progress:
|
|
45
|
+
for document_config in config.documents:
|
|
46
|
+
task = progress.add_task(
|
|
47
|
+
f'Generating code for {document_config.source} in {document_config.output}...',
|
|
48
|
+
total=None,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
codegen = Codegen(document_config)
|
|
52
|
+
codegen.generate()
|
|
53
|
+
|
|
54
|
+
console.print(
|
|
55
|
+
f"[green]✓[/green] Successfully generated code in '{document_config.output}'"
|
|
56
|
+
)
|
|
57
|
+
console.print('[dim]Generated files:[/dim]')
|
|
58
|
+
console.print(
|
|
59
|
+
f' - {document_config.output}/{document_config.models_file}'
|
|
60
|
+
)
|
|
61
|
+
console.print(
|
|
62
|
+
f' - {document_config.output}/{document_config.endpoints_file}'
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
progress.update(task, description='Code generation completed!')
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
console.print(f'[red]Error:[/red] {str(e)}')
|
|
69
|
+
raise typer.Exit(1)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@app.command()
|
|
73
|
+
def version() -> None:
|
|
74
|
+
"""Show the version of otterapi."""
|
|
75
|
+
try:
|
|
76
|
+
from otterapi._version import version
|
|
77
|
+
|
|
78
|
+
console.print(f'otterapi version: {version}')
|
|
79
|
+
except ImportError:
|
|
80
|
+
console.print('otterapi version: unknown')
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == '__main__':
|
|
84
|
+
generate('/Users/PLISCD/Downloads/otterapi.yml')
|
|
File without changes
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _name(name: str) -> ast.Name:
|
|
6
|
+
return ast.Name(id=name, ctx=ast.Load())
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _attr(value: str | ast.expr, attr: str) -> ast.Attribute:
|
|
10
|
+
return ast.Attribute(
|
|
11
|
+
value=_name(value) if isinstance(value, str) else value, attr=attr
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _subscript(generic: str, inner: ast.expr) -> ast.Subscript:
|
|
16
|
+
return ast.Subscript(value=_name(generic), slice=inner)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _union_expr(types: list[ast.expr]) -> ast.Subscript:
|
|
20
|
+
# Union[A, B, C]
|
|
21
|
+
return _subscript('Union', ast.Tuple(elts=types))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _optional_expr(inner: ast.expr) -> ast.Subscript:
|
|
25
|
+
return _subscript('Optional', inner)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _argument(name: str, value: ast.expr | None = None) -> ast.arg:
|
|
29
|
+
return ast.arg(
|
|
30
|
+
arg=name,
|
|
31
|
+
annotation=value,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _assign(target: ast.expr, value: ast.expr) -> ast.Assign:
|
|
36
|
+
return ast.Assign(
|
|
37
|
+
targets=[target],
|
|
38
|
+
value=value,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _import(module: str, names: list[str]) -> ast.ImportFrom:
|
|
43
|
+
return ast.ImportFrom(
|
|
44
|
+
module=module,
|
|
45
|
+
names=[ast.alias(name=name) for name in names],
|
|
46
|
+
level=0,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _call(
|
|
51
|
+
func: ast.expr,
|
|
52
|
+
args: list[ast.expr] | None = None,
|
|
53
|
+
keywords: list[ast.keyword] | None = None,
|
|
54
|
+
) -> ast.Call:
|
|
55
|
+
return ast.Call(
|
|
56
|
+
func=func,
|
|
57
|
+
args=args or [],
|
|
58
|
+
keywords=keywords or [],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _func(
|
|
63
|
+
name: str,
|
|
64
|
+
args: list[ast.arg],
|
|
65
|
+
body: list[ast.stmt],
|
|
66
|
+
returns: ast.expr | None = None,
|
|
67
|
+
kwargs: ast.arg = None,
|
|
68
|
+
kwonlyargs: list[ast.arg] = None,
|
|
69
|
+
kw_defaults: list[ast.expr] = None,
|
|
70
|
+
) -> ast.FunctionDef:
|
|
71
|
+
return ast.FunctionDef(
|
|
72
|
+
name=name,
|
|
73
|
+
args=ast.arguments(
|
|
74
|
+
posonlyargs=[],
|
|
75
|
+
args=args,
|
|
76
|
+
kwarg=kwargs,
|
|
77
|
+
kwonlyargs=kwonlyargs or [],
|
|
78
|
+
kw_defaults=kw_defaults or [],
|
|
79
|
+
defaults=[],
|
|
80
|
+
),
|
|
81
|
+
body=body,
|
|
82
|
+
decorator_list=[],
|
|
83
|
+
returns=returns,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _async_func(
|
|
88
|
+
name: str,
|
|
89
|
+
args: list[ast.arg],
|
|
90
|
+
body: list[ast.stmt],
|
|
91
|
+
returns: ast.expr | None = None,
|
|
92
|
+
kwargs: ast.arg = None,
|
|
93
|
+
kwonlyargs: list[ast.arg] = None,
|
|
94
|
+
kw_defaults: list[ast.expr] = None,
|
|
95
|
+
) -> ast.AsyncFunctionDef:
|
|
96
|
+
return ast.AsyncFunctionDef(
|
|
97
|
+
name=name,
|
|
98
|
+
args=ast.arguments(
|
|
99
|
+
posonlyargs=[],
|
|
100
|
+
args=args,
|
|
101
|
+
kwarg=kwargs,
|
|
102
|
+
kwonlyargs=kwonlyargs or [],
|
|
103
|
+
kw_defaults=kw_defaults or [],
|
|
104
|
+
defaults=[],
|
|
105
|
+
),
|
|
106
|
+
body=body,
|
|
107
|
+
decorator_list=[],
|
|
108
|
+
returns=returns,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _all(names: Iterable[str]) -> ast.Assign:
|
|
113
|
+
return _assign(
|
|
114
|
+
target=_name('__all__'),
|
|
115
|
+
value=ast.Tuple(
|
|
116
|
+
elts=[ast.Constant(value=name) for name in names], ctx=ast.Load()
|
|
117
|
+
),
|
|
118
|
+
)
|