mirror of
https://github.com/python/cpython.git
synced 2025-11-08 01:21:42 +00:00
gh-109162: libregrtest: remove WorkerJob class (#109204)
* Add attributes to Regrtest and RunTests: * gc_threshold * memory_limit * python_cmd * use_resources * Remove WorkerJob class. Add as_json() and from_json() methods to RunTests. A worker process now only uses RunTests for all parameters. * Add tests on support.set_memlimit() in test_support. Create _parse_memlimit() and also adds tests on it. * Remove 'ns' parameter from runtest.py.
This commit is contained in:
parent
24fa8f2046
commit
0c0f254230
7 changed files with 125 additions and 87 deletions
|
|
@ -177,6 +177,8 @@ def __init__(self, **kwargs) -> None:
|
||||||
self.worker_json = None
|
self.worker_json = None
|
||||||
self.start = None
|
self.start = None
|
||||||
self.timeout = None
|
self.timeout = None
|
||||||
|
self.memlimit = None
|
||||||
|
self.threshold = None
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,10 @@ def __init__(self, ns: Namespace):
|
||||||
self.hunt_refleak = None
|
self.hunt_refleak = None
|
||||||
self.test_dir: str | None = ns.testdir
|
self.test_dir: str | None = ns.testdir
|
||||||
self.junit_filename: str | None = ns.xmlpath
|
self.junit_filename: str | None = ns.xmlpath
|
||||||
|
self.memory_limit: str | None = ns.memlimit
|
||||||
|
self.gc_threshold: int | None = ns.threshold
|
||||||
|
self.use_resources: list[str] = ns.use_resources
|
||||||
|
self.python_cmd: list[str] | None = ns.python
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
self.tests = []
|
self.tests = []
|
||||||
|
|
@ -363,7 +367,7 @@ def _rerun_failed_tests(self, need_rerun, runtests: RunTests):
|
||||||
return runtests
|
return runtests
|
||||||
|
|
||||||
def rerun_failed_tests(self, need_rerun, runtests: RunTests):
|
def rerun_failed_tests(self, need_rerun, runtests: RunTests):
|
||||||
if self.ns.python:
|
if self.python_cmd:
|
||||||
# Temp patch for https://github.com/python/cpython/issues/94052
|
# Temp patch for https://github.com/python/cpython/issues/94052
|
||||||
self.log(
|
self.log(
|
||||||
"Re-running failed tests is not supported with --python "
|
"Re-running failed tests is not supported with --python "
|
||||||
|
|
@ -453,12 +457,12 @@ def run_test(self, test_name: str, runtests: RunTests, tracer):
|
||||||
if tracer is not None:
|
if tracer is not None:
|
||||||
# If we're tracing code coverage, then we don't exit with status
|
# If we're tracing code coverage, then we don't exit with status
|
||||||
# if on a false return value from main.
|
# if on a false return value from main.
|
||||||
cmd = ('result = run_single_test(test_name, runtests, self.ns)')
|
cmd = ('result = run_single_test(test_name, runtests)')
|
||||||
ns = dict(locals())
|
ns = dict(locals())
|
||||||
tracer.runctx(cmd, globals=globals(), locals=ns)
|
tracer.runctx(cmd, globals=globals(), locals=ns)
|
||||||
result = ns['result']
|
result = ns['result']
|
||||||
else:
|
else:
|
||||||
result = run_single_test(test_name, runtests, self.ns)
|
result = run_single_test(test_name, runtests)
|
||||||
|
|
||||||
self.accumulate_result(result)
|
self.accumulate_result(result)
|
||||||
|
|
||||||
|
|
@ -876,9 +880,14 @@ def action_run_tests(self):
|
||||||
quiet=self.quiet,
|
quiet=self.quiet,
|
||||||
hunt_refleak=self.hunt_refleak,
|
hunt_refleak=self.hunt_refleak,
|
||||||
test_dir=self.test_dir,
|
test_dir=self.test_dir,
|
||||||
junit_filename=self.junit_filename)
|
junit_filename=self.junit_filename,
|
||||||
|
memory_limit=self.memory_limit,
|
||||||
|
gc_threshold=self.gc_threshold,
|
||||||
|
use_resources=self.use_resources,
|
||||||
|
python_cmd=self.python_cmd,
|
||||||
|
)
|
||||||
|
|
||||||
setup_tests(runtests, self.ns)
|
setup_tests(runtests)
|
||||||
|
|
||||||
tracer = self.run_tests(runtests)
|
tracer = self.run_tests(runtests)
|
||||||
self.display_result(runtests)
|
self.display_result(runtests)
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,18 @@
|
||||||
import gc
|
import gc
|
||||||
import importlib
|
import importlib
|
||||||
import io
|
import io
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import unittest
|
import unittest
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import TestStats
|
from test.support import TestStats
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
from test.support import threading_helper
|
from test.support import threading_helper
|
||||||
from test.libregrtest.cmdline import Namespace
|
|
||||||
from test.libregrtest.save_env import saved_test_environment
|
from test.libregrtest.save_env import saved_test_environment
|
||||||
from test.libregrtest.utils import clear_caches, format_duration, print_warning
|
from test.libregrtest.utils import clear_caches, format_duration, print_warning
|
||||||
|
|
||||||
|
|
@ -230,6 +231,10 @@ class RunTests:
|
||||||
hunt_refleak: HuntRefleak | None = None
|
hunt_refleak: HuntRefleak | None = None
|
||||||
test_dir: str | None = None
|
test_dir: str | None = None
|
||||||
junit_filename: str | None = None
|
junit_filename: str | None = None
|
||||||
|
memory_limit: str | None = None
|
||||||
|
gc_threshold: int | None = None
|
||||||
|
use_resources: list[str] = None
|
||||||
|
python_cmd: list[str] | None = None
|
||||||
|
|
||||||
def copy(self, **override):
|
def copy(self, **override):
|
||||||
state = dataclasses.asdict(self)
|
state = dataclasses.asdict(self)
|
||||||
|
|
@ -249,11 +254,32 @@ def iter_tests(self):
|
||||||
else:
|
else:
|
||||||
yield from self.tests
|
yield from self.tests
|
||||||
|
|
||||||
|
def as_json(self):
|
||||||
|
return json.dumps(self, cls=_EncodeRunTests)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json_dict(json_dict):
|
def from_json(worker_json):
|
||||||
if json_dict['hunt_refleak']:
|
return json.loads(worker_json, object_hook=_decode_runtests)
|
||||||
json_dict['hunt_refleak'] = HuntRefleak(**json_dict['hunt_refleak'])
|
|
||||||
return RunTests(**json_dict)
|
|
||||||
|
class _EncodeRunTests(json.JSONEncoder):
|
||||||
|
def default(self, o: Any) -> dict[str, Any]:
|
||||||
|
if isinstance(o, RunTests):
|
||||||
|
result = dataclasses.asdict(o)
|
||||||
|
result["__runtests__"] = True
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
return super().default(o)
|
||||||
|
|
||||||
|
|
||||||
|
def _decode_runtests(data: dict[str, Any]) -> RunTests | dict[str, Any]:
|
||||||
|
if "__runtests__" in data:
|
||||||
|
data.pop('__runtests__')
|
||||||
|
if data['hunt_refleak']:
|
||||||
|
data['hunt_refleak'] = HuntRefleak(**data['hunt_refleak'])
|
||||||
|
return RunTests(**data)
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
# Minimum duration of a test to display its duration or to mention that
|
# Minimum duration of a test to display its duration or to mention that
|
||||||
|
|
@ -320,7 +346,7 @@ def abs_module_name(test_name: str, test_dir: str | None) -> str:
|
||||||
return 'test.' + test_name
|
return 'test.' + test_name
|
||||||
|
|
||||||
|
|
||||||
def setup_support(runtests: RunTests, ns: Namespace):
|
def setup_support(runtests: RunTests):
|
||||||
support.PGO = runtests.pgo
|
support.PGO = runtests.pgo
|
||||||
support.PGO_EXTENDED = runtests.pgo_extended
|
support.PGO_EXTENDED = runtests.pgo_extended
|
||||||
support.set_match_tests(runtests.match_tests, runtests.ignore_tests)
|
support.set_match_tests(runtests.match_tests, runtests.ignore_tests)
|
||||||
|
|
@ -332,7 +358,7 @@ def setup_support(runtests: RunTests, ns: Namespace):
|
||||||
support.junit_xml_list = None
|
support.junit_xml_list = None
|
||||||
|
|
||||||
|
|
||||||
def _runtest(result: TestResult, runtests: RunTests, ns: Namespace) -> None:
|
def _runtest(result: TestResult, runtests: RunTests) -> None:
|
||||||
# Capture stdout and stderr, set faulthandler timeout,
|
# Capture stdout and stderr, set faulthandler timeout,
|
||||||
# and create JUnit XML report.
|
# and create JUnit XML report.
|
||||||
verbose = runtests.verbose
|
verbose = runtests.verbose
|
||||||
|
|
@ -346,7 +372,7 @@ def _runtest(result: TestResult, runtests: RunTests, ns: Namespace) -> None:
|
||||||
faulthandler.dump_traceback_later(timeout, exit=True)
|
faulthandler.dump_traceback_later(timeout, exit=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
setup_support(runtests, ns)
|
setup_support(runtests)
|
||||||
|
|
||||||
if output_on_failure:
|
if output_on_failure:
|
||||||
support.verbose = True
|
support.verbose = True
|
||||||
|
|
@ -366,7 +392,7 @@ def _runtest(result: TestResult, runtests: RunTests, ns: Namespace) -> None:
|
||||||
# warnings will be written to sys.stderr below.
|
# warnings will be written to sys.stderr below.
|
||||||
print_warning.orig_stderr = stream
|
print_warning.orig_stderr = stream
|
||||||
|
|
||||||
_runtest_env_changed_exc(result, runtests, ns, display_failure=False)
|
_runtest_env_changed_exc(result, runtests, display_failure=False)
|
||||||
# Ignore output if the test passed successfully
|
# Ignore output if the test passed successfully
|
||||||
if result.state != State.PASSED:
|
if result.state != State.PASSED:
|
||||||
output = stream.getvalue()
|
output = stream.getvalue()
|
||||||
|
|
@ -381,7 +407,7 @@ def _runtest(result: TestResult, runtests: RunTests, ns: Namespace) -> None:
|
||||||
else:
|
else:
|
||||||
# Tell tests to be moderately quiet
|
# Tell tests to be moderately quiet
|
||||||
support.verbose = verbose
|
support.verbose = verbose
|
||||||
_runtest_env_changed_exc(result, runtests, ns,
|
_runtest_env_changed_exc(result, runtests,
|
||||||
display_failure=not verbose)
|
display_failure=not verbose)
|
||||||
|
|
||||||
xml_list = support.junit_xml_list
|
xml_list = support.junit_xml_list
|
||||||
|
|
@ -395,10 +421,9 @@ def _runtest(result: TestResult, runtests: RunTests, ns: Namespace) -> None:
|
||||||
support.junit_xml_list = None
|
support.junit_xml_list = None
|
||||||
|
|
||||||
|
|
||||||
def run_single_test(test_name: str, runtests: RunTests, ns: Namespace) -> TestResult:
|
def run_single_test(test_name: str, runtests: RunTests) -> TestResult:
|
||||||
"""Run a single test.
|
"""Run a single test.
|
||||||
|
|
||||||
ns -- regrtest namespace of options
|
|
||||||
test_name -- the name of the test
|
test_name -- the name of the test
|
||||||
|
|
||||||
Returns a TestResult.
|
Returns a TestResult.
|
||||||
|
|
@ -410,7 +435,7 @@ def run_single_test(test_name: str, runtests: RunTests, ns: Namespace) -> TestRe
|
||||||
result = TestResult(test_name)
|
result = TestResult(test_name)
|
||||||
pgo = runtests.pgo
|
pgo = runtests.pgo
|
||||||
try:
|
try:
|
||||||
_runtest(result, runtests, ns)
|
_runtest(result, runtests)
|
||||||
except:
|
except:
|
||||||
if not pgo:
|
if not pgo:
|
||||||
msg = traceback.format_exc()
|
msg = traceback.format_exc()
|
||||||
|
|
@ -472,7 +497,7 @@ def regrtest_runner(result: TestResult, test_func, runtests: RunTests) -> None:
|
||||||
FOUND_GARBAGE = []
|
FOUND_GARBAGE = []
|
||||||
|
|
||||||
|
|
||||||
def _load_run_test(result: TestResult, runtests: RunTests, ns: Namespace) -> None:
|
def _load_run_test(result: TestResult, runtests: RunTests) -> None:
|
||||||
# Load the test function, run the test function.
|
# Load the test function, run the test function.
|
||||||
module_name = abs_module_name(result.test_name, runtests.test_dir)
|
module_name = abs_module_name(result.test_name, runtests.test_dir)
|
||||||
|
|
||||||
|
|
@ -513,7 +538,6 @@ def test_func():
|
||||||
|
|
||||||
|
|
||||||
def _runtest_env_changed_exc(result: TestResult, runtests: RunTests,
|
def _runtest_env_changed_exc(result: TestResult, runtests: RunTests,
|
||||||
ns: Namespace,
|
|
||||||
display_failure: bool = True) -> None:
|
display_failure: bool = True) -> None:
|
||||||
# Detect environment changes, handle exceptions.
|
# Detect environment changes, handle exceptions.
|
||||||
|
|
||||||
|
|
@ -532,7 +556,7 @@ def _runtest_env_changed_exc(result: TestResult, runtests: RunTests,
|
||||||
support.gc_collect()
|
support.gc_collect()
|
||||||
|
|
||||||
with save_env(test_name, runtests):
|
with save_env(test_name, runtests):
|
||||||
_load_run_test(result, runtests, ns)
|
_load_run_test(result, runtests)
|
||||||
except support.ResourceDenied as msg:
|
except support.ResourceDenied as msg:
|
||||||
if not quiet and not pgo:
|
if not quiet and not pgo:
|
||||||
print(f"{test_name} skipped -- {msg}", flush=True)
|
print(f"{test_name} skipped -- {msg}", flush=True)
|
||||||
|
|
|
||||||
|
|
@ -47,49 +47,16 @@
|
||||||
@dataclasses.dataclass(slots=True)
|
@dataclasses.dataclass(slots=True)
|
||||||
class WorkerJob:
|
class WorkerJob:
|
||||||
runtests: RunTests
|
runtests: RunTests
|
||||||
namespace: Namespace
|
|
||||||
|
|
||||||
|
|
||||||
class _EncodeWorkerJob(json.JSONEncoder):
|
def create_worker_process(runtests: RunTests,
|
||||||
def default(self, o: Any) -> dict[str, Any]:
|
|
||||||
match o:
|
|
||||||
case WorkerJob():
|
|
||||||
result = dataclasses.asdict(o)
|
|
||||||
result["__worker_job__"] = True
|
|
||||||
return result
|
|
||||||
case Namespace():
|
|
||||||
result = vars(o)
|
|
||||||
result["__namespace__"] = True
|
|
||||||
return result
|
|
||||||
case _:
|
|
||||||
return super().default(o)
|
|
||||||
|
|
||||||
|
|
||||||
def _decode_worker_job(d: dict[str, Any]) -> WorkerJob | dict[str, Any]:
|
|
||||||
if "__worker_job__" in d:
|
|
||||||
d.pop('__worker_job__')
|
|
||||||
d['runtests'] = RunTests.from_json_dict(d['runtests'])
|
|
||||||
return WorkerJob(**d)
|
|
||||||
if "__namespace__" in d:
|
|
||||||
d.pop('__namespace__')
|
|
||||||
return Namespace(**d)
|
|
||||||
else:
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_worker_json(worker_json: str) -> tuple[Namespace, str]:
|
|
||||||
return json.loads(worker_json, object_hook=_decode_worker_job)
|
|
||||||
|
|
||||||
|
|
||||||
def create_worker_process(worker_job: WorkerJob,
|
|
||||||
output_file: TextIO,
|
output_file: TextIO,
|
||||||
tmp_dir: str | None = None) -> subprocess.Popen:
|
tmp_dir: str | None = None) -> subprocess.Popen:
|
||||||
ns = worker_job.namespace
|
python_cmd = runtests.python_cmd
|
||||||
python = ns.python
|
worker_json = runtests.as_json()
|
||||||
worker_json = json.dumps(worker_job, cls=_EncodeWorkerJob)
|
|
||||||
|
|
||||||
if python is not None:
|
if python_cmd is not None:
|
||||||
executable = python
|
executable = python_cmd
|
||||||
else:
|
else:
|
||||||
executable = [sys.executable]
|
executable = [sys.executable]
|
||||||
cmd = [*executable, *support.args_from_interpreter_flags(),
|
cmd = [*executable, *support.args_from_interpreter_flags(),
|
||||||
|
|
@ -121,14 +88,12 @@ def create_worker_process(worker_job: WorkerJob,
|
||||||
|
|
||||||
|
|
||||||
def worker_process(worker_json: str) -> NoReturn:
|
def worker_process(worker_json: str) -> NoReturn:
|
||||||
worker_job = _parse_worker_json(worker_json)
|
runtests = RunTests.from_json(worker_json)
|
||||||
runtests = worker_job.runtests
|
|
||||||
ns = worker_job.namespace
|
|
||||||
test_name = runtests.tests[0]
|
test_name = runtests.tests[0]
|
||||||
match_tests: FilterTuple | None = runtests.match_tests
|
match_tests: FilterTuple | None = runtests.match_tests
|
||||||
|
|
||||||
setup_test_dir(runtests.test_dir)
|
setup_test_dir(runtests.test_dir)
|
||||||
setup_tests(runtests, ns)
|
setup_tests(runtests)
|
||||||
|
|
||||||
if runtests.rerun:
|
if runtests.rerun:
|
||||||
if match_tests:
|
if match_tests:
|
||||||
|
|
@ -137,7 +102,7 @@ def worker_process(worker_json: str) -> NoReturn:
|
||||||
else:
|
else:
|
||||||
print(f"Re-running {test_name} in verbose mode", flush=True)
|
print(f"Re-running {test_name} in verbose mode", flush=True)
|
||||||
|
|
||||||
result = run_single_test(test_name, runtests, ns)
|
result = run_single_test(test_name, runtests)
|
||||||
print() # Force a newline (just in case)
|
print() # Force a newline (just in case)
|
||||||
|
|
||||||
# Serialize TestResult as dict in JSON
|
# Serialize TestResult as dict in JSON
|
||||||
|
|
@ -330,9 +295,6 @@ def _runtest(self, test_name: str) -> MultiprocessResult:
|
||||||
if match_tests:
|
if match_tests:
|
||||||
kwargs['match_tests'] = match_tests
|
kwargs['match_tests'] = match_tests
|
||||||
worker_runtests = self.runtests.copy(tests=tests, **kwargs)
|
worker_runtests = self.runtests.copy(tests=tests, **kwargs)
|
||||||
worker_job = WorkerJob(
|
|
||||||
worker_runtests,
|
|
||||||
namespace=self.ns)
|
|
||||||
|
|
||||||
# gh-94026: Write stdout+stderr to a tempfile as workaround for
|
# gh-94026: Write stdout+stderr to a tempfile as workaround for
|
||||||
# non-blocking pipes on Emscripten with NodeJS.
|
# non-blocking pipes on Emscripten with NodeJS.
|
||||||
|
|
@ -347,12 +309,12 @@ def _runtest(self, test_name: str) -> MultiprocessResult:
|
||||||
tmp_dir = tempfile.mkdtemp(prefix="test_python_")
|
tmp_dir = tempfile.mkdtemp(prefix="test_python_")
|
||||||
tmp_dir = os.path.abspath(tmp_dir)
|
tmp_dir = os.path.abspath(tmp_dir)
|
||||||
try:
|
try:
|
||||||
retcode = self._run_process(worker_job, stdout_file, tmp_dir)
|
retcode = self._run_process(worker_runtests, stdout_file, tmp_dir)
|
||||||
finally:
|
finally:
|
||||||
tmp_files = os.listdir(tmp_dir)
|
tmp_files = os.listdir(tmp_dir)
|
||||||
os_helper.rmtree(tmp_dir)
|
os_helper.rmtree(tmp_dir)
|
||||||
else:
|
else:
|
||||||
retcode = self._run_process(worker_job, stdout_file)
|
retcode = self._run_process(worker_runtests, stdout_file)
|
||||||
tmp_files = ()
|
tmp_files = ()
|
||||||
stdout_file.seek(0)
|
stdout_file.seek(0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ def setup_test_dir(testdir: str | None) -> None:
|
||||||
sys.path.insert(0, os.path.abspath(testdir))
|
sys.path.insert(0, os.path.abspath(testdir))
|
||||||
|
|
||||||
|
|
||||||
def setup_tests(runtests, ns):
|
def setup_tests(runtests):
|
||||||
try:
|
try:
|
||||||
stderr_fd = sys.__stderr__.fileno()
|
stderr_fd = sys.__stderr__.fileno()
|
||||||
except (ValueError, AttributeError):
|
except (ValueError, AttributeError):
|
||||||
|
|
@ -71,15 +71,15 @@ def setup_tests(runtests, ns):
|
||||||
if runtests.hunt_refleak:
|
if runtests.hunt_refleak:
|
||||||
unittest.BaseTestSuite._cleanup = False
|
unittest.BaseTestSuite._cleanup = False
|
||||||
|
|
||||||
if ns.memlimit is not None:
|
if runtests.memory_limit is not None:
|
||||||
support.set_memlimit(ns.memlimit)
|
support.set_memlimit(runtests.memory_limit)
|
||||||
|
|
||||||
if ns.threshold is not None:
|
if runtests.gc_threshold is not None:
|
||||||
gc.set_threshold(ns.threshold)
|
gc.set_threshold(runtests.gc_threshold)
|
||||||
|
|
||||||
support.suppress_msvcrt_asserts(runtests.verbose and runtests.verbose >= 2)
|
support.suppress_msvcrt_asserts(runtests.verbose and runtests.verbose >= 2)
|
||||||
|
|
||||||
support.use_resources = ns.use_resources
|
support.use_resources = runtests.use_resources
|
||||||
|
|
||||||
if hasattr(sys, 'addaudithook'):
|
if hasattr(sys, 'addaudithook'):
|
||||||
# Add an auditing hook for all tests to ensure PySys_Audit is tested
|
# Add an auditing hook for all tests to ensure PySys_Audit is tested
|
||||||
|
|
|
||||||
|
|
@ -878,27 +878,31 @@ def inner(*args, **kwds):
|
||||||
|
|
||||||
MAX_Py_ssize_t = sys.maxsize
|
MAX_Py_ssize_t = sys.maxsize
|
||||||
|
|
||||||
def set_memlimit(limit):
|
def _parse_memlimit(limit: str) -> int:
|
||||||
global max_memuse
|
|
||||||
global real_max_memuse
|
|
||||||
sizes = {
|
sizes = {
|
||||||
'k': 1024,
|
'k': 1024,
|
||||||
'm': _1M,
|
'm': _1M,
|
||||||
'g': _1G,
|
'g': _1G,
|
||||||
't': 1024*_1G,
|
't': 1024*_1G,
|
||||||
}
|
}
|
||||||
m = re.match(r'(\d+(\.\d+)?) (K|M|G|T)b?$', limit,
|
m = re.match(r'(\d+(?:\.\d+)?) (K|M|G|T)b?$', limit,
|
||||||
re.IGNORECASE | re.VERBOSE)
|
re.IGNORECASE | re.VERBOSE)
|
||||||
if m is None:
|
if m is None:
|
||||||
raise ValueError('Invalid memory limit %r' % (limit,))
|
raise ValueError(f'Invalid memory limit: {limit!r}')
|
||||||
memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()])
|
return int(float(m.group(1)) * sizes[m.group(2).lower()])
|
||||||
real_max_memuse = memlimit
|
|
||||||
if memlimit > MAX_Py_ssize_t:
|
def set_memlimit(limit: str) -> None:
|
||||||
memlimit = MAX_Py_ssize_t
|
global max_memuse
|
||||||
|
global real_max_memuse
|
||||||
|
memlimit = _parse_memlimit(limit)
|
||||||
if memlimit < _2G - 1:
|
if memlimit < _2G - 1:
|
||||||
raise ValueError('Memory limit %r too low to be useful' % (limit,))
|
raise ValueError('Memory limit {limit!r} too low to be useful')
|
||||||
|
|
||||||
|
real_max_memuse = memlimit
|
||||||
|
memlimit = min(memlimit, MAX_Py_ssize_t)
|
||||||
max_memuse = memlimit
|
max_memuse = memlimit
|
||||||
|
|
||||||
|
|
||||||
class _MemoryWatchdog:
|
class _MemoryWatchdog:
|
||||||
"""An object which periodically watches the process' memory consumption
|
"""An object which periodically watches the process' memory consumption
|
||||||
and prints it out.
|
and prints it out.
|
||||||
|
|
|
||||||
|
|
@ -760,7 +760,45 @@ def recursive_function(depth):
|
||||||
else:
|
else:
|
||||||
self.fail("RecursionError was not raised")
|
self.fail("RecursionError was not raised")
|
||||||
|
|
||||||
#self.assertEqual(available, 2)
|
def test_parse_memlimit(self):
|
||||||
|
parse = support._parse_memlimit
|
||||||
|
KiB = 1024
|
||||||
|
MiB = KiB * 1024
|
||||||
|
GiB = MiB * 1024
|
||||||
|
TiB = GiB * 1024
|
||||||
|
self.assertEqual(parse('0k'), 0)
|
||||||
|
self.assertEqual(parse('3k'), 3 * KiB)
|
||||||
|
self.assertEqual(parse('2.4m'), int(2.4 * MiB))
|
||||||
|
self.assertEqual(parse('4g'), int(4 * GiB))
|
||||||
|
self.assertEqual(parse('1t'), TiB)
|
||||||
|
|
||||||
|
for limit in ('', '3', '3.5.10k', '10x'):
|
||||||
|
with self.subTest(limit=limit):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parse(limit)
|
||||||
|
|
||||||
|
def test_set_memlimit(self):
|
||||||
|
_4GiB = 4 * 1024 ** 3
|
||||||
|
TiB = 1024 ** 4
|
||||||
|
old_max_memuse = support.max_memuse
|
||||||
|
old_real_max_memuse = support.real_max_memuse
|
||||||
|
try:
|
||||||
|
if sys.maxsize > 2**32:
|
||||||
|
support.set_memlimit('4g')
|
||||||
|
self.assertEqual(support.max_memuse, _4GiB)
|
||||||
|
self.assertEqual(support.real_max_memuse, _4GiB)
|
||||||
|
|
||||||
|
big = 2**100 // TiB
|
||||||
|
support.set_memlimit(f'{big}t')
|
||||||
|
self.assertEqual(support.max_memuse, sys.maxsize)
|
||||||
|
self.assertEqual(support.real_max_memuse, big * TiB)
|
||||||
|
else:
|
||||||
|
support.set_memlimit('4g')
|
||||||
|
self.assertEqual(support.max_memuse, sys.maxsize)
|
||||||
|
self.assertEqual(support.real_max_memuse, _4GiB)
|
||||||
|
finally:
|
||||||
|
support.max_memuse = old_max_memuse
|
||||||
|
support.real_max_memuse = old_real_max_memuse
|
||||||
|
|
||||||
# XXX -follows a list of untested API
|
# XXX -follows a list of untested API
|
||||||
# make_legacy_pyc
|
# make_legacy_pyc
|
||||||
|
|
@ -773,7 +811,6 @@ def recursive_function(depth):
|
||||||
# EnvironmentVarGuard
|
# EnvironmentVarGuard
|
||||||
# transient_internet
|
# transient_internet
|
||||||
# run_with_locale
|
# run_with_locale
|
||||||
# set_memlimit
|
|
||||||
# bigmemtest
|
# bigmemtest
|
||||||
# precisionbigmemtest
|
# precisionbigmemtest
|
||||||
# bigaddrspacetest
|
# bigaddrspacetest
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue