PK!*Ilfastest/__init__.pyname = "fastest" PK!D" fastest/__main__.pyimport argparse import os 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 parser = argparse.ArgumentParser(description='Create test cases automatically') parser.add_argument('--path', required=True, help='Project root, use $(pwd) to be sure') parser.add_argument('--source',required=True, help='Modules to check coverage for') parser.add_argument('--watch', help='Comma separated names of folders that should be watched') parser.add_argument('--exclude', help='Comma separated names of folders that should NOT be watched') args = parser.parse_args() def main(): if not os.path.exists('./test'): os.mkdir('./test') test_files = [ test_file.replace('.py', '') for test_file in os.listdir('./test') ] report_path = os.path.abspath(os.path.join(args.path, 'htmlcov/index.html')) class PyFileHandler(PatternMatchingEventHandler): patterns = ["*.py"] def process(self, event): """ event.event_type 'modified' | 'created' | 'moved' | 'deleted' event.is_directory True | False event.src_path path/to/observed/file """ print(event.src_path, event.event_type) if '__test.py' not in event.src_path and \ os.path.isfile(event.src_path) and \ 'core' not in event.src_path: page = read_file(args.path, event.src_path) functions = get_functions(page) compile_tests.build(functions, event.src_path, args.path) command = ['test.{}'.format(test_file) for test_file in test_files] subprocess.call(['coverage', 'run', '--source', args.source, '-m', 'unittest'] + command) subprocess.call(['coverage', 'report']) subprocess.call(['coverage', 'html']) print(report_path) def on_modified(self, event): self.process(event) def on_created(self, event): self.process(event) observer = Observer() observer.schedule(PyFileHandler(), args.path, recursive=True) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join() PK!fastest/bodies/__init__.pyname = "bodies_pkg"PK!qxfastest/bodies/f.pyimport uuid from fastest.type import type_inference from fastest import code_style from fastest.bodies.testers_notes import get_testers_notes def case_generator(): return str(uuid.uuid4()).upper().replace("-", "")[0:10] def create_var_type_test_case(function_obj, variable_name, statements): function_name = function_obj['name'] args = ', '.join(function_obj['args']) expected_types = type_inference.infer(variable_name, statements) if len(expected_types) == 1: expected_type = expected_types[0] type_message = 'Automatically detected type of {variable} to be an {type}'\ .format(variable=variable_name, type=expected_type) return """ def test__{function}__for_typeof__arg__{variable}(self): # You must always check type of arguments # to prevent unexpected crashes # # Feel free to remove this test if you're confident that this is not required # {message} self.assert({function}({args}), {type}) """.format(function=function_name, args=args, variable=variable_name, message=type_message, type=expected_type) else: expected_type = '' return """ def test__{function}__for_typeof__arg__{variable}(self): # You must always check type of arguments # to prevent unexpected crashes # # Feel free to remove this test if you're confident that this is not required self.assert({function}({args}), {type}) """.format(function=function_name, args=args, variable=variable_name, type=expected_type) def create_return_type_test_case(function_obj, return_values, statements): function_name = function_obj['name'] args = ', '.join(function_obj['args']) for return_value in return_values: expected_types = type_inference.infer(return_value, statements) multi_type_return = len(list(set(expected_types))) > 1 if multi_type_return: type_message = 'Seems like the return value {} is one of {}. ' \ 'Be very careful with such functions, ' \ 'a better approach would be to have different ' \ 'functions to perform the different kind of tasks'.format(return_value, expected_types) expected_type = '' return """ def test__{function}__for_typeof_return__{variable}(self): # You must always have checkpoints for return values # # Feel free to remove this test if you're confident that this is not required # {message} self.assertIs({function}({args}), {type}) """.format(function=function_name, args=args, variable=return_value, message=type_message, type=expected_type) else: expected_type = expected_types[0] return """ def test__{function}__for_typeof_return__{variable}(self): # You must always have checkpoints for return values # # # Feel free to remove this test if you're confident that this is not required self.assertIs({function}({args}), {type}) """.format(function=function_name, args=args, variable=return_value, type=expected_type) def create_naive_test_case(function_object, test): function_too_long = code_style.is_function_too_long(function_object['str']) has_too_many_conditions = code_style.has_too_many_if_statements(function_object['str']) control_structure_overuse = code_style.get_loop_complexity(function_object['str']) testers_notes = get_testers_notes(function_too_long, has_too_many_conditions, control_structure_overuse) return """ def test__{function_name}__{case_id}(self): {testers_notes} self.assertEqual({function}, {value}) """.format( function_name=function_object['name'], case_id=case_generator(), function=test['from'], value=test['expect'], testers_notes=testers_notes ) PK!=JJfastest/bodies/testers_notes.pydef on_depth(depth): return """ # testers notes: # -------------- # Your function has {depth} lines of code, it is strongly advised to break them # when they are doing too many operations. """.format(depth=depth) def on_conditional_complexity(condition_complexity): return """ # testers notes: # -------------- # Your function has {condition_complexity} conditions, it is strongly advised to convert them # to individual functions. It is likely that they are doing different tasks. """.format(condition_complexity=condition_complexity) def on_control_overuse(control_overuse): return """ # testers notes: # -------------- # Your function has {control_overuse} number of nested loops!! # please try to find a better way, there has to be one. """.format(control_overuse=control_overuse) def get_testers_notes(depth, condition_complexity, control_overuse): function_too_deep, lines = depth complex_conditions, complexity = condition_complexity control_overuse, nested_loops = control_overuse notes = '' if function_too_deep: notes += on_depth(lines) + '\n\n' if complex_conditions: notes += on_conditional_complexity(complexity) + '\n\n' if control_overuse: notes += on_control_overuse(nested_loops) + '\n\n' return notes PK!"fastest/case_labyrinth/__init__.pyname = "case_labyrinth_pkg"PK!U4fastest/case_labyrinth/type_check_detect/__init__.pyname = "type_check_detect_pkg" PK!P$6fastest/case_labyrinth/type_check_detect/type_check.pydef detect_type_check(statements, argument): """ example: detect_type_check(["if type(var) is str"], "var") -> True # example: detect_type_check(["if True"], "var") -> False # :param statements: :param argument: :return: """ for statement in statements: # TODO: Does not scale need patterns for string if 'if type({argument})'.format(argument=argument) in statement: return True return False PK!XE}fastest/code_assets/__init__.pyname = "code_assets_pkg" PK!X& fastest/code_assets/arguments.pydef get_args(args_str): """ example: get_args(' (arg1, arg2) ') -> ['arg1', 'arg2'] # example: get_args('(arg1, arg2)') -> ['arg1', 'arg2'] # :param args_str: :return: """ args_str = args_str.strip() args_str = args_str.replace('(', '') args_str = args_str.replace(')', '') args = args_str.split(',') args = [arg.strip() for arg in args] return args PK!i:oofastest/code_assets/function.pyimport re from fastest.code_assets.arguments import get_args from fastest.code_assets.variables import get_variables from fastest.code_assets.return_value import get_return_values from fastest.code_assets.naive_case_detector import get_naive_case def get_functions(page): """ example: get_functions("def fn(arg1, arg2):\narg1 += 1\n\treturn arg1 + arg2") -> [{ 'name': 'fn', 'args': ['arg1', 'arg2'], 'str': 'def fn(arg1, arg2):\n\treturn arg1 + arg2', 'vars': [], 'tests': [] }] # :param page: :return: """ statements = re.split(r'\n', page) function_map = [] function_finder = None function_running = False function_object = {} function_body_start = 0 for statement_number, _ in enumerate(statements): if function_running is False: function_finder = re.match(r'def ((\w[A-Za-z_]*[^\(])(\(.*[^:]\)))', statements[statement_number]) function_body_start = statement_number if function_finder is not None: function_running = True function_object['name'] = function_finder.group(2) function_object['args'] = get_args(function_finder.group(3)) function_finder = None if function_body_start != statement_number and re.match(r'^\s+', statements[statement_number]) is None: function_object['str'] = '\n'.join(statements[function_body_start: statement_number]) function_object['returns'] = get_return_values(function_object['str']) function_object['vars'] = get_variables(function_object['str'], function_object['args']) function_object['tests'] = get_naive_case(function_object['str']) function_map.append(function_object) function_object = {} function_running = False function_finder = None return function_map PK!}77fastest/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!x*fastest/code_assets/naive_case_detector.pyimport re FUNCTION_CALL = 0 OUTPUT = 1 def get_naive_case(statements): """ example: get_naive_case("example: fn_do_work() -> 8 #") -> [{ "from": "fn_do_work()", "expect": 8 }] # :param statements: :return: """ function_call_pattern = r'example: [\s\S]+?(?=->)' total_pattern = r'example: [\s\S]+?(?=#)' function_call_matches = re.findall(function_call_pattern, statements, re.M) total_matches = re.findall(total_pattern, statements, re.M) test_cases = [] for func_match, total_match in zip(function_call_matches, total_matches): func_match = func_match.replace('example:', '').strip() expectation = total_match\ .replace(func_match, '')\ .replace('example:', '')\ .replace('->', '')\ .strip() test_cases.append({ 'from': func_match, 'expect': expectation }) return test_casesPK!TQ__#fastest/code_assets/return_value.pyimport re def get_return_values(statements): """ example: get_return_values('return 5') -> ['5'] # example: get_return_values('') -> [None] # :param statements: :return: """ return_values = re.findall(r'return (.*)', statements) if len(return_values) > 0: return return_values else: return [None] PK!~~ fastest/code_assets/variables.pyimport re from fastest.code_assets import keywords def get_variables(statements, arguments): """ example: get_variables("def fn(arg1, arg2):\n\tc = 4\n\treturn arg1 + arg2", ["arg1", "arg2"]) -> ["c"] # :param statements: :param arguments: :return: """ statements = statements.split('\n') statements = statements[1:] statements = ' '.join(statements) statements = re.sub(r'\s+', ' ', statements).replace(':', '') words = statements.split(' ') exclude_options = keywords.RESERVED + arguments return list(set([word for word in words if word not in exclude_options and word.isalpha()])) PK!c__fastest/code_style/__init__.pyfrom fastest.code_style.control_overuse import * from fastest.code_style.depth_metric import * PK!,occ%fastest/code_style/control_overuse.pyimport re CURRENT_CONDITIONAL_THRESHOLD = 5 CURRENT_CONTROL_COMPLEXITY_THRESHOLD = 3 def has_too_many_if_statements(function_body): """ example: has_too_many_if_statements("if \nif \nif \nif \nif \nif \nif ") -> (True, 7) # example: has_too_many_if_statements("if if if") -> (False, 3) # :param function_body: :return: """ matches = re.findall(r'if|elif|else', function_body) return len(matches) >= CURRENT_CONDITIONAL_THRESHOLD, len(matches) def get_indent_of_loop(statement): """ example: get_indent_of_loop(" for i in range(10):") -> 3 # :param statement: :return: """ return len(re.findall(r'\s+?(?=for)', statement)[0]) def get_statements_from_function_body(function_body): """ example: get_statements_from_function_body("a\n b") -> ['a', ' b'] # :param function_body: :return: """ return [ statement for statement in function_body.split('\n') if len(statement.strip()) > 0 ] def count_loop_indents(function_body): """ example: count_loop_indents(" for i in range(1):") -> [3] # :param function_body: :return: """ statements = get_statements_from_function_body(function_body) loops = [] for statement in statements: if re.match(r'for ', statement.strip()): loops.append(get_indent_of_loop(statement)) return loops def get_loop_complexity(function_body): """ example: get_loop_complexity(" for i in range(1):\n for i in range(1):") -> (False, 1) # :param function_body: :return: """ loop_indents = count_loop_indents(function_body) complexity = 0 for i, _ in enumerate(loop_indents): if len(loop_indents) > i + 1 and loop_indents[i] < loop_indents[i + 1]: complexity += 1 return CURRENT_CONTROL_COMPLEXITY_THRESHOLD < complexity, complexity PK!`"fastest/code_style/depth_metric.pyCURRENT_DEPTH_THRESHOLD = 20 def is_function_too_long(function_body): """ example: is_function_too_long("\n\n\n\n\n") -> (False, 6)# example: is_function_too_long("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") -> (True, 31) # :param function_body: :return: """ naive_code_depth = len(function_body.split('\n')) return naive_code_depth >= CURRENT_DEPTH_THRESHOLD, naive_code_depth PK!Wfastest/compiler/__init__.pyname = "compiler_pkg" PK!!fastest/compiler/compile_tests.pyimport os from fastest.bodies import f def build(function_objects, src_file_path, base_path): test_file_name = src_file_path.split('/')[-1].replace('.py', '__test.py') deps_import = src_file_path.replace(base_path + '/', '').replace('/', '.').replace('.py', '') root_module_name = deps_import.split('.')[-1] test_file_path = os.path.join(base_path, 'test', test_file_name) with open(test_file_path, 'w+') as fp: fp.write('import unittest\n\n') for function_object in function_objects: fp.write('from {} import {}\n'.format(deps_import, function_object['name'])) for function_object in function_objects: fp.write("""\n class Test_{}_{}(unittest.TestCase):""".format(root_module_name, function_object['name'])) for test in function_object['tests']: fp.write(f.create_naive_test_case(function_object, test)) PK![nfastest/io/__init__.pyname = "io_pkg" PK!ً9fastest/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.9.dist-info/entry_points.txtN+I/N.,()JK,.I-.z񹉙yV PK!HڽTUfastest-0.0.9.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!HHu% fastest-0.0.9.dist-info/METADATAWr}Wt,'K:"HYeɱd{J C`L`gO~ܯ xv"1ӗӧK/ gi2zFӣ-d5I'M4®g^K9Z*AɝJ.1V&/M#G(uTinJW,uWV*&T 7lg$nlu\_:Ok@('çt9Pz4яo^f) ~G~i4)GK[xxv+g> SyN'O'r>L2wgt![7v}xK[z0itfڏޮ[dk? Ir@GάuZd kעikl(M;H3S|M/DQ 6VO 1c!'G|*|Ob׆ͥ3j# 5'V.ky3ڢ `'!p[ 7Wi/?F s>ƿtV~=r9@N[]>4,bN/|vzKXU%B{U+KiJ>u"uReƖߓP}ѭ:Z0o;)SW)YAqnx9JoN@dCɈMԷes:S$ɲl&wU-:,o'v;~6vM#_bڞӠfUI+)c ?d񇡏!hLNqa,nq:ǣi RsD4uhۘ0ބ5KƳ>Qz c nD85EsMVaJxИ&im4zw[IGl$tY%+gcIP||󾹫?F:o'$d/ CM:ޔC*-:p@PػJ3FP`ZKksqP:9H ?bp}H,a;wޟY?>)?GtpCJYMWZ $J6"~tˌ~ySo{0K̃(C SF_='0 ?[)YFg_M(ytiALV@Mƀ $.vk= ,!(V+}g7ס,P1/͊?^A ̧l`|^pSUΓu湴׼Q1` qӆhPUgtM1mugj1YPcrQỳݍ?|e0<ެ`dA bp4Y+|)E!kUĠ>Z[1]?a:NU KQ ojL~ǝ8a+35 jrA%85bM\CC+cLC^77;]B!+=Q31"q,J Px} /mu#c7byJZ~?tg;aV7 JmaqV^I8=b;6օKx|g1|㸞aT idZR 4)d@ƉYuAJ'#7 bmh>yz6χSF&J_r\'?rS8eo g=ݢ.˞^;bL54j%<]S{8#PK!*Ilfastest/__init__.pyPK!D" Bfastest/__main__.pyPK!Z fastest/bodies/__init__.pyPK!qx fastest/bodies/f.pyPK!=JJ{fastest/bodies/testers_notes.pyPK!" fastest/case_labyrinth/__init__.pyPK!U4] fastest/case_labyrinth/type_check_detect/__init__.pyPK!P$6 fastest/case_labyrinth/type_check_detect/type_check.pyPK!XE}"fastest/code_assets/__init__.pyPK!X& @#fastest/code_assets/arguments.pyPK!i:oo %fastest/code_assets/function.pyPK!}77,fastest/code_assets/keywords.pyPK!x*,2fastest/code_assets/naive_case_detector.pyPK!TQ__#,6fastest/code_assets/return_value.pyPK!~~ 7fastest/code_assets/variables.pyPK!c__:fastest/code_style/__init__.pyPK!,occ%#;fastest/code_style/control_overuse.pyPK!`"Bfastest/code_style/depth_metric.pyPK!WDfastest/compiler/__init__.pyPK!!Efastest/compiler/compile_tests.pyPK![nHfastest/io/__init__.pyPK!ً9Ifastest/io/read_file.pyPK!_RShJfastest/type/__init__.pyPK!Jfastest/type/type_inference.pyPK!rwL#Ofastest/type/type_usage_patterns.pyPK!No'@ bfastest/utils.pyPK!H<+1(bfastest-0.0.9.dist-info/entry_points.txtPK!HڽTUHcfastest-0.0.9.dist-info/WHEELPK!HHu% cfastest-0.0.9.dist-info/METADATAPK!H0#9 :kfastest-0.0.9.dist-info/RECORDPK q