Skip to content

Disable assertion rewriting external modules #13421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
44fa471
13403: Disable assertion rewriting for external modules
Tusenka May 11, 2025
b4abb80
13403: Disable assertion rewriting for external modules
Tusenka May 11, 2025
f47a2c9
13403: Disable assertion rewriting for external modules
Tusenka May 12, 2025
9d3afc2
Merge branch 'pytest-dev:main' into disable_assertion_rewriting_exter…
Tusenka May 12, 2025
b3d8825
13403: Disable assertion rewriting for external modules
Tusenka May 11, 2025
66a68e3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 13, 2025
094e892
Merge remote-tracking branch 'origin/disable_assertion_rewriting_exte…
Tusenka May 13, 2025
c6f2074
13403: Disable assertion rewriting for external modules
Tusenka May 11, 2025
3b34bfe
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 13, 2025
0cd03d6
13403: Disable assertion rewriting for external modules - move root p…
Tusenka May 13, 2025
5d5ad4e
Merge remote-tracking branch 'origin/disable_assertion_rewriting_exte…
Tusenka May 13, 2025
54f46d7
13403: Disable assertion rewriting for external modules - refactor
Tusenka May 15, 2025
1592f85
13403: Disable assertion rewriting for external modules - refactor
Tusenka May 16, 2025
ba67fe3
13403: Disable assertion rewriting for external modules - refactor
Tusenka May 16, 2025
232ba33
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 16, 2025
ad5317b
13403: Disable assertion rewriting for external modules - add tests
Tusenka May 18, 2025
5efcb78
Merge remote-tracking branch 'origin/disable_assertion_rewriting_exte…
Tusenka May 18, 2025
ba4263d
13403: Disable assertion rewriting for external modules - add test fo…
Tusenka May 19, 2025
db7dcd4
13403: Disable assertion rewriting for external modules - add test fo…
Tusenka May 22, 2025
651f7e0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 22, 2025
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 rewriting of external modules
14 changes: 14 additions & 0 deletions src/_pytest/assertion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import annotations

from collections.abc import Generator
import os
import sys
from typing import Any
from typing import TYPE_CHECKING
Expand Down Expand Up @@ -108,6 +109,19 @@ def __init__(self, config: Config, mode) -> None:
self.trace = config.trace.root.get("assertion")
self.hook: rewrite.AssertionRewritingHook | None = None

@property
def rootpath(self):
"""
Get current root path (current working dir)
"""
try:
return os.getcwd()
except FileNotFoundError:
# Fixes for py's trying to os.getcwd() on py34
# when current working directory doesn't exist (previously triggered via xdist only).
# Ref: https://github.com/pytest-dev/py/pull/207
return os.path.abspath(os.sep)


def install_importhook(config: Config) -> rewrite.AssertionRewritingHook:
"""Try to install the rewrite hook, raise SystemError if it fails."""
Expand Down
3 changes: 2 additions & 1 deletion src/_pytest/assertion/rewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,9 @@ def _should_rewrite(self, name: str, fn: str, state: AssertionState) -> bool:
# modules not passed explicitly on the command line are only
# 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.rootpath):
state.trace(f"matched test file {fn!r}")
return True

Expand Down
113 changes: 113 additions & 0 deletions testing/test_assertrewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import inspect
import marshal
import os
from os import mkdir
from pathlib import Path
import py_compile
import re
Expand All @@ -22,6 +23,8 @@
from unittest import mock
import zipfile

from mock.mock import Mock

import _pytest._code
from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE
from _pytest.assertion import util
Expand All @@ -35,6 +38,7 @@
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 +374,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 @@ -1278,6 +1283,44 @@ def test_meta_path():
)
assert pytester.runpytest().ret == 0

def test_rootpath_base(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
"""
Base cases for get rootpath from AssertionState
"""
from _pytest.assertion import AssertionState

config = pytester.parseconfig()
monkeypatch.chdir(pytester.path)
state = AssertionState(config, "rewrite")
assert state.rootpath == str(pytester.path)
new_rootpath = pytester.path + "/test"
if not os.path.exists(new_rootpath):
os.mkdir(new_rootpath)
monkeypatch.chdir(new_rootpath)
state = AssertionState(config, "rewrite")
assert state.rootpath == 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_rootpath_cwd_removed(
self, pytester: Pytester, monkeypatch: MonkeyPatch
) -> None:
# Setup conditions for py's trying to os.getcwd() on py34
# when current working directory doesn't exist (previously triggered via xdist only).
# Ref: https://github.com/pytest-dev/py/pull/207
from _pytest.assertion import AssertionState

config = pytester.parseconfig()
monkeypatch.setattr(
target=os, name="getcwd", value=Mock(side_effect=FileNotFoundError)
)
state = AssertionState(config, "rewrite")
assert state.rootpath == os.path.abspath(os.sep)

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 @@ -1955,6 +1998,76 @@ def test_simple_failure():
assert hook.find_spec("file") is not None
assert self.find_spec_calls == ["file"]

def test_assert_rewrites_only_rootpath(
self, pytester: Pytester, hook: AssertionRewritingHook, monkeypatch
) -> None:
"""
If test files contained outside the rootpath, then skip them
"""
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

rootpath = f"{os.getcwd()}/tests"
if not os.path.exists(rootpath):
mkdir(rootpath)
monkeypatch.chdir(rootpath)
with mock.patch.object(hook, "fnpats", ["*.py"]):
assert hook.find_spec("file") is None

def test_assert_rewrite_correct_for_conftfest(
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mamy name it in another way - test_assert_rewrite_for_conftfest

self, pytester: Pytester, hook: AssertionRewritingHook, monkeypatch
) -> None:
"""
Conftest is always rewritten regardless of the root dir
"""
pytester.makeconftest(
"""
import pytest
@pytest.fixture
def fix(): return 1
"""
)

rootpath = f"{os.getcwd()}/tests"
if not os.path.exists(rootpath):
mkdir(rootpath)
monkeypatch.chdir(rootpath)

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

def test_assert_rewrite_correct_for_plugins(
self, pytester: Pytester, hook: AssertionRewritingHook, monkeypatch
) -> None:
"""
Plugins has always been rewritten regardless of the root dir
"""
pkgdir = pytester.mkpydir("plugin")
pkgdir.joinpath("__init__.py").write_text(
"import pytest\n"
"@pytest.fixture\n"
"def special_asserter():\n"
" def special_assert(x, y):\n"
" assert x == y\n"
" return special_assert\n",
encoding="utf-8",
)
hook.mark_rewrite("plugin")
rootpath = f"{os.getcwd()}/tests"
if not os.path.exists(rootpath):
mkdir(rootpath)
monkeypatch.chdir(rootpath)
with mock.patch.object(hook, "fnpats", ["*.py"]):
assert hook.find_spec("plugin") is not None

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