Added python function scan

This commit is contained in:
Sven Heidemann 2022-01-16 01:56:16 +01:00
parent 2be818fcc1
commit 416940fd10
10 changed files with 198 additions and 9 deletions

View File

@ -7,10 +7,12 @@ from cpl_core.environment import ApplicationEnvironment
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.python_parser_abc import PythonParserABC
from py_to_uxf_core.abc.umlet_creator_abc import UmletCreatorABC
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.python_parser_service import PythonParserService
from py_to_uxf_core.service.umlet_creator_service import UmletCreatorService
@ -29,6 +31,7 @@ class Startup(StartupABC):
def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC:
services.add_transient(FileScannerABC, FileScannerService)
services.add_transient(ClassScannerABC, ClassScannerService)
services.add_transient(FunctionScannerABC, FunctionScannerService)
services.add_singleton(PythonParserABC, PythonParserService)
services.add_singleton(UmletCreatorABC, UmletCreatorService)

View File

@ -1,8 +1,6 @@
from abc import ABC, abstractmethod
from typing import Optional
from cpl_query.extension import List
from py_to_uxf_core.model.python_class import PythonClass

View File

@ -0,0 +1,13 @@
from abc import ABC, abstractmethod
from typing import Optional
from py_to_uxf_core.model.python_function import PythonFunction
class FunctionScannerABC(ABC):
@abstractmethod
def __init__(self): pass
@abstractmethod
def scan_line_for_function(self, line: str) -> Optional[PythonFunction]: pass

View File

@ -0,0 +1,9 @@
from enum import Enum
class FunctionAccessModifierEnum(Enum):
public = '+'
private = '-'
protected = '#'

View File

@ -1,8 +1,21 @@
from cpl_query.extension import List
from py_to_uxf_core.model.python_function import PythonFunction
class PythonClass:
def __init__(self, name):
self._name = name
self._functions = List(PythonFunction)
@property
def name(self) -> str:
return self._name
@property
def functions(self) -> List[PythonFunction]:
return self._functions
def add_function(self, func: PythonFunction):
self._functions.append(func)

View File

@ -0,0 +1,29 @@
from cpl_query.extension import List
from py_to_uxf_core.model.function_access_modifier_enum import FunctionAccessModifierEnum
from py_to_uxf_core.model.python_function_argument import PythonFunctionArgument
class PythonFunction:
def __init__(self, access_modifier: FunctionAccessModifierEnum, name: str, args: List[PythonFunctionArgument], return_type: str):
self._access_modifier = access_modifier
self._name = name
self._args = args
self._return_type = return_type
@property
def access_modifier(self):
return self._access_modifier
@property
def name(self) -> str:
return self._name
@property
def args(self) -> List[PythonFunctionArgument]:
return self._args
@property
def return_type(self) -> str:
return self._return_type

View File

@ -0,0 +1,19 @@
class PythonFunctionArgument:
def __init__(self, name: str, type: str):
self._name = name
self._type = type
@property
def name(self) -> str:
return self._name
@property
def type(self) -> str:
return self._type
def __str__(self):
return f'{self._name}: {self._type}'
def __repr__(self):
return f'{self._name}: {self._type}'

View File

@ -12,8 +12,8 @@ class ClassScannerService(ClassScannerABC):
ClassScannerABC.__init__(self)
def scan_line_for_classes(self, line: str) -> Optional[PythonClass]:
if 'class' in line:
name = line.split(' ')[1]
if line.startswith('class'):
name = line.split('class ')[1]
if '(' in name:
name = name.split('(')[0]
else:

View File

@ -0,0 +1,66 @@
from typing import Optional
import value as value
from cpl_query.extension import List
from py_to_uxf_core.abc.function_scanner_abc import FunctionScannerABC
from py_to_uxf_core.model.function_access_modifier_enum import FunctionAccessModifierEnum
from py_to_uxf_core.model.python_function import PythonFunction
from py_to_uxf_core.model.python_function_argument import PythonFunctionArgument
class FunctionScannerService(FunctionScannerABC):
def __init__(self):
FunctionScannerABC.__init__(self)
def scan_line_for_function(self, line: str) -> Optional[PythonFunction]:
# def xy():
# def xy() -> int:
# def xy(x: int, y: int) -> int:
# async def xy(x: int, y: int) -> int:
# async def _xy(x: int, y: int) -> int:
# async def __xy(x: int, y: int) -> int:
if line.startswith('def') or line.startswith('async def'):
# name
name = line.split('def ')[1]
name = name.split('(')[0]
access_modifier = FunctionAccessModifierEnum.public
args = List()
return_type = 'None'
# access_modifier
if name == '__init__' or name == '__repr__' or name == '__str__':
access_modifier = FunctionAccessModifierEnum.public
elif name.startswith('_'):
access_modifier = FunctionAccessModifierEnum.protected
elif name.startswith('__'):
access_modifier = FunctionAccessModifierEnum.private
# args
args_str = line.split('(')[1]
args_str = args_str.split(')')[0]
# multiple arguments
if ',' in args_str:
for argument in args_str.split(','):
# with type
type_str = 'any'
arg_name = argument
if ':' in argument:
type_str = argument.split(': ')[1]
arg_name = argument.split(': ')[0]
# without type
if arg_name.startswith(' '):
arg_name = arg_name.split(' ')[1]
args.append(PythonFunctionArgument(arg_name, type_str))
# single argument
elif args_str != '':
pass
# return_type
if '->' in line:
return_type_str = line.split('-> ')[1]
return_type = return_type_str.split(':')[0]
return PythonFunction(access_modifier, name, args, return_type)
return None

View File

@ -4,27 +4,66 @@ from cpl_core.console import Console
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.python_parser_abc import PythonParserABC
from py_to_uxf_core.model.python_class import PythonClass
class PythonParserService(PythonParserABC):
def __init__(self, file_scanner: FileScannerABC, class_scanner: ClassScannerABC):
def __init__(
self,
file_scanner: FileScannerABC,
class_scanner: ClassScannerABC,
function_scanner: FunctionScannerABC
):
PythonParserABC.__init__(self)
self._file_scanner = file_scanner
self._class_scanner = class_scanner
self._function_scanner = function_scanner
def parse(self):
files = self._file_scanner.scan_files()
for file in files:
Console.write_line('f:', file)
Console.write_line('\nfi:', file)
is_comment = False
with open(file, 'r') as file_content:
cls: Optional[PythonClass] = None
for line in file_content.readlines():
if cls is None:
cls = self._class_scanner.scan_line_for_classes(line)
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
Console.write_line('c:', cls.name)
# one line comments
if line != '"""' and line.startswith('"""') and line.endswith('"""') or line.startswith('#'):
continue
# start multi line comment
if line.startswith('"""') and not is_comment:
is_comment = True
continue
# end multi line comment
if line.startswith('"""') or line.endswith('"""') and is_comment:
is_comment = False
continue
if is_comment:
continue
if cls is None:
cls = self._class_scanner.scan_line_for_classes(line)
if cls is not None:
Console.write_line('cl:', cls.name)
continue
func = self._function_scanner.scan_line_for_function(line)
if func is not None:
cls.add_function(func)
Console.write_line('fu:', func.access_modifier.value, func.name, func.args, func.return_type)