PK!*Ilfastest/__init__.pyname = "fastest" PK![v fastest/__main__.pyimport argparse import os import fnmatch import time import subprocess from fastest.io.read_file import read_file from fastest.code_assets.function import get_functions from fastest.compiler import compile_tests from watchdog.observers import Observer from watchdog.events import PatternMatchingEventHandler from logger.logger import logger parser = argparse.ArgumentParser(description='Create test cases automatically') parser.add_argument('--path', help='Project root, use $(pwd) to be sure') parser.add_argument('--source', help='Modules to check coverage for') parser.add_argument('--poll-duration', default='1', help='Modules to check coverage for') parser.add_argument( '--exclude', help='Comma separated names of files/dirs that should NOT be watched', ) args = parser.parse_args() monitor_path = args.path if args.path is not None else os.getcwd() poll_duration = int(args.poll_duration) \ if type(args.poll_duration) is str and \ args.poll_duration.isdigit() \ else 1 def make_test_module(): if os.path.exists('./test'): return os.mkdir('./test') open('./test/__init__.py', 'a').close() def get_report_path(): return os.path.abspath(os.path.join(monitor_path, 'htmlcov/index.html')) def get_test_files(): test_files = os.listdir('./test') return [ test_file.replace('.py', '') for test_file in test_files if '.pyc' not in test_file and '__pycache__' not in test_file ] def is_path_to_be_ignored(event_path, ignore_patterns): for ignored_pattern in ignore_patterns: _, current_file_path = event_path.split(monitor_path + '/') if fnmatch.fnmatch(current_file_path, ignored_pattern): return True return False def create_test_command(): test_files = get_test_files() return ['test.{}'.format(test_file) for test_file in test_files] def execute_coverage_and_tests(source): command = create_test_command() report_path = get_report_path() source_present_command = ['coverage', 'run', '--source', source, '-m', 'unittest'] + command source_missing_command = ['coverage', 'run', '-m', 'unittest'] + command subprocess.call(source_present_command) if source else subprocess.call(source_missing_command) subprocess.call(['coverage', 'report']) subprocess.call(['coverage', 'html']) logger.info('Check coverage: ' + report_path) def main(): logger.info('Monitoring started...') class PyFileHandler(PatternMatchingEventHandler): patterns = ['*.py'] exclude_files = args.exclude if args.exclude is not None else '' ignore_patterns = [path.strip() for path in exclude_files.split(',')] ignore_patterns += ['test/*', '__pycache__', '*.pyc', '*__test.py'] def process(self, event): if is_path_to_be_ignored(event.src_path, self.ignore_patterns): return None page = read_file(monitor_path, event.src_path) functions = get_functions(page) compile_tests.build(functions, event.src_path, monitor_path) execute_coverage_and_tests(args.source) def on_modified(self, event): self.process(event) def on_created(self, event): self.process(event) observer = Observer() observer.schedule(PyFileHandler(), monitor_path, recursive=True) observer.start() try: while True: time.sleep(poll_duration) except KeyboardInterrupt: observer.stop() observer.join() PK!fastest/bodies/__init__.pyname = "bodies_pkg"PK!h(q::fastest/bodies/f.pyimport uuid from fastest.constants import KEYS, CONTENT def case_generator(): return str(uuid.uuid4()).upper().replace("-", "")[0:10] def get_empty_of_type(input_type): empty_type = { 'str': '\'\'', 'int': '0', 'list': '[]', 'dict': '{}' } return empty_type[input_type] def create_naive_test_case(function_object, test): """ ---- :param function_object: :param test: :return: """ test_template = CONTENT.TEST_CASE_TEMPLATE.format( function_name=function_object[KEYS.NAME], case_id=case_generator(), ) params = [] for param in function_object[KEYS.TESTS][KEYS.PARAMS]: params.append(get_empty_of_type(param)) if len(params) > 0: empty_param_call = '{}({})'.format(function_object[KEYS.NAME], ', '.join(params)) test_template += CONTENT.TYPE_ASSERT_TEMPLATE.format( function=empty_param_call, value=function_object[KEYS.TESTS][KEYS.RETURN] ) if function_object[KEYS.TESTS][KEYS.VARIABLES]: for variable in function_object[KEYS.TESTS][KEYS.VARIABLES]: test_template += CONTENT.VARIABLES_TEMPLATE.format(variables=variable) test_template += CONTENT.ASSERTION_TEMPLATE.format(function=test[KEYS.FROM], value=test[KEYS.EXPECT]) return test_template PK!XE}fastest/code_assets/__init__.pyname = "code_assets_pkg" PK!efastest/code_assets/function.pyimport ast from fastest.code_assets.naive_case_detector import get_test_from_example_passage def get_functions_from_node(node): return [{ 'name': n.name, 'tests': get_test_from_example_passage(ast.get_docstring(n)) } for n in node.body if isinstance(n, ast.FunctionDef)] def get_functions(page): """ ------ examples: @let page = 'def f(): return 1' @end 1) get_functions(page) -> [{'name': 'f', 'tests': None}] ------ :param page: :return: """ node = ast.parse(page) functions = get_functions_from_node(node) classes = [n for n in node.body if isinstance(n, ast.ClassDef)] methods = [get_functions_from_node(class_) for class_ in classes] return functions + methods PK!b]]fastest/code_assets/keywords.pyFALSE = 'False' CLASS = 'class' FINALLY = 'finally' IS = 'is' RETURN = 'return' NONE = 'None' CONTINUE = 'continue' FOR = 'for' LAMBDA = 'lambda' TRY = 'try' TRUE = 'True' DEF = 'def' FROM = 'from' NONLOCAL = 'nonlocal' WHILE = 'while' AND = 'and' DEL = 'del' GLOBAL = 'global' NOT = 'not' WITH = 'with' AS = 'as' ELIF = 'elif' IF = 'if' OR = 'or' YIELD = 'yield' ASSERT = 'assert' ELSE = 'else' IMPORT = 'import' PASS = 'pass' BREAK = 'break' EXCEPT = 'except' IN = 'in' RAISE = 'raise' RESERVED = [ FALSE, CLASS, FINALLY, IS, RETURN, NONE, CONTINUE, FOR, LAMBDA, TRY, TRUE, DEF, FROM, NONLOCAL, WHILE, AND, DEL, GLOBAL, NOT, WITH, AS, ELIF, IF, OR, YIELD, ASSERT, ELSE, IMPORT, PASS, BREAK, EXCEPT, IN, RAISE, "+", "-", "/", "*", "+=", "-=", "/=", "*=", "**", "%", "!", "@", "^", "&", "(", ")", "=", "'", "\"", "|", ">", ">=", "<", "<=", ",", "!=", "==", ":", '.' ] PK!YAA*fastest/code_assets/naive_case_detector.pyimport re from fastest.constants import KEYS, PATTERNS FUNCTION_CALL = 0 OUTPUT = 1 def stack_imports(import_statements): """ ----- examples: @let output = ['from datetime import datetime\\n', 'import numpy as np\\n'] @end 1) stack_imports(['from datetime import datetime ', ' import numpy as np ']) -> output ----- :param import_statements: :return: """ return [ import_statement.strip() + '\n' for import_statement in import_statements if len(import_statement) > 0 ] def get_imports_from_docstring(example_passage): """ ---- examples: @let no_import_in_docstring = 'nothing to see here' docstring_with_imports = 'import numpy as np' @end 1) get_imports_from_docstring(no_import_in_docstring) -> [] 2) get_imports_from_docstring(docstring_with_imports) -> [] ---- :param example_passage: :return: """ needed_imports = re.findall(PATTERNS.NEED_IMPORT, example_passage, re.M) needed_imports = needed_imports if len(needed_imports) > 0 else None if needed_imports is None: return [] needed_imports = ''.join(needed_imports).replace(PATTERNS.IMPORT_DEC, '').split('\n') return stack_imports(needed_imports) def get_variables_from_docstring(example_passage): """ ---- examples: 1) get_variables_from_docstring("@let\\na = 1\\n@end") -> ['a = 1'] ---- :param example_passage: :return: """ needed_variables = re.findall(PATTERNS.NEEDED_VARIABLES, example_passage) if len(needed_variables) == 0: return '' needed_variables = needed_variables[0] needed_variables = needed_variables.replace('@let', '') return needed_variables.split('\n') def stack_examples(examples_strings): """ ----- examples: @let output = [{ 'from': 'stack_examples("1) some_fn() -> 1")', 'expect': 1 }] @end 1) stack_examples([]) -> [] 2) stack_examples("1) some_fn() -> 1") -> output ----- :param examples_strings: :return: """ example_stack = [] for example in examples_strings: test_function, expectation = re.sub(PATTERNS.NUMBER_BULLET, '', example, 1)\ .rsplit(PATTERNS.TEST_SEP, 1) example_stack.append({ KEYS.FROM: test_function, KEYS.EXPECT: expectation }) return example_stack def get_params_from_docstring(statements): params = re.findall(r':param .*:(.*)', statements, re.M) return [ param.replace(' ', '') for param in params ] def get_return_from_docstring(statements): return_statement = re.search(r':return: (.*)', statements, re.M) return return_statement.group(1) if return_statement is not None else None def get_test_case_examples(example_passage): """ ---- examples: @let example_passage = '1) some_fn() -> 1' output = [{ 'from': 'stack_examples("1) some_fn() -> 1")', 'expect': 1 }] @end 1) get_test_case_examples(example_passage) -> output ---- :param example_passage: :return: """ examples_strings = re.findall(PATTERNS.TEST_CASE_EXAMPLE, example_passage, re.M) examples_strings = examples_strings if len(examples_strings) > 0 else [] return stack_examples(examples_strings) def get_test_from_example_passage(statements): """ ---- examples: @let some_fn = lambda: 1 output = { 'imports': ['import numpy as np'] 'variables': ['a = 1'], 'examples': [{ 'from': 'some_fn()', 'expect': 1 }] } @end 1) get_test_from_example_passage('---\\n@needs\\nimport numpy as np\\n@end\\n\\n@let\\na = 1\\n1) some_fn() -> 1') -> output ---- :param statements: :return: """ if statements is None: return None example_passage = re.findall(PATTERNS.EXAMPLE_PASSAGE, statements, re.I) example_passage = example_passage[0] if len(example_passage) > 0 else None if example_passage is None: return None import_statements = get_imports_from_docstring(example_passage) variables = get_variables_from_docstring(example_passage) examples = get_test_case_examples(example_passage) params = get_params_from_docstring(statements) return_statement = get_return_from_docstring(statements) return None \ if examples is None \ else { KEYS.IMPORTS: import_statements, KEYS.VARIABLES: variables, KEYS.EXAMPLES: examples, KEYS.PARAMS: params, KEYS.RETURN: return_statement } PK!Wfastest/compiler/__init__.pyname = "compiler_pkg" PK!b>!fastest/compiler/compile_tests.pyimport os from fastest.bodies import f from fastest.constants import CONTENT, KEYS, SYS def add_imports_for_test_case(test, imports): if test[KEYS.IMPORTS] is None: return imports for import_statement in test[KEYS.IMPORTS]: imports.add(import_statement) return imports def create_test_class(imports, contents, deps_import, function_object, root_module_name): imports.add(CONTENT.IMPORT_UNITTEST) imports.add(CONTENT.DEPS_IMPORT_TEMPLATE.format(deps_import, function_object[KEYS.NAME])) contents.append(CONTENT.CLASS_CREATE_TEMPLATE.format(root_module_name, function_object[KEYS.NAME])) return imports, contents def create_test_case_content(function_object, imports, contents): for example in function_object[KEYS.TESTS][KEYS.EXAMPLES]: contents.append(f.create_naive_test_case(function_object, example)) imports = add_imports_for_test_case(function_object[KEYS.TESTS], imports) return imports, contents def create_test_case(function_objects, deps_import, root_module_name): imports = set() contents = [] for function_object in function_objects: if type(function_object) is not dict: continue if function_object[KEYS.TESTS] is None: continue imports, contents = create_test_class(imports, contents, deps_import, function_object, root_module_name) imports, contents = create_test_case_content(function_object, imports, contents) return imports, contents def write_tests_to_file(fp, imports, contents): return fp.write(''.join(sorted(list(imports)) + contents)) def build(function_objects, src_file_path, base_path): last_file = -1 test_file_name = 'test__' + src_file_path.split(SYS.SLASH)[last_file] deps_import = src_file_path.replace(base_path + SYS.SLASH, '').replace(SYS.SLASH, '.').replace('.py', '') root_module_name = deps_import.split('.')[-1] test_file_path = os.path.join(base_path, KEYS.TEST, test_file_name) with open(test_file_path, 'w+') as fp: imports, contents = create_test_case(function_objects, deps_import, root_module_name) write_tests_to_file(fp, imports, contents) PK!''fastest/constants.pyimport platform class SYS: __UNIX_SLASH = '/' __WINDOWS_SLASH = '\\' PY = '.py' SLASH = __WINDOWS_SLASH if platform.system == 'Windows' else __UNIX_SLASH TEST_FILE_ENDING = '__test.py' class KEYS: IMPORTS = 'imports' NAME = 'name' TESTS = 'tests' TEST = 'test' STR = 'str' EXAMPLES = 'examples' FROM = 'from' EXPECT = 'expect' VARIABLES = 'variables' PARAMS = 'params' RETURN = 'return' class CONTENT: CLASS_CREATE_TEMPLATE = '\nclass Test{}{}(unittest.TestCase):\n' IMPORT_UNITTEST = 'import unittest\n' DEPS_IMPORT_TEMPLATE = 'from {} import {}\n' TEST_CASE_TEMPLATE = ' def test__{function_name}__{case_id}(self):' TESTERS_NOTES_TEMPLATE = ' {testers_notes}' VARIABLES_TEMPLATE = ' {variables}\n' TYPE_ASSERT_TEMPLATE = '\n self.assertIsInstance({function}, {value})' ASSERTION_TEMPLATE = '\n self.assertEqual({function}, {value})\n' class PATTERNS: FUNCTION_CALL = r'example: [\s\S]+?(?=->)' IMPORT_DEC = '@need\n' VAR_DEC = r'@let ' NEED_IMPORT = r'@need[\s\S]+?(?=@end)' NEEDED_VARIABLES = r'@let[\s\S]+?(?=@end)' NUMBER_BULLET = r'\d\) ' TEST_CASE_EXAMPLE = r'\d\) [\s\S]+?(?=\n)' EXAMPLE_PASSAGE = r'-{3,}[\s\S]+?(?=---)' TEST_SEP = ' -> ' PK![nfastest/io/__init__.pyname = "io_pkg" PK!vfastest/io/read_file.pyimport os def get_current_directory(): return os.path.dirname(os.path.realpath(__file__)) def read_file(dir_path, file_path): goal_dir = os.path.join(dir_path, file_path) with open(os.path.abspath(goal_dir), 'r') as fp: contents = fp.read() return contents PK!_RSfastest/type/__init__.pyname = "type_pkg" PK!fastest/type/type_inference.pyfrom fastest.type import type_usage_patterns INT = 0 STR = 1 LIST = 2 TUPLE = 3 DICT = 4 type_map = ['int', 'str', 'list', 'tuple', 'dict'] def infer(variable, statements): """ example: infer("list_var", "def fn():\n\tlist_var = [1]") -> ['list'] # example: infer("some_var", "def fn():\n\tsome_var + some_other") -> ['int', 'str'] # :param variable: :param statements: :return: """ statements = statements.split('\n') statements = statements[1:] type_chances = [0] * 5 for statement in statements: type_chances[INT] += type_usage_patterns.used_as_int(statement, variable) type_chances[STR] += type_usage_patterns.used_as_str(statement, variable) type_chances[LIST] += type_usage_patterns.used_as_list(statement, variable) type_chances[TUPLE] += type_usage_patterns.used_as_tuple(statement, variable) type_chances[DICT] += type_usage_patterns.used_as_dict(statement, variable) max_prob_type = max(type_chances) if type_chances.count(max_prob_type) > 1: return [type_map[i] for i, type_chance in enumerate(type_chances) if type_chance == max_prob_type] else: return [type_map[type_chances.index(max_prob_type)]] PK!rwL#fastest/type/type_usage_patterns.pyimport re from fastest.utils import count_truthy def used_as_int(statement, variable): """ example: used_as_int("a = 4", "a") -> 1 # example: used_as_int("a + 4", "a") -> 1 # example: used_as_int("a * 4", "a") -> 1 # example: used_as_int("a - 4", "a") -> 1 # :param statement: :param variable: :return: """ statement = statement.strip() assignment = re.search(r'{variable}\s*=\s*\d+'.format(variable=variable), statement) addition = re.search(r'{variable}\s*\+\s*'.format(variable=variable), statement) addition_inc = re.search(r'{variable}\s*\+=\s*\d+'.format(variable=variable), statement) multiplication = re.search(r'{variable}\s*\*\s*'.format(variable=variable), statement) subtraction = re.search(r'{variable}\s*-\s*'.format(variable=variable), statement) division = re.search(r'{variable}\s*/\s*'.format(variable=variable), statement) return count_truthy([assignment, addition, subtraction,multiplication, division, addition_inc]) def used_as_str(statement, variable): """ example: used_as_str("string_var = 'something'", "string_var") -> 1 # example: used_as_str("string_var + 'something'", "string_var") -> 1 # example: used_as_str("string_var * 5", "string_var") -> 1 # :param statement: :param variable: :return: """ statement = statement.strip() assignment = re.match('{variable}\s*=\s*"|\'\w*"|\''.format(variable=variable), statement) addition = re.match(r'{variable}\s*\+\s*'.format(variable=variable), statement) multiplication = re.match(r'{variable}\s*\*\d*'.format(variable=variable), statement) return count_truthy([assignment, addition, multiplication]) def used_as_iterable(statement, variable): """ example: used_as_iterable("for word in words", "words") -> 1 # :param statement: :param variable: :return: """ statement = statement.strip() loop = re.match(r'for \w+ in {variable}'.format(variable=variable), statement) map_fn = re.search(r'map\(.*[^,)],\s*{variable}'.format(variable=variable), statement) filter_fn = re.search(r'filter\(.*[^,)],\s*{variable}'.format(variable=variable), statement) reduce_fn = re.search(r'reduce\(.*[^,)],\s*{variable}'.format(variable=variable), statement) item_index = re.match(r'{variable}\[\d+\]'.format(variable=variable), statement) return count_truthy([loop, map_fn, filter_fn, reduce_fn, item_index]) def used_as_list(statement, variable): """ example: used_as_list("apples.append(10)", "apples") -> 1 # example: used_as_list("apples = [11, 12]", "apples") -> 1 # :param statement: :param variable: :return: """ statement = statement.strip() assignment = re.match(r'{variable}\s*=\s*\['.format(variable=variable), statement) assignment_as_instance = re.match(r'{variable}\s*=\s*list\('.format(variable=variable), statement) append = re.search(r'{variable}.append\('.format(variable=variable), statement) return count_truthy([assignment_as_instance, assignment, append]) + used_as_iterable(statement, variable) def used_as_tuple(statement, variable): """ example: used_as_tuple("words = (11, 2)", "words") -> 1 # :param statement: :param variable: :return: """ statement = statement.strip() assignment = re.match(r'{variable}\s*=\s*\('.format(variable=variable), statement) assignment_as_instance = re.match(r'{variable}\s*=\s*tuple\('.format(variable=variable), statement) insert = re.match(r'{variable}.insert\('.format(variable=variable), statement) return count_truthy([assignment_as_instance, assignment, insert]) + used_as_iterable(statement, variable) def used_as_dict(statement, variable): """ example: used_as_dict("dict_input['val']", "dict_input") -> 1 # :param statement: :param variable: :return: """ statement = statement.strip() assignment = re.search(r'{variable}\s*=\s*\{{'.format(variable=variable), statement) key_ref_str = re.search(r'{variable}\[\"|\'\w+\"|\'\]'.format(variable=variable), statement) key_ref_var = re.search(r'{variable}\[\w+\]'.format(variable=variable), statement) get_access = re.search(r'{variable}.get\('.format(variable=variable), statement) return count_truthy([assignment, key_ref_str, key_ref_var, get_access]) PK!No'@fastest/utils.pydef count_truthy(items): counter = 0 for item in items: if item is not None: counter += 1 return counter PK!H<+1)fastest-0.0.12.dist-info/entry_points.txtN+I/N.,()JK,.I-.z񹉙yV PK!HڽTUfastest-0.0.12.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!H+ B!fastest-0.0.12.dist-info/METADATAW۸8{js32M&&h\(%$jIoQ}xƩJ.3G0U^Mo( m^ILdj ܢ#keEcfFTi~U_VYlΈl67E c U.c+Yd޴zSj_8>?G;LBY2۷eeEÍժFnԓ/a |/7P\%(m)*BQU+r!uǪz3+u2%$L&_˘nxhda[\طꎆ48weq;SqݟDE,hN=_“xT4,}]ml}ML&g N/f.Wv)3:@1"VS{#DcEPt֖B/ DC2ނ$9N(‰pxM҅ig4P&)8ׇY0k^nmې֛J'6l6,۟ʫ;Ȋ6aH1T %(qLf1+I^e2-mupj S#Mb6UXBcDׂ`)hT); &كv@7SBS`){O/L\7>>~ ۄ hݎgYֻЦ?֭[{U,&'1]ҟE.!t6 0vwNm,}OD qsuD`TA4{֣̎OzV~B8,e0" qFbng=3cy:J+Mbz͐jbQ(K 1A;qԃ\nx8A?ưBU*spG*^3q)9;K^t#oR֜z&]␞q}C*#1׍#+y08T `OVjd!TI 0w\~*lYk%Zס$j1F6LE&`Ybf׺)2 wz q׹Js44 KM{nc<Vx{O|U;lSgO178>\~WzOBmмۢxC FwpZlx7^Sڵ^qS# Un{Nm~?hW+n%٭sV Gx'_;ʺE@[{} ƵL;?:@ΣD̋( y(p&4/8ۉۨlgtɏn< `D891\gRS- H9r_ nϠeJնctl^/6lM? Q;P 'nVeae1ߩjY{[KOk~v;ܼe[UCJ\sZrt47vxˆRy/PK!H˛fastest-0.0.12.dist-info/RECORDuɒH}fDDTDMB2˘OVDw׻!2X|y2ϟ'Z[I > kcdؿ#ΠC0jMz+VA'SU+;w30b+^R:ն.r \˥#~.O)_+y=1/Q l7ΨOS]j^iV0O7 {|bD+#Bj,A[nOQ Qjm&=<AQܗ^B{ML|ygunrFEC͓kP[$ɼUîQR uh4O,NםqvLk-sOS,4M.X4 f-MxN A =5)Ź#En%=B@}T6-hIʉNvukTǛ™`4[<$ͻK&Q?*+Jb>Ka$;|]G hDͬ㚉 E('۱։M͑b$-E^W53a4U2Q3' Xwb: Sq~%l֤|w]YY"&G!0 q_eG=m8v5 @:B{@^ݠTMs̬ *.uv ;i%`$͡Ńԥa;]S= aKaOC_|l7<ꅬM6bx/A.vAPy}#IއS24MoHLɆ;W.Vw(ɗ4p]dw!z*k7t(gR*|  ?~ @U&1B#Ȓe;7NQĜ=ɖ=Rot;Y>|;gX徿72lu?6'sל& ?|6U֕3f|uC]ϱhc|%_PK!*Ilfastest/__init__.pyPK![v Bfastest/__main__.pyPK!Ifastest/bodies/__init__.pyPK!h(q::fastest/bodies/f.pyPK!XE}fastest/code_assets/__init__.pyPK!eUfastest/code_assets/function.pyPK!b]]fastest/code_assets/keywords.pyPK!YAA*%fastest/code_assets/naive_case_detector.pyPK!W.fastest/compiler/__init__.pyPK!b>!.fastest/compiler/compile_tests.pyPK!''7fastest/constants.pyPK![n=fastest/io/__init__.pyPK!v`=fastest/io/read_file.pyPK!_RS>fastest/type/__init__.pyPK!>fastest/type/type_inference.pyPK!rwL#0Dfastest/type/type_usage_patterns.pyPK!No'@kVfastest/utils.pyPK!H<+1)"Wfastest-0.0.12.dist-info/entry_points.txtPK!HڽTUWfastest-0.0.12.dist-info/WHEELPK!H+ B!$Xfastest-0.0.12.dist-info/METADATAPK!H˛dafastest-0.0.12.dist-info/RECORDPKe