This class is used to dynamically load classes from files. Specifically, it is used to load data sources from the data_source/sources directory.
| 15 | |
| 16 | |
| 17 | class DynamicLoader: |
| 18 | """ |
| 19 | This class is used to dynamically load classes from files. |
| 20 | Specifically, it is used to load data sources from the data_source/sources directory. |
| 21 | """ |
| 22 | SOURCES_PATH = os.path.join('data_source', 'sources') |
| 23 | |
| 24 | @staticmethod |
| 25 | def extract_classes(file_path: str): |
| 26 | with open(file_path, 'r') as f: |
| 27 | file_ast = ast.parse(f.read()) |
| 28 | classes = {} |
| 29 | for node in file_ast.body: |
| 30 | if isinstance(node, ast.ClassDef): |
| 31 | classes[node.name] = {'node': node, 'file': file_path} |
| 32 | return classes |
| 33 | |
| 34 | @staticmethod |
| 35 | def get_data_source_class(data_source_name: str): |
| 36 | class_name = f"{snake_case_to_pascal_case(data_source_name)}DataSource" |
| 37 | class_file_path = DynamicLoader.find_class_file(DynamicLoader.SOURCES_PATH, class_name) |
| 38 | return DynamicLoader.get_class(class_file_path, class_name) |
| 39 | |
| 40 | @staticmethod |
| 41 | def get_class(file_path: str, class_name: str): |
| 42 | loader = importlib.machinery.SourceFileLoader(class_name, file_path) |
| 43 | module = loader.load_module() |
| 44 | try: |
| 45 | return getattr(module, class_name) |
| 46 | except AttributeError: |
| 47 | raise AttributeError(f"Class {class_name} not found in module {module}," |
| 48 | f"make sure you named the class correctly (it should be <Platform>DataSource)") |
| 49 | |
| 50 | @staticmethod |
| 51 | def find_class_file(directory, class_name): |
| 52 | for root, dirs, files in os.walk(directory): |
| 53 | for file in files: |
| 54 | if file.endswith('.py'): |
| 55 | file_path = os.path.join(root, file) |
| 56 | classes = DynamicLoader.extract_classes(file_path) |
| 57 | if class_name in classes: |
| 58 | return file_path |
| 59 | return None |
| 60 | |
| 61 | @staticmethod |
| 62 | def find_data_sources() -> Dict[str, ClassInfo]: |
| 63 | all_classes = {} |
| 64 | # First, extract all classes and their file paths |
| 65 | for root, dirs, files in os.walk(DynamicLoader.SOURCES_PATH): |
| 66 | for file in files: |
| 67 | if file.endswith('.py'): |
| 68 | file_path = os.path.join(root, file) |
| 69 | all_classes.update(DynamicLoader.extract_classes(file_path)) |
| 70 | |
| 71 | def is_base_data_source(class_name: str): |
| 72 | if class_name not in all_classes: |
| 73 | return False |
| 74 |
nothing calls this directly
no outgoing calls
no test coverage detected