diff --git a/README.md b/README.md index c5f869d52..4baa93c9e 100644 --- a/README.md +++ b/README.md @@ -157,5 +157,13 @@ post_hooks: - "black ." ``` +### use_path_prefixes_for_title_model_names + +By default, `openapi-python-client` generates class names which include the full path to the schema, including any parent-types. This can result in very long class names like `MyRouteSomeClassAnotherClassResponse`—which is very unique and unlikely to cause conflicts with future API additions, but also super verbose. + +If you are carefully curating your `title` properties already to ensure no duplicate class names, you can turn off this prefixing feature by setting `use_path_prefixes_for_title_model_names` to `false` in your config file. This will use the `title` property of any object that has it set _without_ prefixing. + +If this option results in conflicts, you will need to manually override class names instead via the `class_overrides` option. + [changelog.md]: CHANGELOG.md [poetry]: https://python-poetry.org/ diff --git a/openapi_python_client/config.py b/openapi_python_client/config.py index ace95c6b1..d63d708db 100644 --- a/openapi_python_client/config.py +++ b/openapi_python_client/config.py @@ -27,6 +27,7 @@ class Config(BaseModel): project_name_override: Optional[str] package_name_override: Optional[str] package_version_override: Optional[str] + use_path_prefixes_for_title_model_names: bool = True post_hooks: List[str] = [ "autoflake -i -r --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports .", "isort .", diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 18e4c4c43..38080cd40 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -391,9 +391,14 @@ def build_model_property( roots: Set of strings that identify schema objects on which the new ModelProperty will depend process_properties: Determines whether the new ModelProperty will be initialized with property data """ - class_string = data.title or name - if parent_name: - class_string = f"{utils.pascal_case(parent_name)}{utils.pascal_case(class_string)}" + if not config.use_path_prefixes_for_title_model_names and data.title: + class_string = data.title + else: + title = data.title or name + if parent_name: + class_string = f"{utils.pascal_case(parent_name)}{utils.pascal_case(title)}" + else: + class_string = title class_info = Class.from_string(string=class_string, config=config) model_roots = {*roots, class_info.name} required_properties: Optional[List[Property]] = None diff --git a/tests/test_parser/test_properties/test_model_property.py b/tests/test_parser/test_properties/test_model_property.py index 7f8a92270..8e82145bb 100644 --- a/tests/test_parser/test_properties/test_model_property.py +++ b/tests/test_parser/test_properties/test_model_property.py @@ -1,3 +1,4 @@ +from typing import Optional from unittest.mock import MagicMock import pytest @@ -201,6 +202,46 @@ def test_model_name_conflict(self): assert new_schemas == schemas assert err == PropertyError(detail='Attempted to generate duplicate models with name "OtherModel"', data=data) + @pytest.mark.parametrize( + "name, title, parent_name, use_title_prefixing, expected", + ids=( + "basic name only", + "title override", + "name with parent", + "name with parent and title prefixing disabled", + "title with parent", + "title with parent and title prefixing disabled", + ), + argvalues=( + ("prop", None, None, True, "Prop"), + ("prop", "MyModel", None, True, "MyModel"), + ("prop", None, "parent", True, "ParentProp"), + ("prop", None, "parent", False, "ParentProp"), + ("prop", "MyModel", "parent", True, "ParentMyModel"), + ("prop", "MyModel", "parent", False, "MyModel"), + ), + ) + def test_model_naming( + self, name: str, title: Optional[str], parent_name: Optional[str], use_title_prefixing: bool, expected: str + ): + from openapi_python_client.parser.properties import Schemas, build_model_property + + data = oai.Schema( + title=title, + properties={}, + ) + result = build_model_property( + data=data, + name=name, + schemas=Schemas(), + required=True, + parent_name=parent_name, + config=Config(use_path_prefixes_for_title_model_names=use_title_prefixing), + roots={"root"}, + process_properties=True, + )[0] + assert result.class_info.name == expected + def test_model_bad_properties(self): from openapi_python_client.parser.properties import Schemas, build_model_property