Skip to content

Commit cc7bbf5

Browse files
Merge pull request #60 from stufisher/basic-customisation
Add basic template customisation
2 parents 568677b + c75606a commit cc7bbf5

File tree

10 files changed

+81
-15
lines changed

10 files changed

+81
-15
lines changed

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ repos:
1818
language: system
1919
types: [ python ]
2020
- repo: https://github.com/pycqa/isort
21-
rev: 5.10.1
21+
rev: 5.11.5
2222
hooks:
2323
- id: isort
2424
name: isort (python)

docs/references/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ Options:
1818
--library The library to use. Defaults to `httpx`.
1919
--env-token-name The name of the environment variable to use for the API key. Defaults to `access_token`.
2020
--use-orjson Use the `orjson` library for serialization. Defaults to `false`.
21+
--custom-template-path Use a custom template path to override the built in templates.
2122
-h, --help Show this help message and exit.
2223
```

src/openapi_python_generator/__main__.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,30 @@
3232
help="Use the orjson library to serialize the data. This is faster than the default json library and provides "
3333
"serialization of datetimes and other types that are not supported by the default json library.",
3434
)
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+
)
3541
@click.version_option(version=__version__)
3642
def main(
3743
source: str,
3844
output: str,
3945
library: Optional[HTTPLibrary] = HTTPLibrary.httpx,
4046
env_token_name: Optional[str] = None,
4147
use_orjson: bool = False,
48+
custom_template_path: Optional[str] = None,
4249
) -> None:
4350
"""
4451
Generate Python code from an OpenAPI 3.0 specification.
4552
4653
Provide a SOURCE (file or URL) containing the OpenAPI 3 specification and
4754
an OUTPUT path, where the resulting client is created.
4855
"""
49-
generate_data(source, output, library, env_token_name, use_orjson)
56+
generate_data(
57+
source, output, library, env_token_name, use_orjson, custom_template_path
58+
)
5059

5160

5261
if __name__ == "__main__": # pragma: no cover

src/openapi_python_generator/generate_data.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
from .common import HTTPLibrary
1717
from .common import library_config_dict
1818
from .language_converters.python.generator import generator
19-
from .language_converters.python.jinja_config import JINJA_ENV
2019
from .language_converters.python.jinja_config import SERVICE_TEMPLATE
20+
from .language_converters.python.jinja_config import create_jinja_env
2121
from .models import ConversionResult
2222

2323

@@ -109,13 +109,14 @@ def write_data(data: ConversionResult, output: Union[str, Path]) -> None:
109109
files = []
110110

111111
# Write the services.
112+
jinja_env = create_jinja_env()
112113
for service in data.services:
113114
if len(service.operations) == 0:
114115
continue
115116
files.append(service.file_name)
116117
write_code(
117118
services_path / f"{service.file_name}.py",
118-
JINJA_ENV.get_template(SERVICE_TEMPLATE).render(**service.dict()),
119+
jinja_env.get_template(SERVICE_TEMPLATE).render(**service.dict()),
119120
)
120121

121122
# Create services.__init__.py file containing imports to all services.
@@ -137,13 +138,20 @@ def generate_data(
137138
library: Optional[HTTPLibrary] = HTTPLibrary.httpx,
138139
env_token_name: Optional[str] = None,
139140
use_orjson: bool = False,
141+
custom_template_path: Optional[str] = None,
140142
) -> None:
141143
"""
142144
Generate Python code from an OpenAPI 3.0 specification.
143145
"""
144146
data = get_open_api(source)
145147
click.echo(f"Generating data from {source}")
146148

147-
result = generator(data, library_config_dict[library], env_token_name, use_orjson)
149+
result = generator(
150+
data,
151+
library_config_dict[library],
152+
env_token_name,
153+
use_orjson,
154+
custom_template_path,
155+
)
148156

149157
write_data(result, output)

src/openapi_python_generator/language_converters/python/api_config_generator.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
from openapi_python_generator.language_converters.python.jinja_config import (
66
API_CONFIG_TEMPLATE,
77
)
8-
from openapi_python_generator.language_converters.python.jinja_config import JINJA_ENV
8+
from openapi_python_generator.language_converters.python.jinja_config import (
9+
create_jinja_env,
10+
)
911
from openapi_python_generator.models import APIConfig
1012

1113

@@ -15,9 +17,10 @@ def generate_api_config(
1517
"""
1618
Generate the API model.
1719
"""
20+
jinja_env = create_jinja_env()
1821
return APIConfig(
1922
file_name="api_config",
20-
content=JINJA_ENV.get_template(API_CONFIG_TEMPLATE).render(
23+
content=jinja_env.get_template(API_CONFIG_TEMPLATE).render(
2124
env_token_name=env_token_name, **data.dict()
2225
),
2326
base_url=data.servers[0].url if len(data.servers) > 0 else "NO SERVER",

src/openapi_python_generator/language_converters/python/common.py

+20
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import keyword
22
import re
3+
from typing import Optional
34

45

56
_use_orjson: bool = False
7+
_custom_template_path: str = None
68
_symbol_ascii_strip_re = re.compile(r"[^A-Za-z0-9_]")
79

810

@@ -24,6 +26,24 @@ def get_use_orjson() -> bool:
2426
return _use_orjson
2527

2628

29+
def set_custom_template_path(value: Optional[str]) -> None:
30+
"""
31+
Set the value of the global variable _custom_template_path.
32+
:param value: value of the variable
33+
"""
34+
global _custom_template_path
35+
_custom_template_path = value
36+
37+
38+
def get_custom_template_path() -> Optional[str]:
39+
"""
40+
Get the value of the global variable _custom_template_path.
41+
:return: value of the variable
42+
"""
43+
global _custom_template_path
44+
return _custom_template_path
45+
46+
2747
def normalize_symbol(symbol: str) -> str:
2848
"""
2949
Remove invalid characters & keywords in Python symbol names

src/openapi_python_generator/language_converters/python/generator.py

+2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ def generator(
2121
library_config: LibraryConfig,
2222
env_token_name: Optional[str] = None,
2323
use_orjson: bool = False,
24+
custom_template_path: Optional[str] = None,
2425
) -> ConversionResult:
2526
"""
2627
Generate Python code from an OpenAPI 3.0 specification.
2728
"""
2829

2930
common.set_use_orjson(use_orjson)
31+
common.set_custom_template_path(custom_template_path)
3032

3133
if data.components is not None:
3234
models = generate_models(data.components)
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from pathlib import Path
22

3+
from jinja2 import ChoiceLoader
34
from jinja2 import Environment
45
from jinja2 import FileSystemLoader
56

7+
from . import common
8+
69

710
ENUM_TEMPLATE = "enum.jinja2"
811
MODELS_TEMPLATE = "models.jinja2"
@@ -11,6 +14,20 @@
1114
API_CONFIG_TEMPLATE = "apiconfig.jinja2"
1215
TEMPLATE_PATH = Path(__file__).parent / "templates"
1316

14-
JINJA_ENV = Environment(
15-
loader=FileSystemLoader(TEMPLATE_PATH), autoescape=True, trim_blocks=True
16-
)
17+
18+
def create_jinja_env():
19+
custom_template_path = common.get_custom_template_path()
20+
return Environment(
21+
loader=(
22+
ChoiceLoader(
23+
[
24+
FileSystemLoader(custom_template_path),
25+
FileSystemLoader(TEMPLATE_PATH),
26+
]
27+
)
28+
if custom_template_path is not None
29+
else FileSystemLoader(TEMPLATE_PATH)
30+
),
31+
autoescape=True,
32+
trim_blocks=True,
33+
)

src/openapi_python_generator/language_converters/python/model_generator.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
from openapi_python_generator.language_converters.python.jinja_config import (
1313
ENUM_TEMPLATE,
1414
)
15-
from openapi_python_generator.language_converters.python.jinja_config import JINJA_ENV
1615
from openapi_python_generator.language_converters.python.jinja_config import (
1716
MODELS_TEMPLATE,
1817
)
18+
from openapi_python_generator.language_converters.python.jinja_config import (
19+
create_jinja_env,
20+
)
1921
from openapi_python_generator.models import Model
2022
from openapi_python_generator.models import Property
2123
from openapi_python_generator.models import TypeConversion
@@ -264,6 +266,7 @@ def generate_models(components: Components) -> List[Model]:
264266
if components.schemas is None:
265267
return models
266268

269+
jinja_env = create_jinja_env()
267270
for schema_name, schema_or_reference in components.schemas.items():
268271
name = common.normalize_symbol(schema_name)
269272
if schema_or_reference.enum is not None:
@@ -275,7 +278,7 @@ def generate_models(components: Components) -> List[Model]:
275278
]
276279
m = Model(
277280
file_name=name,
278-
content=JINJA_ENV.get_template(ENUM_TEMPLATE).render(
281+
content=jinja_env.get_template(ENUM_TEMPLATE).render(
279282
name=name, **value_dict
280283
),
281284
openapi_object=schema_or_reference,
@@ -306,7 +309,7 @@ def generate_models(components: Components) -> List[Model]:
306309
)
307310
properties.append(conv_property)
308311

309-
generated_content = JINJA_ENV.get_template(MODELS_TEMPLATE).render(
312+
generated_content = jinja_env.get_template(MODELS_TEMPLATE).render(
310313
schema_name=name, schema=schema_or_reference, properties=properties
311314
)
312315

src/openapi_python_generator/language_converters/python/service_generator.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
from openapi_python_generator.language_converters.python import common
2020
from openapi_python_generator.language_converters.python.common import normalize_symbol
21-
from openapi_python_generator.language_converters.python.jinja_config import JINJA_ENV
21+
from openapi_python_generator.language_converters.python.jinja_config import (
22+
create_jinja_env,
23+
)
2224
from openapi_python_generator.language_converters.python.model_generator import (
2325
type_converter,
2426
)
@@ -269,6 +271,7 @@ def generate_services(
269271
:param paths: paths object to be converted
270272
:return: List of services
271273
"""
274+
jinja_env = create_jinja_env()
272275

273276
def generate_service_operation(
274277
op: Operation, path_name: str, async_type: bool
@@ -296,7 +299,7 @@ def generate_service_operation(
296299
use_orjson=common.get_use_orjson(),
297300
)
298301

299-
so.content = JINJA_ENV.get_template(library_config.template_name).render(
302+
so.content = jinja_env.get_template(library_config.template_name).render(
300303
**so.dict()
301304
)
302305

0 commit comments

Comments
 (0)