Skip to content

Commit 0dd773d

Browse files
Merge pull request #37 from viennadd/main
Normalise names of Python identifiers
2 parents 104206e + 968a43e commit 0dd773d

File tree

5 files changed

+170
-5
lines changed

5 files changed

+170
-5
lines changed

src/openapi_python_generator/language_converters/python/common.py

+17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import keyword
2+
import re
3+
14
_use_orjson: bool = False
5+
_symbol_ascii_strip_re = re.compile(r"[^A-Za-z0-9_]")
26

37

48
def set_use_orjson(value: bool) -> None:
@@ -17,3 +21,16 @@ def get_use_orjson() -> bool:
1721
"""
1822
global _use_orjson
1923
return _use_orjson
24+
25+
26+
def normalize_symbol(symbol: str) -> str:
27+
"""
28+
Remove invalid characters & keywords in Python symbol names
29+
:param symbol: name of the identifier
30+
:return: normalized identifier name
31+
"""
32+
symbol = symbol.replace("-", "_")
33+
normalized_symbol = _symbol_ascii_strip_re.sub('', symbol)
34+
if normalized_symbol in keyword.kwlist:
35+
normalized_symbol = normalized_symbol + '_'
36+
return normalized_symbol

src/openapi_python_generator/language_converters/python/service_generator.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,17 @@ def _generate_params_from_content(content: Union[Reference, Schema]):
8383
continue # pragma: no cover
8484
converted_result = ""
8585
required = False
86+
param_name_cleaned = common.normalize_symbol(param.name)
8687

8788
if isinstance(param.param_schema, Schema):
8889
converted_result = (
89-
f"{param.name} : {type_converter(param.param_schema, param.required).converted_type}"
90+
f"{param_name_cleaned} : {type_converter(param.param_schema, param.required).converted_type}"
9091
+ ("" if param.required else " = None")
9192
)
9293
required = param.required
9394
elif isinstance(param.param_schema, Reference):
9495
converted_result = (
95-
f"{param.name} : {param.param_schema.ref.split('/')[-1] }"
96+
f"{param_name_cleaned} : {param.param_schema.ref.split('/')[-1] }"
9697
+ (
9798
""
9899
if isinstance(param, Reference) or param.required
@@ -153,7 +154,7 @@ def _generate_params_from_content(content: Union[Reference, Schema]):
153154

154155
def generate_operation_id(operation: Operation, http_op: str) -> str:
155156
if operation.operationId is not None:
156-
return f"{operation.operationId.replace('-', '_')}"
157+
return common.normalize_symbol(operation.operationId)
157158
else:
158159
raise Exception(f"OperationId is not defined for {http_op}") # pragma: no cover
159160

@@ -165,8 +166,7 @@ def _generate_params(operation: Operation, param_in : Literal["query", "header"]
165166
params = []
166167
for param in operation.parameters:
167168
if isinstance(param, Parameter) and param.param_in == param_in:
168-
param_name_cleaned = param.name.replace("-", "_")
169-
169+
param_name_cleaned = common.normalize_symbol(param.name)
170170
params.append(f"'{param.name}' : {param_name_cleaned}")
171171

172172
return params
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import pytest
2+
from click.testing import CliRunner
3+
4+
from openapi_python_generator.__main__ import main
5+
from openapi_python_generator.common import HTTPLibrary
6+
from tests.conftest import test_data_folder
7+
from tests.conftest import test_result_path
8+
9+
10+
@pytest.fixture
11+
def runner() -> CliRunner:
12+
"""Fixture for invoking command-line interfaces."""
13+
return CliRunner()
14+
15+
16+
@pytest.mark.parametrize(
17+
"library",
18+
[HTTPLibrary.httpx, HTTPLibrary.requests, HTTPLibrary.aiohttp],
19+
)
20+
def test_issue_keyword_parameter_name(runner: CliRunner, model_data_with_cleanup, library) -> None:
21+
result = runner.invoke(
22+
main,
23+
[
24+
str(test_data_folder / "issue_keyword_parameter_name.json"),
25+
str(test_result_path),
26+
"--library",
27+
library.value,
28+
],
29+
)
30+
assert result.exit_code == 0
31+
32+
33+
@pytest.mark.parametrize(
34+
"library",
35+
[HTTPLibrary.httpx, HTTPLibrary.requests, HTTPLibrary.aiohttp],
36+
)
37+
def test_issue_illegal_character_in_operation_id(runner: CliRunner, model_data_with_cleanup, library) -> None:
38+
result = runner.invoke(
39+
main,
40+
[
41+
str(test_data_folder / "issue_illegal_character_in_operation_id.json"),
42+
str(test_result_path),
43+
"--library",
44+
library.value,
45+
],
46+
)
47+
assert result.exit_code == 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"openapi": "3.0.2",
3+
"info": {
4+
"title": "Test case for ",
5+
"description": "Provide hooks for GetCourse processes and export/import of GetCourse data.",
6+
"version": "0.1.0"
7+
},
8+
"servers": [],
9+
"paths": {
10+
"/v1/diffs": {
11+
"post": {
12+
"tags": [
13+
],
14+
"summary": "Get Groups",
15+
"operationId": "get_grou##ps_$v1_ex%%ports_gr.oups__post",
16+
"parameters": [
17+
],
18+
"requestBody": {
19+
"content": {
20+
"application/json": {
21+
"schema": {
22+
"title": "AccountGroupsRequest",
23+
"required": [
24+
"domain"
25+
],
26+
"type": "object",
27+
"properties": {
28+
"from": {
29+
"title": "Domain",
30+
"type": "string"
31+
}
32+
}
33+
}
34+
}
35+
},
36+
"required": true
37+
},
38+
"responses": {
39+
}
40+
}
41+
}
42+
},
43+
"components": {
44+
"schemas": {}
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"openapi": "3.0.2",
3+
"info": {
4+
"title": "Test case for ",
5+
"description": "Provide hooks for GetCourse processes and export/import of GetCourse data.",
6+
"version": "0.1.0"
7+
},
8+
"servers": [],
9+
"paths": {
10+
"/v1/diffs": {
11+
"post": {
12+
"tags": [
13+
],
14+
"summary": "Get Groups",
15+
"operationId": "get_groups_v1_exports_groups__post",
16+
"parameters": [
17+
{
18+
"required": true,
19+
"schema": {
20+
"title": "from_sha of a diff",
21+
"type": "string"
22+
},
23+
"name": "from",
24+
"in": "query"
25+
}
26+
],
27+
"requestBody": {
28+
"content": {
29+
"application/json": {
30+
"schema": {
31+
"title": "AccountGroupsRequest",
32+
"required": [
33+
"domain"
34+
],
35+
"type": "object",
36+
"properties": {
37+
"from": {
38+
"title": "Domain",
39+
"type": "string"
40+
}
41+
}
42+
}
43+
}
44+
},
45+
"required": true
46+
},
47+
"responses": {
48+
}
49+
}
50+
}
51+
},
52+
"components": {
53+
"schemas": {}
54+
}
55+
}

0 commit comments

Comments
 (0)