-
-
Notifications
You must be signed in to change notification settings - Fork 225
/
Copy pathcli.py
171 lines (144 loc) · 5.42 KB
/
cli.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import codecs
from collections.abc import Sequence
from pathlib import Path
from pprint import pformat
from typing import Optional, Union
import typer
from openapi_python_client import MetaType
from openapi_python_client.config import Config, ConfigFile
from openapi_python_client.parser.errors import ErrorLevel, GeneratorError, ParseError
app = typer.Typer(name="openapi-python-client")
def _version_callback(value: bool) -> None:
from openapi_python_client import __version__
if value:
typer.echo(f"openapi-python-client version: {__version__}")
raise typer.Exit()
def _process_config(
*,
url: Optional[str],
path: Optional[Path],
config_path: Optional[Path],
meta_type: MetaType,
file_encoding: str,
overwrite: bool,
output_path: Optional[Path],
) -> Config:
source: Union[Path, str]
if url and not path:
source = url
elif path and not url:
source = path
elif url and path:
typer.secho("Provide either --url or --path, not both", fg=typer.colors.RED)
raise typer.Exit(code=1)
else:
typer.secho("You must either provide --url or --path", fg=typer.colors.RED)
raise typer.Exit(code=1)
try:
codecs.getencoder(file_encoding)
except LookupError as err:
typer.secho(f"Unknown encoding : {file_encoding}", fg=typer.colors.RED)
raise typer.Exit(code=1) from err
if not config_path:
config_file = ConfigFile()
else:
try:
config_file = ConfigFile.load_from_path(path=config_path)
except Exception as err:
raise typer.BadParameter("Unable to parse config") from err
return Config.from_sources(config_file, meta_type, source, file_encoding, overwrite, output_path=output_path)
# noinspection PyUnusedLocal
@app.callback()
def cli(
version: bool = typer.Option(False, "--version", callback=_version_callback, help="Print the version and exit"),
) -> None:
"""Generate a Python client from an OpenAPI document"""
def _print_parser_error(err: GeneratorError, color: str) -> None:
typer.secho(err.header, bold=True, fg=color, err=True)
typer.echo()
if err.detail:
typer.secho(err.detail, fg=color, err=True)
typer.echo()
if isinstance(err, ParseError) and err.data is not None:
formatted_data = pformat(err.data)
typer.secho(formatted_data, fg=color, err=True)
typer.echo()
def handle_errors(errors: Sequence[GeneratorError], fail_on_warning: bool = False) -> None:
"""Turn custom errors into formatted error messages"""
if len(errors) == 0:
return
error_level = ErrorLevel.WARNING
message = "Warning(s) encountered while generating. Client was generated, but some pieces may be missing"
header_color = typer.colors.BRIGHT_YELLOW
color = typer.colors.YELLOW
for error in errors:
if error.level == ErrorLevel.ERROR:
error_level = ErrorLevel.ERROR
message = "Error(s) encountered while generating, client was not created"
color = typer.colors.RED
header_color = typer.colors.BRIGHT_RED
break
typer.secho(
message,
underline=True,
bold=True,
fg=header_color,
err=True,
)
typer.echo()
for err in errors:
_print_parser_error(err, color)
gh_link = typer.style(
"https://github.com/openapi-generators/openapi-python-client/issues/new/choose", fg=typer.colors.BRIGHT_BLUE
)
typer.secho(
f"If you believe this was a mistake or this tool is missing a feature you need, "
f"please open an issue at {gh_link}",
fg=typer.colors.BLUE,
err=True,
)
if error_level == ErrorLevel.ERROR or fail_on_warning:
raise typer.Exit(code=1)
@app.command()
def generate(
url: Optional[str] = typer.Option(None, help="A URL to read the OpenAPI document from"),
path: Optional[Path] = typer.Option(None, help="A path to the OpenAPI document"),
custom_template_path: Optional[Path] = typer.Option(
None,
help="A path to a directory containing custom template(s)",
file_okay=False,
dir_okay=True,
readable=True,
resolve_path=True,
), # type: ignore
meta: MetaType = typer.Option(
MetaType.POETRY,
help="The type of metadata you want to generate.",
),
file_encoding: str = typer.Option("utf-8", help="Encoding used when writing generated"),
config_path: Optional[Path] = typer.Option(None, "--config", help="Path to the config file to use"),
fail_on_warning: bool = False,
overwrite: bool = typer.Option(False, help="Overwrite the existing client if it exists"),
output_path: Optional[Path] = typer.Option(
None,
help="Path to write the generated code to. "
"Defaults to the OpenAPI document title converted to kebab or snake case (depending on meta type). "
"Can also be overridden with `project_name_override` or `package_name_override` in config.",
),
) -> None:
"""Generate a new OpenAPI Client library"""
from . import generate
config = _process_config(
url=url,
path=path,
config_path=config_path,
meta_type=meta,
file_encoding=file_encoding,
overwrite=overwrite,
output_path=output_path,
)
errors = generate(
custom_template_path=custom_template_path,
config=config,
)
handle_errors(errors, fail_on_warning)