-
Notifications
You must be signed in to change notification settings - Fork 229
[Draft] Fix many import related bugs #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Draft] Fix many import related bugs #47
Conversation
…ents, module and filename conflicts, import name conflicts, relative imports
…itted from output
This looks good (I'm still reading). Perhaps rounding up issues of this nature would also fit well within the scope of this change: #37 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to run black.
) | ||
|
||
output["enums"].append(data) | ||
read_item(item, path, proto_file, output_file_data['package'], output_file_data['template_data'], global_messages) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
black would have something to say about the line length here.
for output_file_name, output_file_data in output_files.items(): | ||
package = output_file_data["package"] | ||
template_data = { | ||
"package": package, | ||
"files": [f.name for f in options["files"]], | ||
"files": [f.name for f in output_file_data["files"]], | ||
"imports": set(), | ||
"datetime_imports": set(), | ||
"typing_imports": set(), | ||
"messages": [], | ||
"enums": [], | ||
"services": [], | ||
} | ||
output_file_data['template_data'] = template_data | ||
|
||
type_mapping = {} | ||
|
||
for proto_file in options["files"]: | ||
# print(proto_file.message_type, file=sys.stderr) | ||
# print(proto_file.service, file=sys.stderr) | ||
# print(proto_file.source_code_info, file=sys.stderr) | ||
|
||
# read messages | ||
global_messages = [] | ||
for output_file_name, output_file_data in output_files.items(): | ||
for proto_file in output_file_data["files"]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this bit could be streamlined a bit more since you're here:
global_messages = []
for _, output_file_data in output_files.items():
output_file_data['template_data'] = {
"package": output_file_data["package"],
"files": [f.name for f in output_file_data["files"]],
"imports": set(),
"datetime_imports": set(),
"typing_imports": set(),
"messages": [],
"enums": [],
"services": [],
}
# read messages
for proto_file in output_file_data["files"]:
for item, path in traverse(proto_file):
read_item(
item,
path,
proto_file,
# < No need to pass packages seperately becuase it's in template_data >
output_file_data['template_data'],
global_messages
)
one_of = item.oneof_decl[field.oneof_index].name | ||
|
||
if "Optional[" in t: | ||
template_data["typing_imports"].add("Optional") | ||
|
||
if "timedelta" in t: | ||
template_data["datetime_imports"].add("timedelta") | ||
elif "datetime" in t: | ||
template_data["datetime_imports"].add("datetime") | ||
|
||
item_data["properties"].append( | ||
{ | ||
"name": field.name, | ||
"py_name": safe_snake_case(field.name), | ||
"number": field.number, | ||
"comment": get_comment(proto_file, path + [2, i]), | ||
"proto_type": int(field.type), | ||
"field_type": field_type, | ||
"field_wraps": field_wraps, | ||
"map_types": map_types, | ||
"type": t, | ||
"zero": zero, | ||
"repeated": repeated, | ||
"packed": packed, | ||
"one_of": one_of, | ||
} | ||
) | ||
|
||
template_data["messages"].append(item_data) | ||
global_messages.append(item_data) | ||
elif isinstance(item, EnumDescriptorProto): | ||
item_data.update( | ||
{ | ||
"type": "Enum", | ||
"comment": get_comment(proto_file, path), | ||
"entries": [ | ||
{ | ||
"name": v.name, | ||
"value": v.number, | ||
"comment": get_comment(proto_file, path + [2, i]), | ||
} | ||
for i, v in enumerate(item.value) | ||
], | ||
} | ||
) | ||
template_data["enums"].append(item_data) | ||
|
||
|
||
def read_service(i, package, proto_file, service, template_data, global_messages): | ||
data = { | ||
"name": service.name, | ||
"py_name": stringcase.pascalcase(service.name), | ||
"comment": get_comment(proto_file, [6, i]), | ||
"methods": [], | ||
} | ||
for j, method in enumerate(service.method): | ||
if method.client_streaming: | ||
raise NotImplementedError("Client streaming not yet supported") | ||
sys.stderr.write(f' add {method.name}\n') | ||
|
||
input_message = None | ||
input_type = get_ref_type( | ||
package, template_data["imports"], method.input_type | ||
).strip('"') | ||
for msg in global_messages: | ||
if msg["full_name"] == method.input_type.lstrip('.'): | ||
input_message = msg | ||
for field in msg["properties"]: | ||
if field["zero"] == "None": | ||
template_data["typing_imports"].add("Optional") | ||
break | ||
|
||
if not input_message: | ||
sys.stderr.write(f"no input message found for {input_type} / {method}\n") | ||
sys.stderr.write(f" {global_messages}\n") | ||
sys.stderr.write(f"\n") | ||
|
||
method_data = { | ||
"name": method.name, | ||
"py_name": stringcase.snakecase(method.name), | ||
"comment": get_comment(proto_file, [6, i, 2, j], indent=8), | ||
"route": f"/{package}.{service.name}/{method.name}", | ||
"input": get_ref_type( | ||
package, template_data["imports"], method.input_type | ||
).strip('"'), | ||
"input_message": input_message, | ||
"output": get_ref_type( | ||
package, template_data["imports"], method.output_type, unwrap=False | ||
).strip('"'), | ||
"client_streaming": method.client_streaming, | ||
"server_streaming": method.server_streaming, | ||
} | ||
# sys.stderr.write(f'METHOD: {method_data}\n') | ||
data["methods"].append(method_data) | ||
|
||
if method.server_streaming: | ||
template_data["typing_imports"].add("AsyncGenerator") | ||
template_data["services"].append(data) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The diff makes it look like this is all new. Is it fair to assume none of it really is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Its because I've moved some code into functions, and the changed indentation has made it look new.
However, I did make an important change on line 427
if msg["full_name"] == method.input_type.lstrip('.'):
to compare with the full name, rather than part of the name, so that all messages can be matched consistently.
Fixes lots of issues with imports.
Circular imports
two python files referring to eachother, preventing imports
I've solved this by wrapping generated Types in quotes (
List["Foo"]
)Imports from parent package
I've solved this for now by using absolute imports there, but I think this still needs a change to use
from ... import [parent]
instead.Module and filename conflicts
consider two messages
animal.domestic.cow
, andanimal.bird
which is in theanimal.proto
file. the python files would look likethat means
animal
is now both a file and a module, and it results in conflicts.I've solved this by exporting all packages as modules:
You can import the animal package simply with
import animal
as before, but still alsoimport animal.domestic
.import name conflicts
Importing two subpackages with the same name resulted in conflicts. See: #25
Solved this by always aliasing the imported package with full package path:
import from .aedt.method_data import v0 as aedt_method_data_v0
relative imports
Relative imports work correctly for siblings, children and descendants.