Handle mutable default arguments cleanly

When generating code, ensure that default list/dict arguments are
initialised in local scope if unspecified or `None`.
This commit is contained in:
Arun Babu Neelicattu
2020-07-07 18:13:43 +02:00
committed by Bouke Versteegh
parent 42e197f985
commit 0ba0692dec
6 changed files with 63 additions and 2 deletions

View File

@@ -369,6 +369,10 @@ def lookup_method_input_type(method, types):
return known_type
def is_mutable_field_type(field_type: str) -> bool:
return field_type.startswith("List[") or field_type.startswith("Dict[")
def read_protobuf_service(
service: ServiceDescriptorProto, index, proto_file, content, output_types
):
@@ -384,8 +388,23 @@ def read_protobuf_service(
for j, method in enumerate(service.method):
method_input_message = lookup_method_input_type(method, output_types)
# This section ensures that method arguments having a default
# value that is initialised as a List/Dict (mutable) is replaced
# with None and initialisation is deferred to the beginning of the
# method definition. This is done so to avoid any side-effects.
# Reference: https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments
mutable_default_args = []
if method_input_message:
for field in method_input_message["properties"]:
if (
not method.client_streaming
and field["zero"] != "None"
and is_mutable_field_type(field["type"])
):
mutable_default_args.append((field["py_name"], field["zero"]))
field["zero"] = "None"
if field["zero"] == "None":
template_data["typing_imports"].add("Optional")
@@ -407,6 +426,7 @@ def read_protobuf_service(
),
"client_streaming": method.client_streaming,
"server_streaming": method.server_streaming,
"mutable_default_args": mutable_default_args,
}
)

View File

@@ -80,6 +80,10 @@ class {{ service.py_name }}Stub(betterproto.ServiceStub):
{{ method.comment }}
{% endif %}
{%- for py_name, zero in method.mutable_default_args %}
{{ py_name }} = {{ py_name }} or {{ zero }}
{% endfor %}
{% if not method.client_streaming %}
request = {{ method.input }}()
{% for field in method.input_message.properties %}