Skip to content

Commit a5ffcf5

Browse files
Support labels with values
1 parent d1d88e2 commit a5ffcf5

5 files changed

Lines changed: 48 additions & 21 deletions

File tree

Doc/library/test.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,13 +531,16 @@ The :mod:`!test.support` module defines the following functions:
531531
rather than looking directly in the path directories.
532532

533533

534-
.. function:: mark(label, *, globals=None)
534+
.. function:: mark(label, value=True, *, globals=None)
535535

536536
Add a label to tests.
537537
The ``@mark('label')`` decorator adds a label to method or class.
538538
``test.support.mark('label', globals=globals())`` adds a label to the whole
539539
module.
540540

541+
The optional *value* (``True`` by default) is matched on the command line
542+
by ``--label label=value``, whereas ``--label label`` matches any value.
543+
541544
Many :mod:`test.support` decorators like :func:`requires_resource`,
542545
:func:`~test.support.cpython_only` or :func:`bigmemtest` add labels
543546
automatically.

Lib/test/libregrtest/cmdline.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,14 @@ def _create_parser():
300300
group.add_argument('-i', '--ignore', metavar='PAT',
301301
dest='match_tests', action=FilterAction, const=False,
302302
help='ignore test cases and methods with glob pattern `PAT`')
303-
group.add_argument('--label', metavar='NAME',
303+
group.add_argument('--label', metavar='NAME[=VALUE]',
304304
dest='match_labels', action=FilterAction, const=True,
305-
help='match test cases and methods with label `NAME`')
306-
group.add_argument('--no-label', metavar='NAME',
305+
help='match test cases and methods with label `NAME` '
306+
'(optionally only if its value is `VALUE`)')
307+
group.add_argument('--no-label', metavar='NAME[=VALUE]',
307308
dest='match_labels', action=FilterAction, const=False,
308-
help='ignore test cases and methods with label `NAME`')
309+
help='ignore test cases and methods with label `NAME` '
310+
'(optionally only if its value is `VALUE`)')
309311
group.add_argument('--matchfile', metavar='FILENAME',
310312
dest='match_tests',
311313
action=FromFileFilterAction, const=True,

Lib/test/libregrtest/filter.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
_test_patterns = ()
1010
_match_labels = ()
1111

12+
# Sentinel returned by _get_label() when the test has no such label.
13+
_no_label = object()
14+
1215

1316
def match_test(test):
1417
# Function used by support.run_unittest() and regrtest --list-cases
@@ -23,27 +26,35 @@ def match_test_id(test):
2326

2427
def match_test_label(test):
2528
result = False
26-
for label, result in reversed(_match_labels):
27-
if _has_label(test, label):
29+
for name, value, result in reversed(_match_labels):
30+
actual = _get_label(test, name)
31+
if actual is _no_label:
32+
continue
33+
# value is None for a plain "--label name" (match any value).
34+
if value is None or value == str(actual):
2835
return result
2936
return not result
3037

31-
def _has_label(test, label):
38+
def _get_label(test, label):
3239
attrname = f'_label_{label}'
33-
if hasattr(test, attrname):
34-
return True
40+
value = getattr(test, attrname, _no_label)
41+
if value is not _no_label:
42+
return value
3543
testMethod = getattr(test, test._testMethodName)
3644
while testMethod is not None:
37-
if hasattr(testMethod, attrname):
38-
return True
45+
value = getattr(testMethod, attrname, _no_label)
46+
if value is not _no_label:
47+
return value
3948
testMethod = getattr(testMethod, '__wrapped__', None)
4049
try:
4150
module = sys.modules[test.__class__.__module__]
42-
if hasattr(module, attrname):
43-
return True
4451
except KeyError:
4552
pass
46-
return False
53+
else:
54+
value = getattr(module, attrname, _no_label)
55+
if value is not _no_label:
56+
return value
57+
return _no_label
4758

4859

4960
def _is_full_match_test(pattern):
@@ -80,7 +91,13 @@ def set_match_tests(patterns=None, match_labels=None):
8091
if not match_labels:
8192
_match_labels = ()
8293
else:
83-
_match_labels = tuple(match_labels)
94+
# "name" matches a label with any value, "name=value" matches only
95+
# the specified value.
96+
_match_labels = tuple(
97+
(name, value if sep else None, result)
98+
for label, result in match_labels
99+
for name, sep, value in [label.partition('=')]
100+
)
84101

85102

86103
def _compile_match_function(patterns):

Lib/test/support/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,18 +1352,21 @@ def wrapper(self):
13521352
#=======================================================================
13531353
# unittest integration.
13541354

1355-
def mark(label, *, globals=None):
1355+
def mark(label, value=True, *, globals=None):
13561356
"""Add a label to test.
13571357
13581358
To add a label to method or class, use it as a decorator.
13591359
13601360
To add a label to module, pass the globals() dict as the globals argument.
1361+
1362+
The optional value (``True`` by default) can be matched on the command
1363+
line with ``--label name=value``.
13611364
"""
13621365
if globals is not None:
1363-
globals[f'_label_{label}'] = True
1366+
globals[f'_label_{label}'] = value
13641367
return
13651368
def decorator(test):
1366-
setattr(test, f'_label_{label}', True)
1369+
setattr(test, f'_label_{label}', value)
13671370
return test
13681371
return decorator
13691372

Misc/NEWS.d/next/Tests/2023-09-03-12-53-53.gh-issue-108828.zoWIyX.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ decorator adds a label to method or class. ``test.support.mark('label',
33
globals=globals())`` adds a label to the whole module. Many
44
:mod:`test.support` decorators like :func:`~test.support.requires_resource`,
55
:func:`~test.support.cpython_only` or :func:`~test.support.bigmemtest` add
6-
labels automatically. Tests which have or have not the specified label can
7-
be filtered by options ``--label`` and ``--no-label``.
6+
labels automatically. A label can have a value (``True`` by default). Tests
7+
which have or have not the specified label can be filtered by options
8+
``--label`` and ``--no-label``; ``--label name=value`` matches a specific
9+
value, while ``--label name`` matches any value.

0 commit comments

Comments
 (0)