From 9ba4e3ad25b4c5c9102a65893892e6695a076928 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 13 Jul 2023 08:49:53 +1000 Subject: [PATCH] Revert "Revert "Support classes being in any arrangement"" This reverts commit ca9fa7cc20f175a732e3249a0efadbe524bb9cc5. --- src/clapy/engine.py | 64 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/clapy/engine.py b/src/clapy/engine.py index d867240..0bacef5 100644 --- a/src/clapy/engine.py +++ b/src/clapy/engine.py @@ -1,11 +1,13 @@ import asyncio +import importlib +import inspect import os from typing import Dict, List, Optional, Type from .common import DIR_EXCLUSIONS, FILE_EXCLUSIONS, Common from .exceptions import PipeConfigurationError from .generics import TInputPort, TOutputPort -from .pipeline import IPipe, PipeConfiguration, PipeConfigurationOption +from .pipeline import IPipe, InputPort, PipeConfiguration, PipeConfigurationOption from .services import IPipelineFactory, IServiceProvider, IUseCaseInvoker @@ -115,6 +117,7 @@ def construct_usecase_registry( directory_exclusion_patterns: Optional[List[str]] = [], file_exclusion_patterns: Optional[List[str]] = []) -> Dict[str, List[str]]: ''' + TODO: DOC CHANGE Summary ------- Scans the provided project location, or entire project if no location provided, for use @@ -138,26 +141,61 @@ def construct_usecase_registry( _UsecaseRegistry = {} for _Location in usecase_locations: - for _Root, _Directories, _Files in os.walk(_Location): + _Classes = Engine._get_all_classes(_Location, directory_exclusion_patterns, file_exclusion_patterns) - Common.apply_exclusion_filter(_Directories, directory_exclusion_patterns + DIR_EXCLUSIONS) - Common.apply_exclusion_filter(_Files, file_exclusion_patterns + FILE_EXCLUSIONS) + _InputPortClasses = [] + _PipeClasses = [] - _DirectoryNamespace = _Root.replace('/', '.').lstrip(".") - _Pipes = [] + for _Class in _Classes: + if issubclass(_Class, InputPort) and _Class != InputPort and _Class not in _InputPortClasses: + _InputPortClasses.append(_Class) - for _File in _Files: - _Namespace = _DirectoryNamespace + "." + _File[:-3] - _Class = Common.import_class_by_namespace(_Namespace) + if issubclass(_Class, IPipe) and _Class not in _PipeClasses: + _PipeClasses.append(_Class) - if issubclass(_Class, IPipe): - _Pipes.append(_Namespace) + for _Pipe in _PipeClasses: + _ExecuteAsyncMethod = next((_Function for _Name, _Function + in inspect.getmembers(_Pipe, inspect.isfunction) + if _Name == IPipe.execute_async.__name__), None) - if _Pipes: - _UsecaseRegistry[_DirectoryNamespace] = _Pipes + _InputPortParam = next((_Param for _Param + in inspect.signature(_ExecuteAsyncMethod).parameters.values() + if _Param.annotation != inspect.Parameter.empty + and _Param.annotation in _InputPortClasses), None) + + if _InputPortParam: + _UsecaseKey = inspect.getsourcefile(next(_Class for _Class + in _InputPortClasses + if type(_Class) == type(_InputPortParam.annotation))) + if _UsecaseKey: + _UsecaseRegistry.setdefault(_UsecaseKey, []).append(_Pipe) + else: + raise ModuleNotFoundError(f"Could not get the source file of {_Pipe}.") return _UsecaseRegistry + @staticmethod + def _get_all_classes(location, directory_exclusion_patterns, file_exclusion_patterns): + ''' + TODO: DOC + ''' + _Classes = [] + + for _Root, _Directories, _Files in os.walk(location): + + Common.apply_exclusion_filter(_Directories, directory_exclusion_patterns + DIR_EXCLUSIONS) + Common.apply_exclusion_filter(_Files, file_exclusion_patterns + FILE_EXCLUSIONS) + + _DirectoryNamespace = _Root.replace('/', '.').lstrip(".") + + for _File in _Files: + _Namespace = _DirectoryNamespace + "." + _File[:-3] + _Module = importlib.import_module(_Namespace, package=None) + for _Name, _Class in inspect.getmembers(_Module, inspect.isclass): + _Classes.append(_Class) + + return _Classes + @staticmethod def _insert_pipe( new_pipe: IPipe,