Skip to content

Commit 407d74b

Browse files
authored
Merge pull request #5015 from Zac-HD/mark-warning-prep
Pre-PR for warnings on unknown markers
2 parents 278b289 + deade37 commit 407d74b

File tree

12 files changed

+68
-36
lines changed

12 files changed

+68
-36
lines changed

changelog/4935.doc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Expand docs on registering marks and the effect of ``--strict``.

doc/en/mark.rst

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,10 @@ which also serve as documentation.
2626
:ref:`fixtures <fixtures>`.
2727

2828

29-
Raising errors on unknown marks: --strict
30-
-----------------------------------------
29+
.. _unknown-marks:
3130

32-
When the ``--strict`` command-line flag is passed, any unknown marks applied
33-
with the ``@pytest.mark.name_of_the_mark`` decorator will trigger an error.
34-
Marks defined or added by pytest or by a plugin will not trigger an error.
31+
Raising errors on unknown marks
32+
-------------------------------
3533

3634
Marks can be registered in ``pytest.ini`` like this:
3735

@@ -42,8 +40,10 @@ Marks can be registered in ``pytest.ini`` like this:
4240
slow
4341
serial
4442
45-
This can be used to prevent users mistyping mark names by accident. Test suites that want to enforce this
46-
should add ``--strict`` to ``addopts``:
43+
When the ``--strict`` command-line flag is passed, any unknown marks applied
44+
with the ``@pytest.mark.name_of_the_mark`` decorator will trigger an error.
45+
Marks added by pytest or by a plugin instead of the decorator will not trigger
46+
this error. To enforce validation of markers, add ``--strict`` to ``addopts``:
4747

4848
.. code-block:: ini
4949
@@ -53,6 +53,9 @@ should add ``--strict`` to ``addopts``:
5353
slow
5454
serial
5555
56+
Third-party plugins should always :ref:`register their markers <registering-markers>`
57+
so that they appear in pytest's help text and do not emit warnings.
58+
5659

5760
.. _marker-revamp:
5861

doc/en/writing_plugins.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,26 @@ the plugin manager like this:
286286
If you want to look at the names of existing plugins, use
287287
the ``--trace-config`` option.
288288

289+
290+
.. _registering-markers:
291+
292+
Registering custom markers
293+
--------------------------
294+
295+
If your plugin uses any markers, you should register them so that they appear in
296+
pytest's help text and do not :ref:`cause spurious warnings <unknown-marks>`.
297+
For example, the following plugin would register ``cool_marker`` and
298+
``mark_with`` for all users:
299+
300+
.. code-block:: python
301+
302+
def pytest_configure(config):
303+
config.addinivalue_line("markers", "cool_marker: this one is for cool tests.")
304+
config.addinivalue_line(
305+
"markers", "mark_with(arg, arg2): this marker takes arguments."
306+
)
307+
308+
289309
Testing plugins
290310
---------------
291311

src/_pytest/mark/structures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def _for_parametrize(cls, argnames, argvalues, func, config, function_definition
129129
)
130130
else:
131131
# empty parameter set (likely computed at runtime): create a single
132-
# parameter set with NOSET values, with the "empty parameter set" mark applied to it
132+
# parameter set with NOTSET values, with the "empty parameter set" mark applied to it
133133
mark = get_empty_parameterset_mark(config, argnames, func)
134134
parameters.append(
135135
ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None)
@@ -152,7 +152,7 @@ def combined_with(self, other):
152152
:type other: Mark
153153
:rtype: Mark
154154
155-
combines by appending aargs and merging the mappings
155+
combines by appending args and merging the mappings
156156
"""
157157
assert self.name == other.name
158158
return Mark(

src/_pytest/pytester.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ def pytest_configure(config):
6868
if checker.matching_platform():
6969
config.pluginmanager.register(checker)
7070

71+
config.addinivalue_line(
72+
"markers",
73+
"pytester_example_path(*path_segments): join the given path "
74+
"segments to `pytester_example_dir` for this test.",
75+
)
76+
7177

7278
def raise_on_kwargs(kwargs):
7379
if kwargs:

testing/python/fixtures.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,7 +1927,7 @@ def test_hello(arg1):
19271927
reprec = testdir.inline_run()
19281928
reprec.assertoutcome(passed=1)
19291929

1930-
@pytest.mark.issue226
1930+
@pytest.mark.issue(226)
19311931
@pytest.mark.parametrize("param1", ["", "params=[1]"], ids=["p00", "p01"])
19321932
@pytest.mark.parametrize("param2", ["", "params=[1]"], ids=["p10", "p11"])
19331933
def test_ordering_dependencies_torndown_first(self, testdir, param1, param2):
@@ -2709,7 +2709,7 @@ def test_3():
27092709
reprec = testdir.inline_run("-v")
27102710
reprec.assertoutcome(passed=5)
27112711

2712-
@pytest.mark.issue246
2712+
@pytest.mark.issue(246)
27132713
@pytest.mark.parametrize("scope", ["session", "function", "module"])
27142714
def test_finalizer_order_on_parametrization(self, scope, testdir):
27152715
testdir.makepyfile(
@@ -2746,7 +2746,7 @@ def test_other():
27462746
reprec = testdir.inline_run("-lvs")
27472747
reprec.assertoutcome(passed=3)
27482748

2749-
@pytest.mark.issue396
2749+
@pytest.mark.issue(396)
27502750
def test_class_scope_parametrization_ordering(self, testdir):
27512751
testdir.makepyfile(
27522752
"""
@@ -2867,7 +2867,7 @@ def test_foo(fix):
28672867
res = testdir.runpytest("-v")
28682868
res.stdout.fnmatch_lines(["*test_foo*alpha*", "*test_foo*beta*"])
28692869

2870-
@pytest.mark.issue920
2870+
@pytest.mark.issue(920)
28712871
def test_deterministic_fixture_collection(self, testdir, monkeypatch):
28722872
testdir.makepyfile(
28732873
"""

testing/python/integration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ def test_blah(self):
393393
assert not call.items
394394

395395

396-
@pytest.mark.issue351
396+
@pytest.mark.issue(351)
397397
class TestParameterize(object):
398398
def test_idfn_marker(self, testdir):
399399
testdir.makepyfile(

testing/python/metafunc.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def func(x, y):
159159
("x", "y"), [("abc", "def"), ("ghi", "jkl")], ids=["one"]
160160
)
161161

162-
@pytest.mark.issue510
162+
@pytest.mark.issue(510)
163163
def test_parametrize_empty_list(self):
164164
def func(y):
165165
pass
@@ -262,7 +262,7 @@ def test_function():
262262
for val, expected in values:
263263
assert _idval(val, "a", 6, None, item=None, config=None) == expected
264264

265-
@pytest.mark.issue250
265+
@pytest.mark.issue(250)
266266
def test_idmaker_autoname(self):
267267
from _pytest.python import idmaker
268268

@@ -356,7 +356,7 @@ def test_idmaker_enum(self):
356356
result = idmaker(("a", "b"), [pytest.param(e.one, e.two)])
357357
assert result == ["Foo.one-Foo.two"]
358358

359-
@pytest.mark.issue351
359+
@pytest.mark.issue(351)
360360
def test_idmaker_idfn(self):
361361
from _pytest.python import idmaker
362362

@@ -375,7 +375,7 @@ def ids(val):
375375
)
376376
assert result == ["10.0-IndexError()", "20-KeyError()", "three-b2"]
377377

378-
@pytest.mark.issue351
378+
@pytest.mark.issue(351)
379379
def test_idmaker_idfn_unique_names(self):
380380
from _pytest.python import idmaker
381381

@@ -459,7 +459,7 @@ def test_idmaker_with_ids_unique_names(self):
459459
)
460460
assert result == ["a0", "a1", "b0", "c", "b1"]
461461

462-
@pytest.mark.issue714
462+
@pytest.mark.issue(714)
463463
def test_parametrize_indirect(self):
464464
def func(x, y):
465465
pass
@@ -473,7 +473,7 @@ def func(x, y):
473473
assert metafunc._calls[0].params == dict(x=1, y=2)
474474
assert metafunc._calls[1].params == dict(x=1, y=3)
475475

476-
@pytest.mark.issue714
476+
@pytest.mark.issue(714)
477477
def test_parametrize_indirect_list(self):
478478
def func(x, y):
479479
pass
@@ -483,7 +483,7 @@ def func(x, y):
483483
assert metafunc._calls[0].funcargs == dict(y="b")
484484
assert metafunc._calls[0].params == dict(x="a")
485485

486-
@pytest.mark.issue714
486+
@pytest.mark.issue(714)
487487
def test_parametrize_indirect_list_all(self):
488488
def func(x, y):
489489
pass
@@ -493,7 +493,7 @@ def func(x, y):
493493
assert metafunc._calls[0].funcargs == {}
494494
assert metafunc._calls[0].params == dict(x="a", y="b")
495495

496-
@pytest.mark.issue714
496+
@pytest.mark.issue(714)
497497
def test_parametrize_indirect_list_empty(self):
498498
def func(x, y):
499499
pass
@@ -503,7 +503,7 @@ def func(x, y):
503503
assert metafunc._calls[0].funcargs == dict(x="a", y="b")
504504
assert metafunc._calls[0].params == {}
505505

506-
@pytest.mark.issue714
506+
@pytest.mark.issue(714)
507507
def test_parametrize_indirect_list_functional(self, testdir):
508508
"""
509509
Test parametrization with 'indirect' parameter applied on
@@ -532,7 +532,7 @@ def test_simple(x,y):
532532
result = testdir.runpytest("-v")
533533
result.stdout.fnmatch_lines(["*test_simple*a-b*", "*1 passed*"])
534534

535-
@pytest.mark.issue714
535+
@pytest.mark.issue(714)
536536
def test_parametrize_indirect_list_error(self, testdir):
537537
def func(x, y):
538538
pass
@@ -541,7 +541,7 @@ def func(x, y):
541541
with pytest.raises(pytest.fail.Exception):
542542
metafunc.parametrize("x, y", [("a", "b")], indirect=["x", "z"])
543543

544-
@pytest.mark.issue714
544+
@pytest.mark.issue(714)
545545
def test_parametrize_uses_no_fixture_error_indirect_false(self, testdir):
546546
"""The 'uses no fixture' error tells the user at collection time
547547
that the parametrize data they've set up doesn't correspond to the
@@ -560,7 +560,7 @@ def test_simple(x):
560560
result = testdir.runpytest("--collect-only")
561561
result.stdout.fnmatch_lines(["*uses no argument 'y'*"])
562562

563-
@pytest.mark.issue714
563+
@pytest.mark.issue(714)
564564
def test_parametrize_uses_no_fixture_error_indirect_true(self, testdir):
565565
testdir.makepyfile(
566566
"""
@@ -580,7 +580,7 @@ def test_simple(x):
580580
result = testdir.runpytest("--collect-only")
581581
result.stdout.fnmatch_lines(["*uses no fixture 'y'*"])
582582

583-
@pytest.mark.issue714
583+
@pytest.mark.issue(714)
584584
def test_parametrize_indirect_uses_no_fixture_error_indirect_string(self, testdir):
585585
testdir.makepyfile(
586586
"""
@@ -597,7 +597,7 @@ def test_simple(x):
597597
result = testdir.runpytest("--collect-only")
598598
result.stdout.fnmatch_lines(["*uses no fixture 'y'*"])
599599

600-
@pytest.mark.issue714
600+
@pytest.mark.issue(714)
601601
def test_parametrize_indirect_uses_no_fixture_error_indirect_list(self, testdir):
602602
testdir.makepyfile(
603603
"""
@@ -614,7 +614,7 @@ def test_simple(x):
614614
result = testdir.runpytest("--collect-only")
615615
result.stdout.fnmatch_lines(["*uses no fixture 'y'*"])
616616

617-
@pytest.mark.issue714
617+
@pytest.mark.issue(714)
618618
def test_parametrize_argument_not_in_indirect_list(self, testdir):
619619
testdir.makepyfile(
620620
"""
@@ -1201,7 +1201,7 @@ def test_foo(x):
12011201
reprec = testdir.runpytest()
12021202
reprec.assert_outcomes(passed=4)
12031203

1204-
@pytest.mark.issue463
1204+
@pytest.mark.issue(463)
12051205
@pytest.mark.parametrize("attr", ["parametrise", "parameterize", "parameterise"])
12061206
def test_parametrize_misspelling(self, testdir, attr):
12071207
testdir.makepyfile(
@@ -1386,7 +1386,7 @@ def pytest_generate_tests(metafunc):
13861386
assert output.count("preparing foo-3") == 1
13871387

13881388

1389-
@pytest.mark.issue308
1389+
@pytest.mark.issue(308)
13901390
class TestMarkersWithParametrization(object):
13911391
def test_simple_mark(self, testdir):
13921392
s = """
@@ -1575,7 +1575,7 @@ def test_increment(n, expected):
15751575
reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG)
15761576
reprec.assertoutcome(passed=2, skipped=2)
15771577

1578-
@pytest.mark.issue290
1578+
@pytest.mark.issue(290)
15791579
def test_parametrize_ID_generation_string_int_works(self, testdir):
15801580
testdir.makepyfile(
15811581
"""

testing/test_capture.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ def test_hello(capfd):
605605
result.stdout.fnmatch_lines(["*KeyboardInterrupt*"])
606606
assert result.ret == 2
607607

608-
@pytest.mark.issue14
608+
@pytest.mark.issue(14)
609609
def test_capture_and_logging(self, testdir):
610610
p = testdir.makepyfile(
611611
"""\

testing/test_conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ def test_no_conftest(fxtr):
490490
("snc", ".", 1),
491491
],
492492
)
493-
@pytest.mark.issue616
493+
@pytest.mark.issue(616)
494494
def test_parsefactories_relative_node_ids(
495495
self, testdir, chdir, testarg, expect_ntests_passed
496496
):

testing/test_mark.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ def test_bar(self): pass
448448
items, rec = testdir.inline_genitems(p)
449449
self.assert_markers(items, test_foo=("a", "b"), test_bar=("a",))
450450

451-
@pytest.mark.issue568
451+
@pytest.mark.issue(568)
452452
def test_mark_should_not_pass_to_siebling_class(self, testdir):
453453
p = testdir.makepyfile(
454454
"""
@@ -651,7 +651,7 @@ def assert_markers(self, items, **expected):
651651
markers = {m.name for m in items[name].iter_markers()}
652652
assert markers == set(expected_markers)
653653

654-
@pytest.mark.issue1540
654+
@pytest.mark.issue(1540)
655655
@pytest.mark.filterwarnings("ignore")
656656
def test_mark_from_parameters(self, testdir):
657657
testdir.makepyfile(

tox.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ filterwarnings =
166166
# Do not cause SyntaxError for invalid escape sequences in py37.
167167
default:invalid escape sequence:DeprecationWarning
168168
pytester_example_dir = testing/example_scripts
169+
markers =
170+
issue
169171

170172
[flake8]
171173
max-line-length = 120

0 commit comments

Comments
 (0)