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
12 changes: 12 additions & 0 deletions src/_pytest/config/argparsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,18 @@ def __init__(
def error(self, message: str) -> NoReturn:
"""Transform argparse error message into UsageError."""
msg = f"{self.prog}: error: {message}"
if "unrecognized arguments:" in message:
file_or_dir_args = getattr(self._parser, "extra_info", {}).get(
"file_or_dir", []
)
if file_or_dir_args:
from pathlib import Path

missing_paths = [
str(p) for p in file_or_dir_args if not Path(str(p)).exists()
]
if missing_paths:
msg += "\nNote: The specified path(s) do not exist, so custom CLI options from conftest.py may not be available."

if hasattr(self._parser, "_config_source_hint"):
msg = f"{msg} ({self._parser._config_source_hint})"
Expand Down
13 changes: 13 additions & 0 deletions src/_pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ def pytest_addoption(parser: Parser) -> None:
"Processed after -W/--pythonwarnings.",
)

parser.addini(
"strict_warnings",
type="bool",
default=False,
help="If true, treat internal pytest warnings (PytestWarning and subclasses) as errors.",
)
group.addoption(
"--strict-warnings",
action="store_true",
dest="strict_warnings",
help="Treat internal pytest warnings (PytestWarning and subclasses) as errors.",
)

group = parser.getgroup("collect", "collection")
group.addoption(
"--collectonly",
Expand Down
6 changes: 6 additions & 0 deletions src/_pytest/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ def catch_warnings_for_item(
for arg in mark.args:
warnings.filterwarnings(*parse_warning_filter(arg, escape=False))

strict_warnings = bool(config.getini("strict_warnings")) or bool(
getattr(config.option, "strict_warnings", False)
)
if strict_warnings:
warnings.filterwarnings("error", category=pytest.PytestWarning)

try:
yield
finally:
Expand Down
13 changes: 13 additions & 0 deletions testing/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,19 @@ def test_absolute_paths_are_resolved_correctly(self, invocation_path: Path) -> N
module_name=None,
)

def test_custom_cli_arg_with_missing_path(pytester: Pytester):
"""Test that a helpful error message is shown when a custom CLI argument is used with a non-existent path."""
pytester.makeconftest(
"""
def pytest_addoption(parser):
parser.addoption("--potato", default="")
"""
)
result = pytester.runpytest("file_does_not_exist.py", "--potato=yum")
assert result.ret != 0
assert "unrecognized arguments: --potato=yum" in result.stderr.str()
assert "Note: The specified path(s) do not exist" in result.stderr.str()


def test_module_full_path_without_drive(pytester: Pytester) -> None:
"""Collect and run test using full path except for the drive letter (#7628).
Expand Down
28 changes: 28 additions & 0 deletions testing/test_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,34 @@ def test_foo():
result, "assertion is always true, perhaps remove parentheses?"
)

def test_strict_warnings_treats_pytest_collection_warning_as_error(
pytester: Pytester,
) -> None:
"""In strict warnings mode, PytestCollectionWarning should be treated as an error."""
pytester.makepyfile(
"""
import pytest
pytestmark = pytest.mark.filterwarnings("default::pytest.PytestCollectionWarning")

class TestFoo:
def __new__(cls):
return super().__new__(cls)
"""
)
result = pytester.runpytest("--collect-only")
result.stdout.fnmatch_lines(
[
"*warnings summary*",
"*PytestCollectionWarning: cannot collect test class 'TestFoo' because it has a __new__ constructor*",
]
)
result_strict = pytester.runpytest("--collect-only", "--strict-warnings")
assert result_strict.ret != 0
result_strict.stdout.fnmatch_lines(
[
"*PytestCollectionWarning: cannot collect test class 'TestFoo' because it has a __new__ constructor*",
]
)

def test_warnings_checker_twice() -> None:
"""Issue #4617"""
Expand Down