diff --git a/openapi_python_client/parser/properties/enum_property.py b/openapi_python_client/parser/properties/enum_property.py index 0f0db0d61..49994e3da 100644 --- a/openapi_python_client/parser/properties/enum_property.py +++ b/openapi_python_client/parser/properties/enum_property.py @@ -187,7 +187,35 @@ def values_from_list(values: list[str] | list[int]) -> dict[str, ValueType]: """Convert a list of values into dict of {name: value}, where value can sometimes be None""" output: dict[str, ValueType] = {} - for i, value in enumerate(values): + # Strip out any duplicate values, while preserving the original order of the list. + # OpenAPI doesn't specifically disallow listing the exact same enum value twice; that + # would have no effect on validation behavior. The problem with it is just that we + # can't define two identically-named constants in the generated code. But there's no + # reason for us to do so, anyway; a single constant will suffice. So, just drop any + # duplicate value. + unique_values = [] + for value in values: + if value not in unique_values: + unique_values.append(value) + + # We normally would like to make nice-looking Python constant names for enum values, + # so that "myValue" becomes MY_VALUE, etc. However, that won't work if an enum has two + # values that differ only by case (which is allowed in OpenAPI). + use_case_sensitive_names = False + for i, value1 in enumerate(unique_values): + if use_case_sensitive_names: + break + for j, value2 in enumerate(unique_values): + if ( + i != j + and isinstance(value1, str) + and isinstance(value2, str) + and value1.upper() == value2.upper() + ): + use_case_sensitive_names = True + break + + for i, value in enumerate(unique_values): value = cast(Union[str, int], value) if isinstance(value, int): if value < 0: @@ -196,11 +224,14 @@ def values_from_list(values: list[str] | list[int]) -> dict[str, ValueType]: output[f"VALUE_{value}"] = value continue if value and value[0].isalpha(): - key = value.upper() + key = value else: key = f"VALUE_{i}" if key in output: raise ValueError(f"Duplicate key {key} in Enum") - sanitized_key = utils.snake_case(key).upper() + if use_case_sensitive_names: + sanitized_key = utils.sanitize(key.replace(" ", "_")) + else: + sanitized_key = utils.snake_case(key.upper()).upper() output[sanitized_key] = utils.remove_string_escapes(value) return output diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index 3290dcd39..3d168eca3 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -364,14 +364,32 @@ def test_values_from_list(self): "VALUE_7": "", } - def test_values_from_list_duplicate(self): + def test_values_from_list_duplicate_is_skipped(self): from openapi_python_client.parser.properties import EnumProperty data = ["abc", "123", "a23", "abc"] - with pytest.raises(ValueError): - EnumProperty.values_from_list(data) + result = EnumProperty.values_from_list(data) + + assert result == { + "ABC": "abc", + "VALUE_1": "123", + "A23": "a23", + } + def test_values_from_list_with_case_sensitive_names(self): + from openapi_python_client.parser.properties import EnumProperty + + data = ["abc", "123", "ABC", "thing with spaces"] + + result = EnumProperty.values_from_list(data) + + assert result == { + "abc": "abc", + "VALUE_1": "123", + "ABC": "ABC", + "thing_with_spaces": "thing with spaces", + } class TestPropertyFromData: def test_property_from_data_str_enum(self, enum_property_factory, config):