Skip to content

[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

Closed

Conversation

boukeversteegh
Copy link
Collaborator

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, and animal.bird which is in the animal.proto file. the python files would look like

animal/domestic/cow.py
animal.py

that 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:

animal/
   __init__.py  # contains bird
   domestic/
     __init__.py  # contains cow

You can import the animal package simply with import animal as before, but still also import 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.

@boukeversteegh boukeversteegh marked this pull request as draft May 21, 2020 09:52
@nat-n
Copy link
Collaborator

nat-n commented May 21, 2020

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

Copy link
Collaborator

@nat-n nat-n left a 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)
Copy link
Collaborator

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.

Comment on lines +230 to +247
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"]:
Copy link
Collaborator

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
            )

Comment on lines +280 to +461
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)


Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants