diff --git a/src/openapi_python_generator/language_converters/python/common.py b/src/openapi_python_generator/language_converters/python/common.py index 72da991..49019a5 100644 --- a/src/openapi_python_generator/language_converters/python/common.py +++ b/src/openapi_python_generator/language_converters/python/common.py @@ -49,8 +49,10 @@ def normalize_symbol(symbol: str) -> str: :param symbol: name of the identifier :return: normalized identifier name """ - symbol = symbol.replace("-", "_") + symbol = symbol.replace("-", "_").replace(" ", "_") normalized_symbol = _symbol_ascii_strip_re.sub("", symbol) if normalized_symbol in keyword.kwlist: normalized_symbol = normalized_symbol + "_" + if len(normalized_symbol) > 0 and normalized_symbol[0].isnumeric(): + normalized_symbol = "_" + normalized_symbol return normalized_symbol diff --git a/src/openapi_python_generator/language_converters/python/jinja_config.py b/src/openapi_python_generator/language_converters/python/jinja_config.py index 172ca70..1a0704b 100644 --- a/src/openapi_python_generator/language_converters/python/jinja_config.py +++ b/src/openapi_python_generator/language_converters/python/jinja_config.py @@ -16,7 +16,7 @@ def create_jinja_env(): custom_template_path = common.get_custom_template_path() - return Environment( + environment = Environment( loader=( ChoiceLoader( [ @@ -30,3 +30,7 @@ def create_jinja_env(): autoescape=True, trim_blocks=True, ) + + environment.filters["normalize_symbol"] = common.normalize_symbol + + return environment diff --git a/src/openapi_python_generator/language_converters/python/model_generator.py b/src/openapi_python_generator/language_converters/python/model_generator.py index 0b7943e..d494b1d 100644 --- a/src/openapi_python_generator/language_converters/python/model_generator.py +++ b/src/openapi_python_generator/language_converters/python/model_generator.py @@ -1,5 +1,4 @@ import itertools -import re from typing import List, Optional, Union import click @@ -414,10 +413,8 @@ def generate_models( name = common.normalize_symbol(schema_name) if schema_or_reference.enum is not None: value_dict = schema_or_reference.model_dump() - regex = re.compile(r"[\s\/=\*\+]+") value_dict["enum"] = [ - re.sub(regex, "_", i) if isinstance(i, str) else f"value_{i}" - for i in value_dict["enum"] + (common.normalize_symbol(str(i)).upper(), i) for i in value_dict["enum"] ] m = Model( file_name=name, diff --git a/src/openapi_python_generator/language_converters/python/templates/enum.jinja2 b/src/openapi_python_generator/language_converters/python/templates/enum.jinja2 index f88bdd6..848f159 100644 --- a/src/openapi_python_generator/language_converters/python/templates/enum.jinja2 +++ b/src/openapi_python_generator/language_converters/python/templates/enum.jinja2 @@ -1,9 +1,11 @@ from enum import Enum class {{ name }}(str, Enum): - {% for enumItem in enum %} + {% for enumItemIdent, enumItem in enum %} {% if enumItem is string %} - {{ enumItem.upper() }} = '{{ enumItem }}'{% else %} - value_{{ enumItem }} = {{ enumItem }}{% endif %} + {{ enumItemIdent }} = '{{ enumItem }}' + {% else %} + {{ enumItemIdent }} = {{ enumItem }} + {% endif %} {% endfor %} diff --git a/src/openapi_python_generator/language_converters/python/templates/models.jinja2 b/src/openapi_python_generator/language_converters/python/templates/models.jinja2 index fbf2e4e..3ff37e4 100644 --- a/src/openapi_python_generator/language_converters/python/templates/models.jinja2 +++ b/src/openapi_python_generator/language_converters/python/templates/models.jinja2 @@ -20,5 +20,5 @@ class {{ schema_name }}(BaseModel): """ {% for property in properties %} - {{ property.name | replace("@","") | replace("-","_") }} : {{ property.type.converted_type | safe }} = Field(alias="{{ property.name }}" {% if not property.required %}, default = {{ property.default }} {% endif %}) + {{ property.name | normalize_symbol }} : {{ property.type.converted_type | safe }} = Field(alias="{{ property.name }}" {% if not property.required %}, default = {{ property.default }} {% endif %}) {% endfor %} diff --git a/src/openapi_python_generator/language_converters/python/templates/models_pydantic_2.jinja2 b/src/openapi_python_generator/language_converters/python/templates/models_pydantic_2.jinja2 index 36edd3b..99bdfcd 100644 --- a/src/openapi_python_generator/language_converters/python/templates/models_pydantic_2.jinja2 +++ b/src/openapi_python_generator/language_converters/python/templates/models_pydantic_2.jinja2 @@ -24,5 +24,5 @@ class {{ schema_name }}(BaseModel): } {% for property in properties %} - {{ property.name | replace("@","") | replace("-","_") }} : {{ property.type.converted_type | safe }} = Field(validation_alias="{{ property.name }}" {% if not property.required %}, default = {{ property.default }} {% endif %}) - {% endfor %} \ No newline at end of file + {{ property.name | normalize_symbol }} : {{ property.type.converted_type | safe }} = Field(validation_alias="{{ property.name }}" {% if not property.required %}, default = {{ property.default }} {% endif %}) + {% endfor %} diff --git a/tests/regression/test_issue_30_87.py b/tests/regression/test_issue_30_87.py new file mode 100644 index 0000000..7456f2f --- /dev/null +++ b/tests/regression/test_issue_30_87.py @@ -0,0 +1,24 @@ +import pytest + +from openapi_python_generator.common import HTTPLibrary +from openapi_python_generator.generate_data import get_open_api +from openapi_python_generator.parsers import generate_code_3_1 +from tests.conftest import test_data_folder + + +@pytest.mark.parametrize( + "library", + [HTTPLibrary.httpx, HTTPLibrary.aiohttp, HTTPLibrary.requests], +) +def test_issue_30_87(library) -> None: + """ + https://github.com/MarcoMuellner/openapi-python-generator/issues/30 + https://github.com/MarcoMuellner/openapi-python-generator/issues/87 + """ + openapi_obj, version = get_open_api(str(test_data_folder / "issue_30_87.json")) + result = generate_code_3_1(openapi_obj, library) # type: ignore + + expected_model = [m for m in result.models if m.openapi_object.title == "UserType"][ + 0 + ] + assert "ADMIN_USER = 'admin-user'" in expected_model.content diff --git a/tests/regression/test_issue_55.py b/tests/regression/test_issue_55.py new file mode 100644 index 0000000..572d610 --- /dev/null +++ b/tests/regression/test_issue_55.py @@ -0,0 +1,23 @@ +import pytest + +from openapi_python_generator.common import HTTPLibrary +from openapi_python_generator.generate_data import get_open_api +from openapi_python_generator.parsers import generate_code_3_1 +from tests.conftest import test_data_folder + + +@pytest.mark.parametrize( + "library", + [HTTPLibrary.httpx, HTTPLibrary.aiohttp, HTTPLibrary.requests], +) +def test_issue_55(library) -> None: + """ + https://github.com/MarcoMuellner/openapi-python-generator/issues/55 + """ + openapi_obj, version = get_open_api(str(test_data_folder / "issue_55.json")) + result = generate_code_3_1(openapi_obj, library) # type: ignore + + expected_model = [m for m in result.models if m.openapi_object.title == "UserType"][ + 0 + ] + assert "ADMIN_USER = 'admin user'" in expected_model.content diff --git a/tests/test_data/issue_30_87.json b/tests/test_data/issue_30_87.json new file mode 100644 index 0000000..4f4f15a --- /dev/null +++ b/tests/test_data/issue_30_87.json @@ -0,0 +1,70 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Title", + "version": "1.0" + }, + "paths": { + "/users": { + "get": { + "summary": "Get users", + "description": "Returns a list of users.", + "operationId": "users_get", + "parameters": [ + { + "name": "type", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/UserType" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "UserType": { + "title": "UserType", + "description": "An enumeration.", + "enum": ["admin-user", "regular-user"] + }, + "User": { + "title": "User", + "description": "A user.", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/UserType" + }, + "30d_active": { + "type": "boolean" + } + } + } + } + } +} diff --git a/tests/test_data/issue_55.json b/tests/test_data/issue_55.json new file mode 100644 index 0000000..3de3259 --- /dev/null +++ b/tests/test_data/issue_55.json @@ -0,0 +1,70 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Title", + "version": "1.0" + }, + "paths": { + "/users": { + "get": { + "summary": "Get users", + "description": "Returns a list of users.", + "operationId": "users_get", + "parameters": [ + { + "name": "type", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/UserType" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "UserType": { + "title": "UserType", + "description": "An enumeration.", + "enum": ["admin user", "regular user"] + }, + "User": { + "title": "User", + "description": "A user.", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/UserType" + }, + "30d_active": { + "type": "boolean" + } + } + } + } + } +}