Skip to content

Commit 395c435

Browse files
committed
Improve F6401:cannot-enumerate-pytest-fixtures:
* Capture and return `stdout` and `stderr` (instead of trashing them) * Fix the 'duplicate-path' error by `relative`-izing the paths before `union`ing them * Update tests to test for both conditions Additionally, fix two `used-before-assignment` pylint issues (`stdout`, `stderr`) 🤪 Signed-off-by: Stavros Ntentos <133706+stdedos@users.noreply.github.com>
1 parent 24b4d2c commit 395c435

File tree

2 files changed

+50
-6
lines changed

2 files changed

+50
-6
lines changed

pylint_pytest/checkers/fixture.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import fnmatch
2-
import os
2+
import io
33
import sys
44
from pathlib import Path
55
from typing import Set, Tuple
@@ -69,7 +69,7 @@ class FixtureChecker(BasePytestChecker):
6969
(
7070
"pylint-pytest plugin cannot enumerate and collect pytest fixtures. "
7171
"Please run `pytest --fixtures --collect-only %s` and resolve "
72-
"any potential syntax error or package dependency issues"
72+
"any potential syntax error or package dependency issues. stdout: %s. stderr: %s."
7373
),
7474
"cannot-enumerate-pytest-fixtures",
7575
"Used when pylint-pytest has been unable to enumerate and collect pytest fixtures.",
@@ -116,11 +116,12 @@ def visit_module(self, node):
116116
is_test_module = True
117117
break
118118

119+
stdout, stderr = sys.stdout, sys.stderr
119120
try:
120-
with open(os.devnull, "w") as devnull:
121+
with io.StringIO() as captured_stdout, io.StringIO() as captured_stderr:
121122
# suppress any future output from pytest
122-
stdout, stderr = sys.stdout, sys.stderr
123-
sys.stderr = sys.stdout = devnull
123+
sys.stderr = captured_stderr
124+
sys.stdout = captured_stdout
124125

125126
# run pytest session with customized plugin to collect fixtures
126127
fixture_collector = FixtureCollector()
@@ -155,9 +156,18 @@ def visit_module(self, node):
155156
)
156157
)
157158
if (ret != pytest.ExitCode.OK or legitimate_failure_paths) and is_test_module:
159+
files_to_report = {
160+
str(Path(x).absolute().relative_to(Path.cwd()))
161+
for x in legitimate_failure_paths | {node.file}
162+
}
163+
158164
self.add_message(
159165
"cannot-enumerate-pytest-fixtures",
160-
args=" ".join(legitimate_failure_paths | {node.file}),
166+
args=(
167+
" ".join(files_to_report),
168+
captured_stdout.getvalue(),
169+
captured_stderr.getvalue(),
170+
),
161171
node=node,
162172
)
163173
finally:

tests/test_cannot_enumerate_fixtures.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import re
2+
13
import pytest
24
from base_tester import BasePytestTester
35

@@ -13,7 +15,39 @@ def test_no_such_package(self, enable_plugin):
1315
self.run_linter(enable_plugin)
1416
self.verify_messages(1 if enable_plugin else 0)
1517

18+
if enable_plugin:
19+
msg = self.msgs[0]
20+
21+
# Asserts/Fixes duplicate filenames in output:
22+
# https://github.com/reverbc/pylint-pytest/pull/22/files#r698204470
23+
filename_arg = msg.args[0]
24+
assert len(re.findall(r"\.py", filename_arg)) == 1
25+
26+
# Asserts that path is relative (usually to the root of the repository).
27+
assert filename_arg[0] != "/"
28+
29+
# Assert `stdout` is non-empty.
30+
assert msg.args[1]
31+
# Assert `stderr` is empty (pytest runs stably, even though fixture collection fails).
32+
assert not msg.args[2]
33+
1634
@pytest.mark.parametrize("enable_plugin", [True, False])
1735
def test_import_corrupted_module(self, enable_plugin):
1836
self.run_linter(enable_plugin)
1937
self.verify_messages(1 if enable_plugin else 0)
38+
39+
if enable_plugin:
40+
msg = self.msgs[0]
41+
42+
# ... somehow, since `import_corrupted_module.py` imports `no_such_package.py`
43+
# both of their names are returned in the message.
44+
filename_arg = msg.args[0]
45+
assert len(re.findall(r"\.py", filename_arg)) == 2
46+
47+
# Asserts that paths are relative (usually to the root of the repository).
48+
assert not [x for x in filename_arg.split(" ") if x[0] == "/"]
49+
50+
# Assert `stdout` is non-empty.
51+
assert msg.args[1]
52+
# Assert `stderr` is empty (pytest runs stably, even though fixture collection fails).
53+
assert not msg.args[2]

0 commit comments

Comments
 (0)