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