Skip to content

Commit 0bb503f

Browse files
committed
Prepend a prefix to field names to allow use as Python identifiers
1 parent 461396d commit 0bb503f

File tree

5 files changed

+48
-1
lines changed

5 files changed

+48
-1
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Unreleased
9+
10+
### Changes
11+
12+
### Fixes
13+
14+
- Prefix generated identifiers to allow leading digits in field names (#206 - @kalzoo).
15+
16+
### Additions
17+
818
## 0.6.1 - 2020-09-26
919

1020
### Changes

end_to_end_tests/golden-record/my_test_api_client/models/a_model.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class AModel:
1717
a_camel_date_time: Union[datetime.datetime, datetime.date]
1818
a_date: datetime.date
1919
nested_list_of_enums: Optional[List[List[DifferentEnum]]] = None
20+
field_1_leading_digit: Optional[str] = None
2021

2122
def to_dict(self) -> Dict[str, Any]:
2223
an_enum_value = self.an_enum_value.value
@@ -44,12 +45,15 @@ def to_dict(self) -> Dict[str, Any]:
4445

4546
nested_list_of_enums.append(nested_list_of_enums_item)
4647

48+
field_1_leading_digit = self.field_1_leading_digit
49+
4750
return {
4851
"an_enum_value": an_enum_value,
4952
"some_dict": some_dict,
5053
"aCamelDateTime": a_camel_date_time,
5154
"a_date": a_date,
5255
"nested_list_of_enums": nested_list_of_enums,
56+
"1_leading_digit": field_1_leading_digit,
5357
}
5458

5559
@staticmethod
@@ -84,10 +88,13 @@ def _parse_a_camel_date_time(data: Dict[str, Any]) -> Union[datetime.datetime, d
8488

8589
nested_list_of_enums.append(nested_list_of_enums_item)
8690

91+
field_1_leading_digit = d.get("1_leading_digit")
92+
8793
return AModel(
8894
an_enum_value=an_enum_value,
8995
some_dict=some_dict,
9096
a_camel_date_time=a_camel_date_time,
9197
a_date=a_date,
9298
nested_list_of_enums=nested_list_of_enums,
99+
field_1_leading_digit=field_1_leading_digit,
93100
)

end_to_end_tests/openapi.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,10 @@
564564
"title": "A Date",
565565
"type": "string",
566566
"format": "date"
567+
},
568+
"1_leading_digit": {
569+
"title": "Leading Digit",
570+
"type": "string"
567571
}
568572
},
569573
"description": "A Model for testing all the ways custom objects can be used "

openapi_python_client/parser/properties.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class Property:
3636
python_name: str = field(init=False)
3737

3838
def __post_init__(self) -> None:
39-
self.python_name = utils.snake_case(self.name)
39+
self.python_name = utils.to_valid_python_identifier(utils.snake_case(self.name))
4040
if self.default is not None:
4141
self.default = self._validate_default(default=self.default)
4242

openapi_python_client/utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,29 @@ def kebab_case(value: str) -> str:
3434

3535
def remove_string_escapes(value: str) -> str:
3636
return value.replace('"', r"\"")
37+
38+
39+
def to_valid_python_identifier(value: str) -> str:
40+
"""
41+
Given a string, attempt to coerce it into a valid Python identifier.
42+
43+
If valid, return it unmodified.
44+
45+
If invalid, prepend a fixed prefix. This resolves some problems caused by the string's leading
46+
character.
47+
48+
If that prefix does not make it a valid identifier - there are unsupported non-leading
49+
characters - raise a ValueError.
50+
51+
See:
52+
https://docs.python.org/3/reference/lexical_analysis.html#identifiers
53+
"""
54+
if value.isidentifier():
55+
return value
56+
57+
new_value = f"field_{value}"
58+
59+
if new_value.isidentifier():
60+
return new_value
61+
62+
raise ValueError(f"Cannot convert {value} to a valid python identifier")

0 commit comments

Comments
 (0)