self.randomize = False
self.fromfile = None
self.fail_env_changed = False
- self.use_resources: list[str] = []
+ self.use_resources: dict[str, str | None] = {}
self.trace = False
self.coverdir = 'coverage'
self.runleaks = False
group.add_argument('-G', '--failfast', action='store_true',
help='fail as soon as a test fails (only with -v or -W)')
group.add_argument('-u', '--use', metavar='RES1,RES2,...',
- action='append', type=resources_list,
+ action='extend', type=resources_list,
help='specify which special resource intensive tests '
'to run.' + more_details)
group.add_argument('-M', '--memlimit', metavar='LIMIT',
def resources_list(string):
- u = [x.lower() for x in string.split(',')]
- for r in u:
+ u = []
+ for x in string.split(','):
+ r, eq, v = x.partition('=')
+ r = r.lower()
+ u.append((r, v if eq else None))
if r == 'all' or r == 'none':
+ if eq:
+ raise argparse.ArgumentTypeError('invalid resource: ' + x)
continue
if r[0] == '-':
+ if eq:
+ raise argparse.ArgumentTypeError('invalid resource: ' + x)
r = r[1:]
if r not in RESOURCE_NAMES:
raise argparse.ArgumentTypeError('invalid resource: ' + r)
# Similar to: -u "all" --timeout=1200
if ns.use is None:
ns.use = []
- ns.use.insert(0, ['all'])
+ ns.use[:0] = [('all', None)]
if ns.timeout is None:
ns.timeout = 1200 # 20 minutes
elif ns.fast_ci:
# Similar to: -u "all,-cpu" --timeout=600
if ns.use is None:
ns.use = []
- ns.use.insert(0, ['all', '-cpu'])
+ ns.use[:0] = [('all', None), ('-cpu', None)]
if ns.timeout is None:
ns.timeout = 600 # 10 minutes
if ns.timeout <= 0:
ns.timeout = None
if ns.use:
- for a in ns.use:
- for r in a:
- if r == 'all':
- ns.use_resources[:] = ALL_RESOURCES
- continue
- if r == 'none':
- del ns.use_resources[:]
- continue
- remove = False
- if r[0] == '-':
- remove = True
- r = r[1:]
- if remove:
- if r in ns.use_resources:
- ns.use_resources.remove(r)
- elif r not in ns.use_resources:
- ns.use_resources.append(r)
+ for r, v in ns.use:
+ if r == 'all':
+ for r in ALL_RESOURCES:
+ ns.use_resources[r] = None
+ elif r == 'none':
+ ns.use_resources.clear()
+ elif r[0] == '-':
+ r = r[1:]
+ ns.use_resources.pop(r, None)
+ else:
+ ns.use_resources[r] = v
if ns.random_seed is not None:
ns.randomize = True
if ns.no_randomize:
import sysconfig
import tempfile
import textwrap
-from collections.abc import Callable, Iterable
+from collections.abc import Callable
from test import support
from test.support import os_helper
return ('_PYTHON_HOST_PLATFORM' in os.environ)
-def format_resources(use_resources: Iterable[str]) -> str:
- use_resources = set(use_resources)
+def format_resources(use_resources: dict[str, str | None]) -> str:
all_resources = set(ALL_RESOURCES)
+ values = []
+ for name in sorted(use_resources):
+ if use_resources[name] is not None:
+ values.append(f'{name}={use_resources[name]}')
+
# Express resources relative to "all"
relative_all = ['all']
- for name in sorted(all_resources - use_resources):
+ for name in sorted(all_resources - set(use_resources)):
relative_all.append(f'-{name}')
- for name in sorted(use_resources - all_resources):
- relative_all.append(f'{name}')
- all_text = ','.join(relative_all)
+ for name in sorted(set(use_resources) - all_resources):
+ if use_resources[name] is None:
+ relative_all.append(name)
+ all_text = ','.join(relative_all + values)
all_text = f"resources: {all_text}"
# List of enabled resources
- text = ','.join(sorted(use_resources))
+ resources = []
+ for name in sorted(use_resources):
+ if use_resources[name] is None:
+ resources.append(name)
+ text = ','.join(resources + values)
text = f"resources ({len(use_resources)}): {text}"
# Pick the shortest string (prefer relative to all if lengths are equal)
return text
-def display_header(use_resources: tuple[str, ...],
+def display_header(use_resources: dict[str, str | None],
python_cmd: tuple[str, ...] | None) -> None:
# Print basic platform information
print("==", platform.python_implementation(), *sys.version.split())
"record_original_stdout", "get_original_stdout", "captured_stdout",
"captured_stdin", "captured_stderr", "captured_output",
# unittest
- "is_resource_enabled", "requires", "requires_freebsd_version",
+ "is_resource_enabled", "get_resource_value", "requires", "requires_resource",
+ "requires_freebsd_version",
"requires_gil_enabled", "requires_linux_version", "requires_mac_ver",
"check_syntax_error",
"requires_gzip", "requires_bz2", "requires_lzma", "requires_zstd",
return attribute
verbose = 1 # Flag set to 0 by regrtest.py
-use_resources = None # Flag set to [] by regrtest.py
+use_resources = None # Flag set to {} by regrtest.py
max_memuse = 0 # Disable bigmem tests (they will still be run with
# small sizes, to make sure they work.)
real_max_memuse = 0
"""
return use_resources is None or resource in use_resources
+def get_resource_value(resource):
+ """Test whether a resource is enabled.
+
+ Known resources are set by regrtest.py. If not running under regrtest.py,
+ all resources are assumed enabled unless use_resources has been set.
+ """
+ if use_resources is None:
+ return None
+ return use_resources.get(resource)
+
def requires(resource, msg=None):
"""Raise ResourceDenied if the specified resource is not available."""
if not is_resource_enabled(resource):
for opt in '-u', '--use':
with self.subTest(opt=opt):
ns = self.parse_args([opt, 'gui,network'])
- self.assertEqual(ns.use_resources, ['gui', 'network'])
+ self.assertEqual(ns.use_resources, {'gui': None, 'network': None})
+ ns = self.parse_args([opt, 'gui', opt, 'network'])
+ self.assertEqual(ns.use_resources, {'gui': None, 'network': None})
ns = self.parse_args([opt, 'gui,none,network'])
- self.assertEqual(ns.use_resources, ['network'])
+ self.assertEqual(ns.use_resources, {'network': None})
+ ns = self.parse_args([opt, 'gui', opt, 'none', opt, 'network'])
+ self.assertEqual(ns.use_resources, {'network': None})
- expected = list(cmdline.ALL_RESOURCES)
- expected.remove('gui')
+ expected = dict.fromkeys(cmdline.ALL_RESOURCES)
+ del expected['gui']
ns = self.parse_args([opt, 'all,-gui'])
self.assertEqual(ns.use_resources, expected)
+
self.checkError([opt], 'expected one argument')
self.checkError([opt, 'foo'], 'invalid resource')
# all + a resource not part of "all"
+ expected = dict.fromkeys(cmdline.ALL_RESOURCES)
+ expected['tzdata'] = None
ns = self.parse_args([opt, 'all,tzdata'])
- self.assertEqual(ns.use_resources,
- list(cmdline.ALL_RESOURCES) + ['tzdata'])
+ self.assertEqual(ns.use_resources, expected)
+ ns = self.parse_args([opt, 'all', opt, 'tzdata'])
+ self.assertEqual(ns.use_resources, expected)
# test another resource which is not part of "all"
ns = self.parse_args([opt, 'extralargefile'])
- self.assertEqual(ns.use_resources, ['extralargefile'])
+ self.assertEqual(ns.use_resources, {'extralargefile': None})
+
+ # test resource with value
+ ns = self.parse_args([opt, 'xpickle=2.7'])
+ self.assertEqual(ns.use_resources, {'xpickle': '2.7'})
+ ns = self.parse_args([opt, 'xpickle=2.7,xpickle=3.3'])
+ self.assertEqual(ns.use_resources, {'xpickle': '3.3'})
+ ns = self.parse_args([opt, 'xpickle=2.7,none'])
+ self.assertEqual(ns.use_resources, {})
+ ns = self.parse_args([opt, 'xpickle=2.7,-xpickle'])
+ self.assertEqual(ns.use_resources, {})
+
+ expected = dict.fromkeys(cmdline.ALL_RESOURCES)
+ expected['xpickle'] = '2.7'
+ ns = self.parse_args([opt, 'all,xpickle=2.7'])
+ self.assertEqual(ns.use_resources, expected)
+ ns = self.parse_args([opt, 'all', opt, 'xpickle=2.7'])
+ self.assertEqual(ns.use_resources, expected)
+
+ # test invalid resources with value
+ self.checkError([opt, 'all=0'], 'invalid resource: all=0')
+ self.checkError([opt, 'none=0'], 'invalid resource: none=0')
+ self.checkError([opt, 'all,-gui=0'], 'invalid resource: -gui=0')
def test_memlimit(self):
for opt in '-M', '--memlimit':
self.assertTrue(regrtest.fail_env_changed)
self.assertTrue(regrtest.print_slowest)
self.assertEqual(regrtest.output_on_failure, output_on_failure)
- self.assertEqual(sorted(regrtest.use_resources), sorted(use_resources))
+ self.assertEqual(regrtest.use_resources, use_resources)
return regrtest
def test_fast_ci(self):
args = ['--fast-ci']
- use_resources = sorted(cmdline.ALL_RESOURCES)
- use_resources.remove('cpu')
+ use_resources = dict.fromkeys(cmdline.ALL_RESOURCES)
+ del use_resources['cpu']
regrtest = self.check_ci_mode(args, use_resources)
self.assertEqual(regrtest.timeout, 10 * 60)
def test_fast_ci_python_cmd(self):
args = ['--fast-ci', '--python', 'python -X dev']
- use_resources = sorted(cmdline.ALL_RESOURCES)
- use_resources.remove('cpu')
+ use_resources = dict.fromkeys(cmdline.ALL_RESOURCES)
+ del use_resources['cpu']
regrtest = self.check_ci_mode(args, use_resources, rerun=False)
self.assertEqual(regrtest.timeout, 10 * 60)
self.assertEqual(regrtest.python_cmd, ('python', '-X', 'dev'))
def test_fast_ci_resource(self):
# it should be possible to override resources individually
args = ['--fast-ci', '-u-network']
- use_resources = sorted(cmdline.ALL_RESOURCES)
- use_resources.remove('cpu')
- use_resources.remove('network')
+ use_resources = dict.fromkeys(cmdline.ALL_RESOURCES)
+ del use_resources['cpu']
+ del use_resources['network']
self.check_ci_mode(args, use_resources)
def test_fast_ci_verbose(self):
args = ['--fast-ci', '--verbose']
- use_resources = sorted(cmdline.ALL_RESOURCES)
- use_resources.remove('cpu')
+ use_resources = dict.fromkeys(cmdline.ALL_RESOURCES)
+ del use_resources['cpu']
regrtest = self.check_ci_mode(args, use_resources,
output_on_failure=False)
self.assertEqual(regrtest.verbose, True)
def test_slow_ci(self):
args = ['--slow-ci']
- use_resources = sorted(cmdline.ALL_RESOURCES)
+ use_resources = dict.fromkeys(cmdline.ALL_RESOURCES)
regrtest = self.check_ci_mode(args, use_resources)
self.assertEqual(regrtest.timeout, 20 * 60)
def test_ci_no_randomize(self):
- all_resources = set(cmdline.ALL_RESOURCES)
+ use_resources = dict.fromkeys(cmdline.ALL_RESOURCES)
self.check_ci_mode(
- ["--slow-ci", "--no-randomize"], all_resources, randomize=False
+ ["--slow-ci", "--no-randomize"], use_resources, randomize=False
)
+ del use_resources['cpu']
self.check_ci_mode(
- ["--fast-ci", "--no-randomize"], all_resources - {'cpu'}, randomize=False
+ ["--fast-ci", "--no-randomize"], use_resources, randomize=False
)
def test_dont_add_python_opts(self):
format_resources = utils.format_resources
ALL_RESOURCES = utils.ALL_RESOURCES
self.assertEqual(
- format_resources(("network",)),
+ format_resources({"network": None}),
'resources (1): network')
self.assertEqual(
- format_resources(("audio", "decimal", "network")),
+ format_resources(dict.fromkeys(("audio", "decimal", "network"))),
'resources (3): audio,decimal,network')
self.assertEqual(
- format_resources(ALL_RESOURCES),
+ format_resources(dict.fromkeys(ALL_RESOURCES)),
'resources: all')
self.assertEqual(
- format_resources(tuple(name for name in ALL_RESOURCES
- if name != "cpu")),
+ format_resources({name: None for name in ALL_RESOURCES
+ if name != "cpu"}),
'resources: all,-cpu')
self.assertEqual(
- format_resources((*ALL_RESOURCES, "tzdata")),
+ format_resources({**dict.fromkeys(ALL_RESOURCES), "tzdata": None}),
'resources: all,tzdata')
def test_match_test(self):