Ejemplo n.º 1
0
    def initialize_top_entries(self, manager: ImportManager,
                               base_ontology_module: ModuleType):
        """
        Parses the file corresponding to `base_ontology_module` -
        (1) Imports the imports defined by the base file,
        (2) Imports the public API defined by the base file in it's `__all__`
        attribute. The imports are added to the import manager.

        (3) Extracts the name and inheritance of the class definitions and
        populates `self.top_to_core_entries`,
        (4) Extracts `__init__` arguments of class definitions and populates
        `self.top_init_args`
        (5) Includes type annotations for the `__init__` arguments.

        Args:
            manager: The import manager to be populated
            base_ontology_module: File path of the module to be parsed.

        Returns:
        """
        tree = ast.parse(open(base_ontology_module.__file__, 'r').read())
        base_module_name = base_ontology_module.__name__

        # Record a map from the import name to the full name.
        full_names = {}

        for elem in tree.body:
            # Adding all the imports.
            if isinstance(elem, ast.Import):
                for import_ in elem.names:
                    as_name = import_.asname
                    import_name = import_.name if as_name is None else as_name
                    full_names[import_name] = import_.name
                    manager.add_object_to_import(import_.name)

            if isinstance(elem, ast.ImportFrom):
                for import_ in elem.names:
                    full_names[import_.name] = f"{elem.module}.{import_.name}"
                    full_name = f"{elem.module}.{import_.name}"
                    manager.add_object_to_import(full_name)

            # Adding all the module objects defined in `__all__` to imports.
            if isinstance(elem, ast.Assign) and len(elem.targets) > 0:
                if elem.targets[0].id == '__all__':
                    full_names.update(
                        [(name.s,
                          f"{base_ontology_module.__name__}.{name.s}")
                         for name in elem.value.elts])

                    for name in elem.value.elts:
                        full_class_name = f"{base_module_name}.{name.s}"
                        manager.add_object_to_import(full_class_name)

            # Adding `__init__` arguments for each class
            if isinstance(elem, ast.ClassDef):

                # Adding base names for each class
                elem_base_names = set()
                if elem.bases is not None and len(elem.bases) > 0:
                    for elem_base in elem.bases:
                        while isinstance(elem_base, ast.Subscript):
                            # TODO: Doesn't handle typed class well.
                            elem_base = elem_base.slice.value
                        elem_base_names.add(elem_base.id)
                init_func = None

                for func in elem.body:
                    if isinstance(func, ast.FunctionDef) and \
                            func.name == '__init__':
                        init_func = func
                        break

                if init_func is None:
                    warnings.warn(
                        f"No `__init__` function found in the class"
                        f" {elem.name} of the module "
                        f"{base_ontology_module}.")
                else:
                    full_ele_name = full_names[elem.name]

                    # Assuming no variable args and keyword only args present in
                    # the base ontology module
                    for arg in init_func.args.args:
                        # Parsing the nested list of arg annotations
                        if arg.annotation is not None:
                            arg_ann = arg.annotation
                            while isinstance(arg_ann, ast.Subscript):
                                module = arg_ann.value.id
                                if module is not None and module in full_names:
                                    arg_ann.value.id = full_names[module]
                                arg_ann = arg_ann.slice.value
                            module = arg_ann.id

                            if module is not None and module in full_names:
                                arg_ann.id = full_names[module]
                                manager.add_object_to_import(arg_ann.id)

                                # Convert from PackType to more concrete pack
                                # type, such as DataPack or MultiPack.
                                if arg_ann.id == PACK_TYPE_CLASS_NAME:
                                    pack_class = hardcoded_pack_map(
                                        full_ele_name)
                                    manager.add_object_to_import(pack_class)
                                    arg_ann.id = pack_class

                    self.top_to_core_entries[full_ele_name] = elem_base_names
                    self.base_entry_lookup[full_ele_name] = full_ele_name
                    self.top_init_args[full_ele_name] = init_func.args