[WIP] Added implementation scanning
This commit is contained in:
		| @@ -4,9 +4,11 @@ from cpl_core.application import ApplicationABC | ||||
| from cpl_core.configuration import ConfigurationABC | ||||
| from cpl_core.console import Console | ||||
| from cpl_core.dependency_injection import ServiceProviderABC | ||||
| from cpl_query.extension import List | ||||
|  | ||||
| from py_to_uxf_core.abc.python_parser_abc import PythonParserABC | ||||
| from py_to_uxf_core.abc.umlet_creator_abc import UmletCreatorABC | ||||
| from py_to_uxf_core.model.class_implementation import ClassImplementation | ||||
| from py_to_uxf_core.model.python_class import PythonClass | ||||
|  | ||||
|  | ||||
| @@ -58,8 +60,11 @@ class Application(ApplicationABC): | ||||
|         Console.write_line(f'Output path:', self._file) | ||||
|         xml = '' | ||||
|         try: | ||||
|             classes: list[PythonClass] = self._parser.parse() | ||||
|             Console.write_line(f'Found {len(classes)} classes') | ||||
|             classes: List[PythonClass] = self._parser.parse_classes() | ||||
|             Console.write_line(f'Found {classes.count()} classes') | ||||
|             implementations: List[ClassImplementation] = self._parser.parse_implementations(classes) | ||||
|             Console.write_line(f'Found {implementations.count()} implementations') | ||||
|             Console.write_line(implementations) | ||||
|             # self._console_output(classes) | ||||
|             xml = self._umlet_creator.generate_xml(classes) | ||||
|         except Exception as e: | ||||
|   | ||||
| @@ -9,12 +9,14 @@ from py_to_uxf_core.abc.attribute_scanner_abc import AttributeScannerABC | ||||
| from py_to_uxf_core.abc.class_scanner_abc import ClassScannerABC | ||||
| from py_to_uxf_core.abc.file_scanner_abc import FileScannerABC | ||||
| from py_to_uxf_core.abc.function_scanner_abc import FunctionScannerABC | ||||
| from py_to_uxf_core.abc.implementation_scanner_abc import ImplementationScannerABC | ||||
| from py_to_uxf_core.abc.python_parser_abc import PythonParserABC | ||||
| from py_to_uxf_core.abc.umlet_creator_abc import UmletCreatorABC | ||||
| from py_to_uxf_core.service.attribute_scanner_service import AttributeScannerService | ||||
| from py_to_uxf_core.service.class_scanner_service import ClassScannerService | ||||
| from py_to_uxf_core.service.file_scanner_service import FileScannerService | ||||
| from py_to_uxf_core.service.function_scanner_service import FunctionScannerService | ||||
| from py_to_uxf_core.service.implementation_scanner_service import ImplementationScannerService | ||||
| from py_to_uxf_core.service.python_parser_service import PythonParserService | ||||
| from py_to_uxf_core.service.umlet_creator_service import UmletCreatorService | ||||
|  | ||||
| @@ -38,6 +40,7 @@ class Startup(StartupABC): | ||||
|         services.add_transient(ClassScannerABC, ClassScannerService) | ||||
|         services.add_transient(FunctionScannerABC, FunctionScannerService) | ||||
|         services.add_transient(AttributeScannerABC, AttributeScannerService) | ||||
|         services.add_transient(ImplementationScannerABC, ImplementationScannerService) | ||||
|  | ||||
|         services.add_singleton(PythonParserABC, PythonParserService) | ||||
|         services.add_singleton(UmletCreatorABC, UmletCreatorService) | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/py_to_uxf_core/abc/implementation_scanner_abc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/py_to_uxf_core/abc/implementation_scanner_abc.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| from abc import ABC, abstractmethod | ||||
| from typing import Optional | ||||
|  | ||||
| from py_to_uxf_core.model.class_implementation import ClassImplementation | ||||
| from py_to_uxf_core.model.python_class import PythonClass | ||||
|  | ||||
|  | ||||
| class ImplementationScannerABC(ABC): | ||||
|  | ||||
|     @abstractmethod | ||||
|     def __init__(self): pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     def scan_line_for_implementation(self, line: str, classes: list[PythonClass]) -> Optional[ClassImplementation]: pass | ||||
| @@ -2,6 +2,7 @@ from abc import ABC, abstractmethod | ||||
|  | ||||
| from cpl_query.extension import List | ||||
|  | ||||
| from py_to_uxf_core.model.class_implementation import ClassImplementation | ||||
| from py_to_uxf_core.model.python_class import PythonClass | ||||
|  | ||||
|  | ||||
| @@ -11,4 +12,7 @@ class PythonParserABC(ABC): | ||||
|     def __init__(self): pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     def parse(self) -> List[PythonClass]: pass | ||||
|     def parse_classes(self) -> List[PythonClass]: pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     def parse_implementations(self, classes: List[PythonClass]) -> List[ClassImplementation]: pass | ||||
|   | ||||
							
								
								
									
										22
									
								
								src/py_to_uxf_core/model/class_implementation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/py_to_uxf_core/model/class_implementation.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| from py_to_uxf_core.model.python_class import PythonClass | ||||
|  | ||||
|  | ||||
| class ClassImplementation: | ||||
|  | ||||
|     def __init__(self, base: PythonClass, subclass: PythonClass): | ||||
|         self._base = base | ||||
|         self._subclass = subclass | ||||
|  | ||||
|     @property | ||||
|     def base(self) -> PythonClass: | ||||
|         return self._base | ||||
|  | ||||
|     @property | ||||
|     def subclass(self) -> PythonClass: | ||||
|         return self._subclass | ||||
|  | ||||
|     def __str__(self): | ||||
|         return f'{self._subclass.name} -> {self._base.name}' | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return f'{self._subclass.name} -> {self._base.name}' | ||||
							
								
								
									
										32
									
								
								src/py_to_uxf_core/service/implementation_scanner_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/py_to_uxf_core/service/implementation_scanner_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| from typing import Optional | ||||
|  | ||||
| from cpl_core.console import Console | ||||
| from cpl_query.extension import List | ||||
|  | ||||
| from py_to_uxf_core.abc.implementation_scanner_abc import ImplementationScannerABC | ||||
| from py_to_uxf_core.model.class_implementation import ClassImplementation | ||||
| from py_to_uxf_core.model.python_class import PythonClass | ||||
|  | ||||
|  | ||||
| class ImplementationScannerService(ImplementationScannerABC): | ||||
|  | ||||
|     def __init__(self): | ||||
|         pass | ||||
|  | ||||
|     def scan_line_for_implementation(self, line: str, classes: List[PythonClass]) -> Optional[ClassImplementation]: | ||||
|         line = line.replace('    ', '') | ||||
|         line = line.replace('\t', '') | ||||
|         if line.startswith('class ') and '(' in line and ')' in line: | ||||
|             sub_name = line.split('class ')[1].split('(')[0] | ||||
|             sub: Optional[PythonClass] = classes.where(lambda c: c.name == sub_name).first_or_default() | ||||
|             if sub is None: | ||||
|                 return None | ||||
|  | ||||
|             base_name = line.split('(')[1].split(')')[0] | ||||
|             base: Optional[PythonClass] = classes.where(lambda c: c.name == base_name).first_or_default() | ||||
|             if base is None: | ||||
|                 return None | ||||
|  | ||||
|             return ClassImplementation(base, sub) | ||||
|  | ||||
|         return None | ||||
| @@ -8,7 +8,9 @@ from py_to_uxf_core.abc.attribute_scanner_abc import AttributeScannerABC | ||||
| from py_to_uxf_core.abc.class_scanner_abc import ClassScannerABC | ||||
| from py_to_uxf_core.abc.file_scanner_abc import FileScannerABC | ||||
| from py_to_uxf_core.abc.function_scanner_abc import FunctionScannerABC | ||||
| from py_to_uxf_core.abc.implementation_scanner_abc import ImplementationScannerABC | ||||
| from py_to_uxf_core.abc.python_parser_abc import PythonParserABC | ||||
| from py_to_uxf_core.model.class_implementation import ClassImplementation | ||||
| from py_to_uxf_core.model.python_class import PythonClass | ||||
|  | ||||
|  | ||||
| @@ -19,7 +21,8 @@ class PythonParserService(PythonParserABC): | ||||
|             file_scanner: FileScannerABC, | ||||
|             class_scanner: ClassScannerABC, | ||||
|             function_scanner: FunctionScannerABC, | ||||
|             attribute_scanner: AttributeScannerABC | ||||
|             attribute_scanner: AttributeScannerABC, | ||||
|             implementation_scanner: ImplementationScannerABC | ||||
|     ): | ||||
|         PythonParserABC.__init__(self) | ||||
|  | ||||
| @@ -27,8 +30,11 @@ class PythonParserService(PythonParserABC): | ||||
|         self._class_scanner = class_scanner | ||||
|         self._function_scanner = function_scanner | ||||
|         self._attribute_scanner = attribute_scanner | ||||
|         self._implementation_scanner = implementation_scanner | ||||
|  | ||||
|     def parse(self) -> List[PythonClass]: | ||||
|         self._files_with_classes: list[str] = [] | ||||
|  | ||||
|     def parse_classes(self) -> List[PythonClass]: | ||||
|         files = self._file_scanner.scan_files() | ||||
|         classes = List(PythonClass) | ||||
|         for file in files: | ||||
| @@ -74,6 +80,7 @@ class PythonParserService(PythonParserABC): | ||||
|                             cls = self._class_scanner.scan_line_for_classes(line) | ||||
|                             if cls is not None: | ||||
|                                 classes.append(cls) | ||||
|                                 self._files_with_classes.append(file) | ||||
|                             continue | ||||
|  | ||||
|                         func = self._function_scanner.scan_line_for_function(line) | ||||
| @@ -90,6 +97,56 @@ class PythonParserService(PythonParserABC): | ||||
|                             cls.add_attribute(attribute) | ||||
|                     except Exception as e: | ||||
|                         Console.error(f'Parsing {file}@{i}', f'{e} -> {traceback.format_exc()}') | ||||
|                         file_content.close() | ||||
|                         exit() | ||||
|  | ||||
|                 file_content.close() | ||||
|         return classes | ||||
|  | ||||
|     def parse_implementations(self, classes: List[PythonClass]) -> List[ClassImplementation]: | ||||
|         implementations = List() | ||||
|         files = self._files_with_classes | ||||
|         for file in files: | ||||
|             is_comment = False | ||||
|             with open(file, 'r') as file_content: | ||||
|                 implementation: Optional[ClassImplementation] = None | ||||
|                 lines = file_content.readlines() | ||||
|  | ||||
|                 for i in range(len(lines)): | ||||
|                     try: | ||||
|                         line = lines[i] | ||||
|                         line = line.replace('    ', '') | ||||
|                         line = line.replace('\t', '') | ||||
|                         # replace line break at the end of line | ||||
|                         if line.endswith('\n'): | ||||
|                             line = line.replace('\n', '') | ||||
|  | ||||
|                         if line == '\n' or line == '': | ||||
|                             continue | ||||
|  | ||||
|                         # one line comments | ||||
|                         if line != '"""' and line.startswith('"""') and line.endswith('"""') or line.startswith('#'): | ||||
|                             continue | ||||
|  | ||||
|                         # start multi line comment | ||||
|                         if line.startswith('"""') or line.count('"""') == 1 and not is_comment: | ||||
|                             is_comment = True | ||||
|                             continue | ||||
|  | ||||
|                         # end multi line comment | ||||
|                         if line.startswith('"""') or line.endswith('"""') or line.count('"""') == 1 and is_comment: | ||||
|                             is_comment = False | ||||
|                             continue | ||||
|  | ||||
|                         if is_comment: | ||||
|                             continue | ||||
|  | ||||
|                         if implementation is None: | ||||
|                             implementation = self._implementation_scanner.scan_line_for_implementation(line, classes) | ||||
|                             if implementation is not None: | ||||
|                                 implementations.append(implementation) | ||||
|                     except Exception as e: | ||||
|                         Console.error(f'Parsing {file}@{i}', f'{e} -> {traceback.format_exc()}') | ||||
|                         file_content.close() | ||||
|                         exit() | ||||
|                 file_content.close() | ||||
|         return implementations | ||||
|   | ||||
		Reference in New Issue
	
	Block a user