def test_error_pos_is_correctly_passed_to_warning( self, src_pos: 'Optional[Pos]') -> None: for op in ['truediv', 'floordiv', 'mod']: TypeCheckLogger.clean_sing() getattr(pv.float(1.0), op)(pv.float(0.0), src_pos) assert len(TypeCheckLogger().warnings) == 1 assert TypeCheckLogger().warnings[-1][2] == src_pos
def test_fail_and_success_in_examples(self, filepath: str, capsys: Any) -> None: # Cleaning type errors TypeCheckLogger.clean_sing() assert len(TypeCheckLogger().warnings) == 0 # Getting expected values running Pytropos expected_output, expected_exitcode, expected_store = \ self._find_output_file_and_store(filepath) # Executing Pytropos file = open(filepath) exit_exitcode, exit_store = main.run_pytropos(file.read(), file.name) out, err = capsys.readouterr() # Checking validity of execution assert out == expected_output assert exit_exitcode == expected_exitcode if expected_exitcode != 2: assert exit_store is not None assert expected_store is not None assert exit_store._global_scope == expected_store else: assert exit_store is None assert expected_store is None # there shouldn't be any trash after the generated code is executed, # everything should be encapsuled # (Note: this is an assertion to prevent a bug to be reintroduced) assert len(TypeCheckLogger().warnings) == 0
def run_transformed_type_checking_code( newast_comp: 'CodeType', pt_globals: 'Optional[Dict[str, Any]]') -> 'Tuple[int, None[Store]]': if pt_globals is None: pt_globals = {} # from pytropos.internals.tools import NonImplementedPT try: # at the module level, locals and globals are the same # see: https://stackoverflow.com/questions/2904274/globals-and-locals-in-python-exec exec(newast_comp, pt_globals) except Exception: derror( "Error: An error inside pytropos has occurred, please open an issue in:" ) derror(" ", metadata.url) derror("Please run the code again with `-vvv` parameter") derror("\nType checking errors found:", verb=2) derror(TypeCheckLogger(), verb=2) derror("\nValue of variables at the moment of the failure:", metadata.url, verb=2) derror(pt_globals['st'], end='\n\n', verb=2) traceback.print_exc() return (2, None) store = pt_globals['st'] if debug_print.verbosity == 2: derror("\nLast computed variables values (Store):", verb=2) derror(store, end='\n\n', verb=2) if debug_print.verbosity == 3: derror("\nLast computed variables values (Store):", verb=3) derror("Store({", verb=3) for i, v in store.items(): derror(f" {i!r}: PythonValue({v.val}),", verb=3) derror("})\n", verb=3) if len(TypeCheckLogger().warnings) > 0: derror(TypeCheckLogger()) return (1, store) else: dprint('No type checking error found.', verb=1) dprint( 'I wasn\'t able to find any error in the code, though there may be some (sorry)', verb=2) return (0, store)
def test_shifts_with_some_values(self, i: PythonValue, j: Optional[int]) -> None: TypeCheckLogger.clean_sing() new_val1 = i.lshift(pv.int(j)) new_val2 = i.rshift(pv.int(j)) assert not i.is_top() assert isinstance(i.val, AbstractValue) assert isinstance(new_val1.val, AbstractValue) assert isinstance(new_val2.val, AbstractValue) if i.val.is_top() or j is None: assert new_val1.val.is_top() assert new_val2.val.is_top() assert len(TypeCheckLogger().warnings) == 0 return if new_val1.val.is_top(): with raises(ValueError): ops.lshift(i.val.val, j) # type: ignore assert len(TypeCheckLogger().warnings) > 0 assert valueError.match(TypeCheckLogger().warnings[0][1]) else: assert ops.lshift(i.val.val, j) == new_val1.val.val # type: ignore if new_val2.val.is_top(): with raises(ValueError): ops.rshift(i.val.val, j) # type: ignore assert len(TypeCheckLogger().warnings) > 0 assert valueError.match(TypeCheckLogger().warnings[-1][1]) else: assert ops.rshift(i.val.val, j) == new_val2.val.val # type: ignore
def test_float_and_ints_comform_to_baseline_python_divs( self, i: int, j: float) -> None: for op in ['truediv', 'floordiv', 'mod']: TypeCheckLogger.clean_sing() if j == 0.0: with raises(ZeroDivisionError): getattr(ops, op)(i, j) newval = getattr(pv.int(i), op)(pv.float(j)) assert isinstance(newval.val, (Int, Float)) assert newval.val.is_top() assert len(TypeCheckLogger().warnings) == 1 assert zeroDivisionError.match( TypeCheckLogger().warnings[0][1]) else: assert check_float_equality( getattr(ops, op)(i, j), getattr(pv.int(i), op)(pv.float(j)).val.val) assert len(TypeCheckLogger().warnings) == 0 if i == 0: with raises(ZeroDivisionError): getattr(ops, op)(j, i) newval = getattr(pv.float(j), op)(pv.int(i)) assert isinstance(newval.val, (Int, Float)) assert newval.val.is_top() assert len(TypeCheckLogger().warnings) > 0 assert zeroDivisionError.match( TypeCheckLogger().warnings[-1][1]) else: assert check_float_equality( getattr(ops, op)(j, i), getattr(pv.float(j), op)(pv.int(i)).val.val)
def test_op_float(self, i: float, j: float) -> None: """ This test basically checks that doing something like this: Float(3) + Float(5) == Float(8) for all operations (+*/...) and Values """ for op in ['add', 'sub', 'mul', 'truediv', 'floordiv', 'mod']: TypeCheckLogger.clean_sing() pt_val = getattr(pv.float(i), op)(pv.float(j)) assert isinstance(pt_val.val, Float) if pt_val.val.is_top(): with raises(ZeroDivisionError): getattr(ops, op)(i, j) assert len(TypeCheckLogger().warnings) == 1 assert zeroDivisionError.match( TypeCheckLogger().warnings[0][1]) else: assert check_float_equality( getattr(ops, op)(i, j), pt_val.val.val) # type: ignore assert len(TypeCheckLogger().warnings) == 0
def test_div_int(self, i: int, j: int) -> None: """ Checks correctness of division between integers. int(3).truediv(int(5)) == int(3/5) for division operations """ for op in ['truediv', 'floordiv', 'mod']: TypeCheckLogger.clean_sing() pt_val = getattr(pv.int(i), op)(pv.int(j)) if pt_val.is_top(): with raises(ZeroDivisionError): getattr(ops, op)(i, j) assert len(TypeCheckLogger().warnings) == 1 assert zeroDivisionError.match( TypeCheckLogger().warnings[0][1]) else: assert isinstance(pt_val.val, (Float, Int)) py_val = getattr(ops, op)(i, j) assert py_val == pt_val.val.val assert type(py_val) is type(pt_val.val.val) # noqa: E721 assert len(TypeCheckLogger().warnings) == 0
def test_bool_returns_always_a_Bool(self, val: PythonValue) -> None: TypeCheckLogger.clean_sing() bool_val = val.bool() assert isinstance(bool_val.val, Bool) assert len(TypeCheckLogger().warnings) == 0
def run_pytropos( # noqa: C901 file: str, filename: str, cursorline: 'Optional[int]' = None, console: bool = False, pt_globals: 'Optional[Dict[str, Any]]' = None ) -> 'Tuple[int, Optional[Store]]': dprint("Starting pytropos", verb=1) dprint( "Parsing and un-parsing a python file (it should preserve all type comments)", verb=2) if debug_print.verbosity > 1: try: from typed_astunparse import unparse except ModuleNotFoundError: print( "Sorry! You need to install `typed_astunparse` for bigger verbosity levels.\n" "Note: If you are using python 3.7 we recommend you to install \n" " `typed_astunparse` with `pip install -r git_requirements.txt` \n" " (you can find `git_requirements.txt` in {})\n".format( metadata.url), file=sys.stderr) exit(1) # Parsing file ast_: ast3.Module try: ast_ = ast3.parse(file, filename=filename) # type: ignore except SyntaxError as msg: derror( f"{msg.filename}:{msg.lineno}:{msg.offset-1}: {type(msg).__name__}: {msg.msg}" ) return (2, None) except (OverflowError, ValueError) as msg: derror(f"{filename}::: {type(msg).__name__}") return (2, None) if debug_print.verbosity > 1: dprint("Original file:", verb=2) dprint("AST dump of original file:", ast3.dump(ast_), verb=3) dprint(unparse(ast_), verb=2) # Converting AST (code) into Pytropos representation newast: ast3.Module try: newast = PytroposTransformer( # type: ignore filename, cursorline=cursorline, console=console).visit(ast_) except AstTransformerError: derror( "Sorry it seems Pytropos cannot run the file. Pytropos doesn't support " "some Python characteristic it uses right now. Sorry :(") traceback.print_exc() # derror(msg) return (2, None) if debug_print.verbosity > 1: dprint("Modified file:", verb=2) dprint("AST dump of modified file:", ast3.dump(newast), verb=3) dprint(unparse(newast), verb=2) newast_py = ast.fix_missing_locations(typed_ast3_to_ast(newast)) # TODO(helq): add these lines of code for optional debugging # import astpretty # astpretty.pprint(newast_py) try: newast_comp = compile(newast_py, '<generated type checking ast>', 'exec') # type: ignore except (ValueError, TypeError): derror( "PYTROPOS INTERNAL ERROR. Sorry, it seems something unexpected happened. " "Please report this issue: run pytropos again with the same input and " "the flag -vvv") traceback.print_exc() # derror(msg) return (2, None) exitvalues = run_transformed_type_checking_code(newast_comp, pt_globals) TypeCheckLogger.clean_sing() dprint("Closing pytropos", verb=1) return exitvalues