Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/13403.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Disable assertion for modules outside current working dir(cwd)
1 change: 1 addition & 0 deletions changelog/13492.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed outdated warning about ``faulthandler`` not working on Windows.
1 change: 1 addition & 0 deletions src/_pytest/assertion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class AssertionState:
def __init__(self, config: Config, mode) -> None:
self.mode = mode
self.trace = config.trace.root.get("assertion")
self.invocation_path = str(config.invocation_params.dir)
self.hook: rewrite.AssertionRewritingHook | None = None


Expand Down
4 changes: 3 additions & 1 deletion src/_pytest/assertion/rewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,9 @@ def _should_rewrite(self, name: str, fn: str, state: AssertionState) -> bool:
# rewritten if they match the naming convention for test files
fn_path = PurePath(fn)
for pat in self.fnpats:
if fnmatch_ex(pat, fn_path):
if fnmatch_ex(pat, fn_path) and fn_path.is_relative_to(
state.invocation_path
):
state.trace(f"matched test file {fn!r}")
return True

Expand Down
4 changes: 4 additions & 0 deletions src/_pytest/pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

from _pytest import timing
from _pytest._code import Source
from _pytest.assertion.rewrite import assertstate_key
from _pytest.capture import _get_multicapture
from _pytest.compat import NOTSET
from _pytest.compat import NotSetType
Expand Down Expand Up @@ -751,6 +752,9 @@ def chdir(self) -> None:
This is done automatically upon instantiation.
"""
self._monkeypatch.chdir(self.path)
self._monkeypatch.setattr(
self._request.config.stash[assertstate_key], "invocation_path", self.path
)

def _makefile(
self,
Expand Down
11 changes: 11 additions & 0 deletions testing/acceptance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,17 @@ def test_issue88_initial_file_multinodes(self, pytester: Pytester) -> None:
result = pytester.runpytest(p, "--collect-only")
result.stdout.fnmatch_lines(["*MyFile*test_issue88*", "*Module*test_issue88*"])

def test_rewrite(self, pytester: Pytester) -> None:
pytester.copy_example("rewrite")
result = pytester.runpytest("tests/test_main.py::test_func", "-v")
result.stdout.fnmatch_lines(["*Full diff*"])

result = pytester.runpytest("tests/test_main.py::test_plugin", "-v")
result.stdout.fnmatch_lines(["*Full diff*"])

result = pytester.runpytest("tests/test_main.py::test_lib", "-v")
result.stdout.no_fnmatch_line("*Full diff*")

def test_issue93_initialnode_importing_capturing(self, pytester: Pytester) -> None:
pytester.makeconftest(
"""
Expand Down
2 changes: 2 additions & 0 deletions testing/example_scripts/rewrite/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
python_files = *.py
13 changes: 13 additions & 0 deletions testing/example_scripts/rewrite/some_plugin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from __future__ import annotations

from typing import Callable

import pytest


@pytest.fixture
def special_asserter() -> Callable[[int, int], None]:
def special_assert(a: int, b: int) -> None:
assert {"plugin_a": a} == {"plugin_b": b}

return special_assert
Empty file.
9 changes: 9 additions & 0 deletions testing/example_scripts/rewrite/src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Stub file for testing"""

from __future__ import annotations


def func(x: int, y: int) -> int:
"""Stub function"""
assert (x) > 0
return 0 if x == y else 1 if x > y else -1
4 changes: 4 additions & 0 deletions testing/example_scripts/rewrite/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from __future__ import annotations


__package__ = ""
16 changes: 16 additions & 0 deletions testing/example_scripts/rewrite/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from __future__ import annotations

from _pytest.fixtures import fixture


pytest_plugins = ["pytester", "some_plugin"]


@fixture
def b():
return 1


@fixture
def a():
return 2
18 changes: 18 additions & 0 deletions testing/example_scripts/rewrite/tests/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from __future__ import annotations

from typing import Callable

from ..src.main import func
from ..venv.lib64.python3.site_packages.external_lib import external_lib


def test_plugin(a: int, b: int, special_asserter: Callable[[int, int], bool]) -> None:
special_asserter(a, b)


def test_func(a: int, b: int) -> None:
assert {"func_a": func(a, b)} == {"func_a": 0}


def test_lib(a: int) -> None:
external_lib.some_check(a)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def some_check(lib_a: int) -> None:
assert {'lib_a': lib_a} != {'lib_a': lib_a}
59 changes: 59 additions & 0 deletions testing/test_assertrewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@
from _pytest.assertion.rewrite import _get_maxsize_for_saferepr
from _pytest.assertion.rewrite import _saferepr
from _pytest.assertion.rewrite import AssertionRewritingHook
from _pytest.assertion.rewrite import assertstate_key
from _pytest.assertion.rewrite import get_cache_dir
from _pytest.assertion.rewrite import PYC_TAIL
from _pytest.assertion.rewrite import PYTEST_TAG
from _pytest.assertion.rewrite import rewrite_asserts
from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pathlib import make_numbered_dir
from _pytest.pytester import Pytester
import pytest
Expand Down Expand Up @@ -370,6 +372,7 @@ def test_rewrites_plugin_as_a_package(self, pytester: Pytester) -> None:
pytester.makeconftest('pytest_plugins = ["plugin"]')
pytester.makepyfile("def test(special_asserter): special_asserter(1, 2)\n")
result = pytester.runpytest()

result.stdout.fnmatch_lines(["*assert 1 == 2*"])

def test_honors_pep_235(self, pytester: Pytester, monkeypatch) -> None:
Expand Down Expand Up @@ -1294,6 +1297,36 @@ def test_meta_path():
)
assert pytester.runpytest().ret == 0

def test_invocation_dir(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
"""Test get invocation param from AssertionState"""
from _pytest.assertion import AssertionState

config = pytester.parseconfig()
state = AssertionState(config, "rewrite")

assert state.invocation_path == str(config.invocation_params.dir)

new_rootpath = pytester.path / "test"
if not os.path.exists(new_rootpath):
os.mkdir(new_rootpath)
monkeypatch.setattr(
config,
"invocation_params",
Config.InvocationParams(
args=(),
plugins=(),
dir=new_rootpath,
),
)
state = AssertionState(config, "rewrite")
assert state.invocation_path == str(new_rootpath)

@pytest.mark.skipif(
sys.platform.startswith("win32"), reason="cannot remove cwd on Windows"
)
@pytest.mark.skipif(
sys.platform.startswith("sunos5"), reason="cannot remove cwd on Solaris"
)
def test_write_pyc(self, pytester: Pytester, tmp_path) -> None:
from _pytest.assertion import AssertionState
from _pytest.assertion.rewrite import _write_pyc
Expand Down Expand Up @@ -1971,6 +2004,32 @@ def test_simple_failure():
assert hook.find_spec("file") is not None
assert self.find_spec_calls == ["file"]

def test_assert_rewrites_only_invocation_path(
self, pytester: Pytester, hook: AssertionRewritingHook, monkeypatch
) -> None:
"""Do not rewrite assertions in tests outside `AssertState.rootpath` (#13403)."""
pytester.makepyfile(
**{
"file.py": """\
def test_simple_failure():
assert 1 + 1 == 3
"""
}
)

with mock.patch.object(hook, "fnpats", ["*.py"]):
assert hook.find_spec("file") is not None

invocation_path = f"{os.getcwd()}/tests"
monkeypatch.setattr(
pytester._request.config.stash[assertstate_key],
"invocation_path",
invocation_path,
)

with mock.patch.object(hook, "fnpats", ["*.py"]):
assert hook.find_spec("file") is None

@pytest.mark.skipif(
sys.platform.startswith("win32"), reason="cannot remove cwd on Windows"
)
Expand Down
Loading