def _read_pyc(source, pyc, trace=lambda x: None): """Possibly read a pytest pyc containing rewritten code. Return rewritten code if successful or None if not. """ try: fp = open(fspath(pyc), "rb") except IOError: return None with fp: try: stat_result = os.stat(fspath(source)) mtime = int(stat_result.st_mtime) size = stat_result.st_size data = fp.read(12) except EnvironmentError as e: trace("_read_pyc({}): EnvironmentError {}".format(source, e)) return None # Check for invalid or out of date pyc file. if ( len(data) != 12 or data[:4] != importlib.util.MAGIC_NUMBER or struct.unpack("<LL", data[4:]) != (mtime & 0xFFFFFFFF, size & 0xFFFFFFFF) ): trace("_read_pyc(%s): invalid or out of date pyc" % source) return None try: co = marshal.load(fp) except Exception as e: trace("_read_pyc({}): marshal.load error {}".format(source, e)) return None if not isinstance(co, types.CodeType): trace("_read_pyc(%s): not a code object" % source) return None return co
def _write_pyc( state: "AssertionState", co: types.CodeType, source_stat: os.stat_result, pyc: Path, ) -> bool: proc_pyc = "{}.{}".format(pyc, os.getpid()) try: fp = open(proc_pyc, "wb") except OSError as e: state.trace("error writing pyc file at {}: errno={}".format( proc_pyc, e.errno)) return False try: _write_pyc_fp(fp, source_stat, co) os.rename(proc_pyc, fspath(pyc)) except OSError as e: state.trace("error writing pyc file at {}: {}".format(pyc, e)) # we ignore any failure to write the cache file # there are many reasons, permission-denied, pycache dir being a # file etc. return False finally: fp.close() return True
def _rewrite_test(fn: Path, config: Config) -> Tuple[os.stat_result, types.CodeType]: """read and rewrite *fn* and return the code object.""" fn_ = fspath(fn) stat = os.stat(fn_) with open(fn_, "rb") as f: source = f.read() tree = ast.parse(source, filename=fn_) rewrite_asserts(tree, source, fn_, config) co = compile(tree, fn_, "exec", dont_inherit=True) return stat, co
def _rewrite_test(fn, config): """read and rewrite *fn* and return the code object.""" fn = fspath(fn) stat = os.stat(fn) with open(fn, "rb") as f: source = f.read() tree = ast.parse(source, filename=fn) rewrite_asserts(tree, source, fn, config) co = compile(tree, fn, "exec", dont_inherit=True) return stat, co
def _write_pyc(state, co, source_stat, pyc): try: with atomic_write(fspath(pyc), mode="wb", overwrite=True) as fp: _write_pyc_fp(fp, source_stat, co) except EnvironmentError as e: state.trace("error writing pyc file at {}: errno={}".format(pyc, e.errno)) # we ignore any failure to write the cache file # there are many reasons, permission-denied, pycache dir being a # file etc. return False return True
def _write_pyc( state: "AssertionState", co: types.CodeType, source_stat: os.stat_result, pyc: Path, ) -> bool: try: with atomic_write(fspath(pyc), mode="wb", overwrite=True) as fp: _write_pyc_fp(fp, source_stat, co) except OSError as e: state.trace("error writing pyc file at {}: {}".format(pyc, e)) # we ignore any failure to write the cache file # there are many reasons, permission-denied, pycache dir being a # file etc. return False return True
def try_makedirs(cache_dir) -> bool: """Attempts to create the given directory and sub-directories exist, returns True if successful or it already exists""" try: os.makedirs(fspath(cache_dir), exist_ok=True) except (FileNotFoundError, NotADirectoryError, FileExistsError): # One of the path components was not a directory: # - we're in a zip file # - it is a file return False except PermissionError: return False except OSError as e: # as of now, EROFS doesn't have an equivalent OSError-subclass if e.errno == errno.EROFS: return False raise return True
def _write_pyc(state, co, source_stat, pyc): # Technically, we don't have to have the same pyc format as # (C)Python, since these "pycs" should never be seen by builtin # import. However, there's little reason deviate. try: with atomicwrites.atomic_write(fspath(pyc), mode="wb", overwrite=True) as fp: fp.write(importlib.util.MAGIC_NUMBER) # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) mtime = int(source_stat.st_mtime) & 0xFFFFFFFF size = source_stat.st_size & 0xFFFFFFFF # "<LL" stands for 2 unsigned longs, little-ending fp.write(struct.pack("<LL", mtime, size)) fp.write(marshal.dumps(co)) except EnvironmentError as e: state.trace("error writing pyc file at {}: errno={}".format(pyc, e.errno)) # we ignore any failure to write the cache file # there are many reasons, permission-denied, pycache dir being a # file etc. return False return True
def _write_pyc(state, co, source_stat, pyc): proc_pyc = "{}.{}".format(pyc, os.getpid()) try: fp = open(proc_pyc, "wb") except EnvironmentError as e: state.trace("error writing pyc file at {}: errno={}".format( proc_pyc, e.errno)) return False try: _write_pyc_fp(fp, source_stat, co) os.rename(proc_pyc, fspath(pyc)) except EnvironmentError as e: state.trace("error writing pyc file at {}: {}".format(pyc, e)) # we ignore any failure to write the cache file # there are many reasons, permission-denied, pycache dir being a # file etc. return False finally: fp.close() return True
def warn_about_none_ast(self, node, module_path, lineno): """ Returns an AST issuing a warning if the value of node is `None`. This is used to warn the user when asserting a function that asserts internally already. See issue #3191 for more details. """ val_is_none = ast.Compare(node, [ast.Is()], [ast.NameConstant(None)]) send_warning = ast.parse( """\ from _pytest.warning_types import PytestAssertRewriteWarning from warnings import warn_explicit warn_explicit( PytestAssertRewriteWarning('asserting the value None, please use "assert is None"'), category=None, filename={filename!r}, lineno={lineno}, ) """.format( filename=fspath(module_path), lineno=lineno ) ).body return ast.If(val_is_none, send_warning, [])
def visit_Assert(self, assert_: ast.Assert) -> List[ast.stmt]: """Return the AST statements to replace the ast.Assert instance. This rewrites the test of an assertion to provide intermediate values and replace it with an if statement which raises an assertion error with a detailed explanation in case the expression is false. """ if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1: from _pytest.warning_types import PytestAssertRewriteWarning import warnings # TODO: This assert should not be needed. assert self.module_path is not None warnings.warn_explicit( PytestAssertRewriteWarning( "assertion is always true, perhaps remove parentheses?"), category=None, filename=fspath(self.module_path), lineno=assert_.lineno, ) self.statements = [] # type: List[ast.stmt] self.variables = [] # type: List[str] self.variable_counter = itertools.count() if self.enable_assertion_pass_hook: self.format_variables = [] # type: List[str] self.stack = [] # type: List[Dict[str, ast.expr]] self.expl_stmts = [] # type: List[ast.stmt] self.push_format_context() # Rewrite assert into a bunch of statements. top_condition, explanation = self.visit(assert_.test) negation = ast.UnaryOp(ast.Not(), top_condition) if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook msg = self.pop_format_context(ast.Str(explanation)) # Failed if assert_.msg: assertmsg = self.helper("_format_assertmsg", assert_.msg) gluestr = "\n>assert " else: assertmsg = ast.Str("") gluestr = "assert " err_explanation = ast.BinOp(ast.Str(gluestr), ast.Add(), msg) err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation) err_name = ast.Name("AssertionError", ast.Load()) fmt = self.helper("_format_explanation", err_msg) exc = ast.Call(err_name, [fmt], []) raise_ = ast.Raise(exc, None) statements_fail = [] statements_fail.extend(self.expl_stmts) statements_fail.append(raise_) # Passed fmt_pass = self.helper("_format_explanation", msg) orig = self._assert_expr_to_lineno()[assert_.lineno] hook_call_pass = ast.Expr( self.helper( "_call_assertion_pass", ast.Num(assert_.lineno), ast.Str(orig), fmt_pass, )) # If any hooks implement assert_pass hook hook_impl_test = ast.If( self.helper("_check_if_assertion_pass_impl"), self.expl_stmts + [hook_call_pass], [], ) statements_pass = [hook_impl_test] # Test for assertion condition main_test = ast.If(negation, statements_fail, statements_pass) self.statements.append(main_test) if self.format_variables: variables = [ ast.Name(name, ast.Store()) for name in self.format_variables ] clear_format = ast.Assign(variables, ast.NameConstant(None)) self.statements.append(clear_format) else: # Original assertion rewriting # Create failure message. body = self.expl_stmts self.statements.append(ast.If(negation, body, [])) if assert_.msg: assertmsg = self.helper("_format_assertmsg", assert_.msg) explanation = "\n>assert " + explanation else: assertmsg = ast.Str("") explanation = "assert " + explanation template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation)) msg = self.pop_format_context(template) fmt = self.helper("_format_explanation", msg) err_name = ast.Name("AssertionError", ast.Load()) exc = ast.Call(err_name, [fmt], []) raise_ = ast.Raise(exc, None) body.append(raise_) # Clear temporary variables by setting them to None. if self.variables: variables = [ ast.Name(name, ast.Store()) for name in self.variables ] clear = ast.Assign(variables, ast.NameConstant(None)) self.statements.append(clear) # Fix line numbers. for stmt in self.statements: set_location(stmt, assert_.lineno, assert_.col_offset) return self.statements