Handle fields that clash with Python reserved keywords
This commit is contained in:
parent
eff9021529
commit
ff8463cf12
@ -296,13 +296,13 @@ $ pipenv run tests
|
|||||||
- [ ] Any support
|
- [ ] Any support
|
||||||
- [x] Enum strings
|
- [x] Enum strings
|
||||||
- [ ] Well known types support (timestamp, duration, wrappers)
|
- [ ] Well known types support (timestamp, duration, wrappers)
|
||||||
- [ ] Support different casing (orig vs. camel vs. others?)
|
- [x] Support different casing (orig vs. camel vs. others?)
|
||||||
- [ ] Async service stubs
|
- [ ] Async service stubs
|
||||||
- [x] Unary-unary
|
- [x] Unary-unary
|
||||||
- [x] Server streaming response
|
- [x] Server streaming response
|
||||||
- [ ] Client streaming request
|
- [ ] Client streaming request
|
||||||
- [x] Renaming messages and fields to conform to Python name standards
|
- [x] Renaming messages and fields to conform to Python name standards
|
||||||
- [ ] Renaming clashes with language keywords and standard library top-level packages
|
- [x] Renaming clashes with language keywords
|
||||||
- [x] Python package
|
- [x] Python package
|
||||||
- [x] Automate running tests
|
- [x] Automate running tests
|
||||||
- [ ] Cleanup!
|
- [ ] Cleanup!
|
||||||
|
@ -26,6 +26,8 @@ import grpclib.client
|
|||||||
import grpclib.const
|
import grpclib.const
|
||||||
import stringcase
|
import stringcase
|
||||||
|
|
||||||
|
from .casing import safe_snake_case
|
||||||
|
|
||||||
# Proto 3 data types
|
# Proto 3 data types
|
||||||
TYPE_ENUM = "enum"
|
TYPE_ENUM = "enum"
|
||||||
TYPE_BOOL = "bool"
|
TYPE_BOOL = "bool"
|
||||||
@ -642,7 +644,7 @@ class Message(ABC):
|
|||||||
for field in dataclasses.fields(self):
|
for field in dataclasses.fields(self):
|
||||||
meta = FieldMetadata.get(field)
|
meta = FieldMetadata.get(field)
|
||||||
v = getattr(self, field.name)
|
v = getattr(self, field.name)
|
||||||
cased_name = casing(field.name)
|
cased_name = casing(field.name).rstrip("_")
|
||||||
if meta.proto_type == "message":
|
if meta.proto_type == "message":
|
||||||
if isinstance(v, list):
|
if isinstance(v, list):
|
||||||
# Convert each item.
|
# Convert each item.
|
||||||
@ -686,7 +688,7 @@ class Message(ABC):
|
|||||||
self._serialized_on_wire = True
|
self._serialized_on_wire = True
|
||||||
fields_by_name = {f.name: f for f in dataclasses.fields(self)}
|
fields_by_name = {f.name: f for f in dataclasses.fields(self)}
|
||||||
for key in value:
|
for key in value:
|
||||||
snake_cased = stringcase.snakecase(key)
|
snake_cased = safe_snake_case(key)
|
||||||
if snake_cased in fields_by_name:
|
if snake_cased in fields_by_name:
|
||||||
field = fields_by_name[snake_cased]
|
field = fields_by_name[snake_cased]
|
||||||
meta = FieldMetadata.get(field)
|
meta = FieldMetadata.get(field)
|
||||||
|
41
betterproto/casing.py
Normal file
41
betterproto/casing.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import stringcase
|
||||||
|
|
||||||
|
|
||||||
|
def safe_snake_case(value: str) -> str:
|
||||||
|
"""Snake case a value taking into account Python keywords."""
|
||||||
|
value = stringcase.snakecase(value)
|
||||||
|
if value in [
|
||||||
|
"and",
|
||||||
|
"as",
|
||||||
|
"assert",
|
||||||
|
"break",
|
||||||
|
"class",
|
||||||
|
"continue",
|
||||||
|
"def",
|
||||||
|
"del",
|
||||||
|
"elif",
|
||||||
|
"else",
|
||||||
|
"except",
|
||||||
|
"finally",
|
||||||
|
"for",
|
||||||
|
"from",
|
||||||
|
"global",
|
||||||
|
"if",
|
||||||
|
"import",
|
||||||
|
"in",
|
||||||
|
"is",
|
||||||
|
"lambda",
|
||||||
|
"nonlocal",
|
||||||
|
"not",
|
||||||
|
"or",
|
||||||
|
"pass",
|
||||||
|
"raise",
|
||||||
|
"return",
|
||||||
|
"try",
|
||||||
|
"while",
|
||||||
|
"with",
|
||||||
|
"yield",
|
||||||
|
]:
|
||||||
|
# https://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles
|
||||||
|
value += "_"
|
||||||
|
return value
|
@ -27,6 +27,8 @@ from google.protobuf.descriptor_pb2 import (
|
|||||||
ServiceDescriptorProto,
|
ServiceDescriptorProto,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from betterproto.casing import safe_snake_case
|
||||||
|
|
||||||
|
|
||||||
def get_ref_type(package: str, imports: set, type_name: str) -> str:
|
def get_ref_type(package: str, imports: set, type_name: str) -> str:
|
||||||
"""
|
"""
|
||||||
@ -255,7 +257,7 @@ def generate_code(request, response):
|
|||||||
data["properties"].append(
|
data["properties"].append(
|
||||||
{
|
{
|
||||||
"name": f.name,
|
"name": f.name,
|
||||||
"py_name": stringcase.snakecase(f.name),
|
"py_name": safe_snake_case(f.name),
|
||||||
"number": f.number,
|
"number": f.number,
|
||||||
"comment": get_comment(proto_file, path + [2, i]),
|
"comment": get_comment(proto_file, path + [2, i]),
|
||||||
"proto_type": int(f.type),
|
"proto_type": int(f.type),
|
||||||
|
5
betterproto/tests/keywords.json
Normal file
5
betterproto/tests/keywords.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"for": 1,
|
||||||
|
"with": 2,
|
||||||
|
"as": 3
|
||||||
|
}
|
7
betterproto/tests/keywords.proto
Normal file
7
betterproto/tests/keywords.proto
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
message Test {
|
||||||
|
int32 for = 1;
|
||||||
|
int32 with = 2;
|
||||||
|
int32 as = 3;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user