Beispiel #1
0
def reference_absolute(imports, py_package, py_type):
    """
    Returns a reference to a python type located in the root, i.e. sys.path.
    """
    string_import = ".".join(py_package)
    string_alias = safe_snake_case(string_import)
    imports.add(f"import {string_import} as {string_alias}")
    return f'"{string_alias}.{py_type}"'
Beispiel #2
0
def get_ref_type(package: str,
                 imports: set,
                 type_name: str,
                 unwrap: bool = True) -> str:
    """
    Return a Python type name for a proto type reference. Adds the import if
    necessary. Unwraps well known type if required.
    """
    # If the package name is a blank string, then this should still work
    # because by convention packages are lowercase and message/enum types are
    # pascal-cased. May require refactoring in the future.
    type_name = type_name.lstrip(".")

    is_wrapper = type_name in WRAPPER_TYPES

    if unwrap:
        if is_wrapper:
            wrapped_type = type(WRAPPER_TYPES[type_name]().value)
            return f"Optional[{wrapped_type.__name__}]"

        if type_name == "google.protobuf.Duration":
            return "timedelta"

        if type_name == "google.protobuf.Timestamp":
            return "datetime"

    if type_name.startswith(package):
        parts = type_name.lstrip(package).lstrip(".").split(".")
        if len(parts) == 1 or (len(parts) > 1
                               and parts[0][0] == parts[0][0].upper()):
            # This is the current package, which has nested types flattened.
            # foo.bar_thing => FooBarThing
            cased = [stringcase.pascalcase(part) for part in parts]
            type_name = f'"{"".join(cased)}"'

    # Use precompiled classes for google.protobuf.* objects
    if type_name.startswith("google.protobuf.") and type_name.count(".") == 2:
        type_name = type_name.rsplit(".", maxsplit=1)[1]
        import_package = "betterproto.lib.google.protobuf"
        import_alias = safe_snake_case(import_package)
        imports.add(f"import {import_package} as {import_alias}")
        return f"{import_alias}.{type_name}"

    if "." in type_name:
        # This is imported from another package. No need
        # to use a forward ref and we need to add the import.
        parts = type_name.split(".")
        parts[-1] = stringcase.pascalcase(parts[-1])
        imports.add(f"from .{'.'.join(parts[:-2])} import {parts[-2]}")
        type_name = f"{parts[-2]}.{parts[-1]}"

    return type_name
Beispiel #3
0
def reference_cousin(current_package: List[str], imports: Set[str],
                     py_package: List[str], py_type: str) -> str:
    """
    Returns a reference to a python type in a package that is not descendent, ancestor or sibling,
    and adds the required import that is aliased to avoid name conflicts.
    """
    shared_ancestry = os.path.commonprefix([current_package, py_package])
    distance_up = len(current_package) - len(shared_ancestry)
    string_from = f".{'.' * distance_up}" + ".".join(
        py_package[len(shared_ancestry):-1])
    string_import = py_package[-1]
    # Add trailing __ to avoid name mangling (python.org/dev/peps/pep-0008/#id34)
    string_alias = (
        f"{'_' * distance_up}" +
        safe_snake_case(".".join(py_package[len(shared_ancestry):])) + "__")
    imports.add(f"from {string_from} import {string_import} as {string_alias}")
    return f'"{string_alias}.{py_type}"'