Example #1
0
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
Example #2
0
    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
Example #3
0
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
Example #4
0
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
Example #5
0
 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
Example #6
0
 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
Example #7
0
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
Example #8
0
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
Example #9
0
    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
Example #10
0
    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, [])
Example #11
0
    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