pytest-deadfixtures 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pytest_deadfixtures/__init__.py +3 -0
- pytest_deadfixtures/plugin.py +356 -0
- pytest_deadfixtures-4.0.0.dist-info/METADATA +159 -0
- pytest_deadfixtures-4.0.0.dist-info/RECORD +7 -0
- pytest_deadfixtures-4.0.0.dist-info/WHEEL +4 -0
- pytest_deadfixtures-4.0.0.dist-info/entry_points.txt +2 -0
- pytest_deadfixtures-4.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Some functions are basically copy n' paste version of code already in pytest.
|
|
3
|
+
Precisely the get_fixtures, get_used_fixturesdefs and write_docstring functions.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os.path
|
|
7
|
+
from itertools import combinations
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from textwrap import dedent
|
|
10
|
+
from types import FunctionType
|
|
11
|
+
from typing import Any, Iterable, NamedTuple
|
|
12
|
+
|
|
13
|
+
import _pytest.config
|
|
14
|
+
import _pytest.main
|
|
15
|
+
import pytest
|
|
16
|
+
from _pytest.compat import getlocation
|
|
17
|
+
|
|
18
|
+
DUPLICATE_FIXTURES_HEADLINE = "\n\nYou may have some duplicate fixtures:"
|
|
19
|
+
UNUSED_FIXTURES_FOUND_HEADLINE = (
|
|
20
|
+
"Hey there, I believe the following {count} fixture(s) are not being used:"
|
|
21
|
+
)
|
|
22
|
+
UNUSED_FIXTURES_NOT_FOUND_HEADLINE = "Cool, every declared fixture is being used."
|
|
23
|
+
|
|
24
|
+
IGNORED_FIXTURES_HEADLINE = "Ignored fixture(s) {count}:"
|
|
25
|
+
IGNORED_FIXTURES_ATTR = "_deadfixtures_ignore"
|
|
26
|
+
|
|
27
|
+
EXIT_CODE_ERROR = 11
|
|
28
|
+
EXIT_CODE_SUCCESS = 0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AvailableFixture(NamedTuple):
|
|
32
|
+
relpath: str
|
|
33
|
+
argname: str
|
|
34
|
+
fixturedef: Any
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CachedFixture(NamedTuple):
|
|
38
|
+
fixturedef: Any
|
|
39
|
+
relpath: str
|
|
40
|
+
result: Any
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def pytest_addoption(parser: pytest.Parser) -> None:
|
|
44
|
+
group = parser.getgroup("deadfixtures")
|
|
45
|
+
group.addoption(
|
|
46
|
+
"--dead-fixtures",
|
|
47
|
+
action="store_true",
|
|
48
|
+
dest="deadfixtures",
|
|
49
|
+
default=False,
|
|
50
|
+
help="Show fixtures not being used",
|
|
51
|
+
)
|
|
52
|
+
group.addoption(
|
|
53
|
+
"--dup-fixtures",
|
|
54
|
+
action="store_true",
|
|
55
|
+
dest="showrepeated",
|
|
56
|
+
default=False,
|
|
57
|
+
help="Show duplicated fixtures",
|
|
58
|
+
)
|
|
59
|
+
group.addoption(
|
|
60
|
+
"--show-ignored-fixtures",
|
|
61
|
+
action="store_true",
|
|
62
|
+
default=False,
|
|
63
|
+
help="Show fixtures ignored with `deadfixtures_ignore` mark",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def pytest_cmdline_main(config: pytest.Config) -> int | None:
|
|
68
|
+
if config.option.deadfixtures:
|
|
69
|
+
config.option.show_fixture_doc = config.option.verbose
|
|
70
|
+
config.option.verbose = -1
|
|
71
|
+
return _show_dead_fixtures(config)
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _show_dead_fixtures(config: pytest.Config) -> int | pytest.ExitCode:
|
|
76
|
+
return _pytest.main.wrap_session(config, show_dead_fixtures)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def commonpath(path1: Path, path2: Path) -> Path | None:
|
|
80
|
+
"""Return the common part shared with the other path, or None if there is
|
|
81
|
+
no common part.
|
|
82
|
+
|
|
83
|
+
If one path is relative and one is absolute, returns None.
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
return Path(os.path.commonpath((str(path1), str(path2))))
|
|
87
|
+
except ValueError:
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def bestrelpath(directory: Path, dest: Path) -> str:
|
|
92
|
+
"""Return a string which is a relative path from directory to dest such
|
|
93
|
+
that directory/bestrelpath == dest.
|
|
94
|
+
|
|
95
|
+
The paths must be either both absolute or both relative.
|
|
96
|
+
|
|
97
|
+
If no such path can be determined, returns dest.
|
|
98
|
+
"""
|
|
99
|
+
assert isinstance(directory, Path)
|
|
100
|
+
assert isinstance(dest, Path)
|
|
101
|
+
if dest == directory:
|
|
102
|
+
return os.curdir
|
|
103
|
+
# Find the longest common directory.
|
|
104
|
+
base = commonpath(directory, dest)
|
|
105
|
+
# Can be the case on Windows for two absolute paths on different drives.
|
|
106
|
+
# Can be the case for two relative paths without common prefix.
|
|
107
|
+
# Can be the case for a relative path and an absolute path.
|
|
108
|
+
if not base:
|
|
109
|
+
return str(dest)
|
|
110
|
+
reldirectory = directory.relative_to(base)
|
|
111
|
+
reldest = dest.relative_to(base)
|
|
112
|
+
return str(Path(*([os.pardir] * len(reldirectory.parts))).joinpath(*reldest.parts))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_best_relpath(func, curdir: Path) -> str:
|
|
116
|
+
loc = getlocation(func, str(curdir))
|
|
117
|
+
return bestrelpath(curdir, Path(loc))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def deadfixtures_ignore(func):
|
|
121
|
+
"""Decorator to mark fixtures that should be ignored by the plugin."""
|
|
122
|
+
setattr(func, IGNORED_FIXTURES_ATTR, True)
|
|
123
|
+
return func
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def is_ignored_fixture(fixturedef) -> bool:
|
|
127
|
+
"""Check if a fixture is marked as ignored."""
|
|
128
|
+
return getattr(fixturedef.func, IGNORED_FIXTURES_ATTR, False)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if pytest.version_tuple < (9, 2, 0):
|
|
132
|
+
|
|
133
|
+
def get_all_fixture_defs(session: pytest.Session) -> Iterable[Any]:
|
|
134
|
+
fm = session._fixturemanager
|
|
135
|
+
for fixturedefs in fm._arg2fixturedefs.values():
|
|
136
|
+
yield from fixturedefs
|
|
137
|
+
else:
|
|
138
|
+
|
|
139
|
+
def get_all_fixture_defs(session: pytest.Session) -> Iterable[Any]:
|
|
140
|
+
fm = session._fixturemanager
|
|
141
|
+
return fm._get_all_fixture_defs()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_fixtures(session: pytest.Session) -> list[AvailableFixture]:
|
|
145
|
+
available = []
|
|
146
|
+
seen = set()
|
|
147
|
+
curdir = Path.cwd()
|
|
148
|
+
|
|
149
|
+
for fixturedef in get_all_fixture_defs(session):
|
|
150
|
+
loc = getlocation(fixturedef.func, str(curdir))
|
|
151
|
+
if (fixturedef.argname, loc) in seen:
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
seen.add((fixturedef.argname, loc))
|
|
155
|
+
|
|
156
|
+
module = fixturedef.func.__module__
|
|
157
|
+
|
|
158
|
+
if (
|
|
159
|
+
not module.startswith("_pytest.")
|
|
160
|
+
and not module.startswith("pytest_")
|
|
161
|
+
and "site-packages" not in loc
|
|
162
|
+
and "dist-packages" not in loc
|
|
163
|
+
and "<string>" not in loc
|
|
164
|
+
):
|
|
165
|
+
available.append(
|
|
166
|
+
AvailableFixture(
|
|
167
|
+
bestrelpath(curdir, Path(loc)), fixturedef.argname, fixturedef
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
available.sort(key=lambda a: a.relpath)
|
|
172
|
+
return available
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def get_used_fixturesdefs(session: pytest.Session):
|
|
176
|
+
fixturesdefs = []
|
|
177
|
+
for test_function in session.items:
|
|
178
|
+
try:
|
|
179
|
+
info = test_function._fixtureinfo
|
|
180
|
+
except AttributeError:
|
|
181
|
+
# doctests items have no _fixtureinfo attribute
|
|
182
|
+
continue
|
|
183
|
+
if not info.name2fixturedefs:
|
|
184
|
+
# this test item does not use any fixtures
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
for _, fixturedefs in sorted(info.name2fixturedefs.items()):
|
|
188
|
+
if fixturedefs is None:
|
|
189
|
+
continue
|
|
190
|
+
fixturesdefs.append(fixturedefs[-1])
|
|
191
|
+
return fixturesdefs
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def get_parametrized_fixtures(
|
|
195
|
+
session: pytest.Session, available_fixtures: list[AvailableFixture]
|
|
196
|
+
):
|
|
197
|
+
params_values = []
|
|
198
|
+
for test_function in session.items:
|
|
199
|
+
try:
|
|
200
|
+
for v in test_function.callspec.params.values():
|
|
201
|
+
if isinstance(v, str):
|
|
202
|
+
params_values.append(v)
|
|
203
|
+
except AttributeError:
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
return [
|
|
207
|
+
available.fixturedef
|
|
208
|
+
for available in available_fixtures
|
|
209
|
+
if available.fixturedef.argname in params_values
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def write_docstring(tw, doc: str) -> None:
|
|
214
|
+
INDENT = " "
|
|
215
|
+
doc = doc.rstrip()
|
|
216
|
+
if "\n" in doc:
|
|
217
|
+
firstline, rest = doc.split("\n", 1)
|
|
218
|
+
else:
|
|
219
|
+
firstline, rest = doc, ""
|
|
220
|
+
|
|
221
|
+
if firstline.strip():
|
|
222
|
+
tw.line(INDENT + firstline.strip())
|
|
223
|
+
|
|
224
|
+
if rest:
|
|
225
|
+
for line in dedent(rest).split("\n"):
|
|
226
|
+
tw.write(INDENT + line + "\n")
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def write_fixtures(tw, fixtures, write_docs: bool) -> None:
|
|
230
|
+
for fixture in fixtures:
|
|
231
|
+
tplt = "Fixture name: {}, location: {}"
|
|
232
|
+
tw.line(tplt.format(fixture.argname, fixture.relpath))
|
|
233
|
+
doc = fixture.fixturedef.func.__doc__ or ""
|
|
234
|
+
if write_docs and doc:
|
|
235
|
+
write_docstring(tw, doc)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
cached_fixtures: list[CachedFixture] = []
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def pytest_sessionstart(session: pytest.Session) -> None:
|
|
242
|
+
cached_fixtures.clear()
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def pytest_fixture_post_finalizer(fixturedef, request):
|
|
246
|
+
if not request.config.getvalue("showrepeated"):
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
if getattr(fixturedef, "cached_result", None):
|
|
250
|
+
curdir = Path.cwd()
|
|
251
|
+
loc = getlocation(fixturedef.func, str(curdir))
|
|
252
|
+
|
|
253
|
+
cached_fixtures.append(
|
|
254
|
+
CachedFixture(
|
|
255
|
+
fixturedef, bestrelpath(curdir, Path(loc)), fixturedef.cached_result[0]
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def same_fixture(one, two) -> bool:
|
|
261
|
+
def result_same_type(a, b):
|
|
262
|
+
return isinstance(a.result, type(b.result))
|
|
263
|
+
|
|
264
|
+
def same_result(a, b) -> bool:
|
|
265
|
+
if not a.result or not b.result:
|
|
266
|
+
return False
|
|
267
|
+
if isinstance(a.result, FunctionType):
|
|
268
|
+
return a.result == b.result
|
|
269
|
+
a_result_dict = getattr(a.result, "__dict__", None)
|
|
270
|
+
b_result_dict = getattr(b.result, "__dict__", None)
|
|
271
|
+
if a_result_dict is not None and b_result_dict is not None:
|
|
272
|
+
return a_result_dict == b_result_dict
|
|
273
|
+
return a.result == b.result
|
|
274
|
+
|
|
275
|
+
def same_loc(a, b) -> bool:
|
|
276
|
+
return a.relpath == b.relpath
|
|
277
|
+
|
|
278
|
+
return result_same_type(one, two) and same_result(one, two) and not same_loc(one, two)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def pytest_sessionfinish(
|
|
282
|
+
session: pytest.Session, exitstatus: int | pytest.ExitCode
|
|
283
|
+
) -> None:
|
|
284
|
+
if exitstatus or not session.config.getvalue("showrepeated"):
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
tw = _pytest.config.create_terminal_writer(session.config)
|
|
288
|
+
|
|
289
|
+
duplicated_fixtures = [
|
|
290
|
+
(a, b) for a, b in combinations(cached_fixtures, 2) if same_fixture(a, b)
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
if duplicated_fixtures:
|
|
294
|
+
tw.line(DUPLICATE_FIXTURES_HEADLINE, red=True)
|
|
295
|
+
msg = "Fixture name: {}, location: {}"
|
|
296
|
+
for a, b in duplicated_fixtures:
|
|
297
|
+
tw.line(msg.format(a.fixturedef.argname, a.relpath))
|
|
298
|
+
tw.line(msg.format(b.fixturedef.argname, b.relpath))
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def show_dead_fixtures(
|
|
302
|
+
config: pytest.Config, session: pytest.Session
|
|
303
|
+
) -> int | pytest.ExitCode | None:
|
|
304
|
+
session.perform_collect()
|
|
305
|
+
tw = _pytest.config.create_terminal_writer(config)
|
|
306
|
+
show_fixture_doc = config.getvalue("show_fixture_doc")
|
|
307
|
+
show_ignored = config.getvalue("show_ignored_fixtures")
|
|
308
|
+
|
|
309
|
+
used_fixtures = get_used_fixturesdefs(session)
|
|
310
|
+
available_fixtures = get_fixtures(session)
|
|
311
|
+
param_fixtures = get_parametrized_fixtures(session, available_fixtures)
|
|
312
|
+
|
|
313
|
+
curdir = Path.cwd()
|
|
314
|
+
|
|
315
|
+
def _fixture_key(fixturedef):
|
|
316
|
+
return fixturedef.argname, getlocation(fixturedef.func, curdir)
|
|
317
|
+
|
|
318
|
+
used_keys = {_fixture_key(fixturedef) for fixturedef in used_fixtures}
|
|
319
|
+
param_keys = {_fixture_key(fixturedef) for fixturedef in param_fixtures}
|
|
320
|
+
|
|
321
|
+
# Separate ignored and unused fixtures
|
|
322
|
+
ignored_fixtures = [
|
|
323
|
+
fixture
|
|
324
|
+
for fixture in available_fixtures
|
|
325
|
+
if is_ignored_fixture(fixture.fixturedef)
|
|
326
|
+
]
|
|
327
|
+
|
|
328
|
+
unused_fixtures = [
|
|
329
|
+
fixture
|
|
330
|
+
for fixture in available_fixtures
|
|
331
|
+
if _fixture_key(fixture.fixturedef) not in used_keys
|
|
332
|
+
and _fixture_key(fixture.fixturedef) not in param_keys
|
|
333
|
+
and not is_ignored_fixture(fixture.fixturedef)
|
|
334
|
+
]
|
|
335
|
+
|
|
336
|
+
tw.line()
|
|
337
|
+
if unused_fixtures:
|
|
338
|
+
tw.line(
|
|
339
|
+
UNUSED_FIXTURES_FOUND_HEADLINE.format(count=len(unused_fixtures)), red=True
|
|
340
|
+
)
|
|
341
|
+
write_fixtures(tw, unused_fixtures, show_fixture_doc)
|
|
342
|
+
else:
|
|
343
|
+
tw.line(UNUSED_FIXTURES_NOT_FOUND_HEADLINE, green=True)
|
|
344
|
+
|
|
345
|
+
# Show ignored fixtures if requested
|
|
346
|
+
if show_ignored and ignored_fixtures:
|
|
347
|
+
tw.line()
|
|
348
|
+
tw.line(
|
|
349
|
+
IGNORED_FIXTURES_HEADLINE.format(count=len(ignored_fixtures)), yellow=True
|
|
350
|
+
)
|
|
351
|
+
write_fixtures(tw, ignored_fixtures, show_fixture_doc)
|
|
352
|
+
|
|
353
|
+
if unused_fixtures:
|
|
354
|
+
return EXIT_CODE_ERROR
|
|
355
|
+
else:
|
|
356
|
+
return EXIT_CODE_SUCCESS
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pytest-deadfixtures
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: A simple plugin to list unused fixtures in pytest
|
|
5
|
+
Project-URL: Issues, https://github.com/jllorencetti/pytest-deadfixtures/issues
|
|
6
|
+
Project-URL: Source, https://github.com/jllorencetti/pytest-deadfixtures
|
|
7
|
+
Author-email: João Luiz Lorencetti <me@dirtycoder.net>
|
|
8
|
+
Maintainer-email: João Luiz Lorencetti <me@dirtycoder.net>, Christian Hartung <6785871+hartungstenio@users.noreply.github.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Framework :: Pytest
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: pytest>=9.0
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# pytest-deadfixtures
|
|
31
|
+
|
|
32
|
+
[](https://pypi.org/project/pytest-deadfixtures)
|
|
33
|
+
[](https://pypi.org/project/pytest-deadfixtures)
|
|
34
|
+
[](https://results.pre-commit.ci/latest/github/jllorencetti/pytest-deadfixtures/main)
|
|
35
|
+
|
|
36
|
+
----
|
|
37
|
+
|
|
38
|
+
A simple plugin to list unused or duplicated fixtures in a pytest suite.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- List unused fixtures in your tests
|
|
43
|
+
- List duplicated fixtures
|
|
44
|
+
- Ignore specific fixtures from analysis with `@deadfixtures_ignore`
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
Install `pytest-deadfixtures` from [PyPI](https://pypi.org/project/pytest-deadfixtures/):
|
|
49
|
+
|
|
50
|
+
```console
|
|
51
|
+
pip install pytest-deadfixtures
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Important
|
|
57
|
+
|
|
58
|
+
The `--dead-fixtures` option does not run your tests and can also be sensitive to
|
|
59
|
+
errors during the pytest collection step. If using it in CI, run it after the
|
|
60
|
+
normal test run:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
script:
|
|
64
|
+
- pytest
|
|
65
|
+
- pytest --dead-fixtures
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Listing unused fixtures
|
|
69
|
+
|
|
70
|
+
Run pytest with the `--dead-fixtures` option:
|
|
71
|
+
|
|
72
|
+
```console
|
|
73
|
+
pytest --dead-fixtures
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
Hey there, I believe the following fixture(s) are not being used:
|
|
78
|
+
Fixture name: some_fixture, location: test_write_docs_when_verbose.py:5
|
|
79
|
+
|
|
80
|
+
no tests ran
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use verbosity to include fixture docstrings in the report:
|
|
84
|
+
|
|
85
|
+
```console
|
|
86
|
+
pytest --dead-fixtures -v
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Listing repeated fixtures
|
|
90
|
+
|
|
91
|
+
The `--dup-fixtures` option compares fixture return values to find possible
|
|
92
|
+
duplicates. Fixtures without a truthy return value are skipped. Treat the report
|
|
93
|
+
as a hint and verify that the fixtures provide the same functionality before
|
|
94
|
+
removing one.
|
|
95
|
+
|
|
96
|
+
Unlike `--dead-fixtures`, this option runs the tests:
|
|
97
|
+
|
|
98
|
+
```console
|
|
99
|
+
pytest --dup-fixtures
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
tests/test_deadfixtures.py ........
|
|
104
|
+
|
|
105
|
+
You may have some duplicate fixtures:
|
|
106
|
+
Fixture name: someclass_fixture, location: test_repeated_fixtures.py:12
|
|
107
|
+
Fixture name: someclass_samefixture, location: test_repeated_fixtures.py:17
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Ignoring specific fixtures
|
|
111
|
+
|
|
112
|
+
Use the `@deadfixtures_ignore` decorator to exclude a fixture from the analysis:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import pytest
|
|
116
|
+
|
|
117
|
+
from pytest_deadfixtures import deadfixtures_ignore
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@pytest.fixture
|
|
121
|
+
@deadfixtures_ignore
|
|
122
|
+
def intentionally_unused_fixture():
|
|
123
|
+
"""This fixture won't be reported as unused."""
|
|
124
|
+
return "something"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
To see which fixtures are ignored, use `--show-ignored-fixtures`:
|
|
128
|
+
|
|
129
|
+
```console
|
|
130
|
+
pytest --dead-fixtures --show-ignored-fixtures
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
No unused fixtures found. Great job!
|
|
135
|
+
|
|
136
|
+
Ignored fixture(s) 1:
|
|
137
|
+
Fixture name: intentionally_unused_fixture, location: conftest.py:10
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Projects using it
|
|
141
|
+
|
|
142
|
+
- [wemake-django-template](https://github.com/wemake-services/wemake-django-template)
|
|
143
|
+
- [fands.dev django template](https://github.com/fandsdev/django)
|
|
144
|
+
|
|
145
|
+
## Contributing
|
|
146
|
+
|
|
147
|
+
Contributions are welcome. Run the tests with [`hatch test`](https://hatch.pypa.io/latest/cli/reference/#hatch-test)
|
|
148
|
+
and check the code with [`hatch check code`](https://hatch.pypa.io/latest/cli/reference/#hatch-check)
|
|
149
|
+
before submitting a pull request.
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
Distributed under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
154
|
+
`pytest-deadfixtures` is free and open source software.
|
|
155
|
+
|
|
156
|
+
## Issues
|
|
157
|
+
|
|
158
|
+
If you encounter any problems, [file an issue](https://github.com/jllorencetti/pytest-deadfixtures/issues)
|
|
159
|
+
with a detailed description.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
pytest_deadfixtures/__init__.py,sha256=IhffZzQvwp9lxwBblmXUK9vwjLw8sgEAokEFZvJHChw,75
|
|
2
|
+
pytest_deadfixtures/plugin.py,sha256=iHZsk9lNfPtCqTTdx9h5xWPHJrix4pA8zyFZSg9C4TY,10807
|
|
3
|
+
pytest_deadfixtures-4.0.0.dist-info/METADATA,sha256=MhE6tiTArfeULus5g7fUFnfl1_kZasCjS4LMB_BT8EI,4846
|
|
4
|
+
pytest_deadfixtures-4.0.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
5
|
+
pytest_deadfixtures-4.0.0.dist-info/entry_points.txt,sha256=tnHaWCnR8PjAP5gUtCq1xv6r5fXxU-vzobPE3mR7Y5U,53
|
|
6
|
+
pytest_deadfixtures-4.0.0.dist-info/licenses/LICENSE,sha256=6ZFh-GBggddu8SVAu3jMK_xAj7mq_nh7Q8X1gvYpZAA,1088
|
|
7
|
+
pytest_deadfixtures-4.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 João Luiz Lorencetti
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|