summaryrefslogtreecommitdiffstats
path: root/Modules/_decimal
diff options
context:
space:
mode:
authorStefan Krah <skrah@bytereef.org>2020-06-08 17:33:12 (GMT)
committerGitHub <noreply@github.com>2020-06-08 17:33:12 (GMT)
commit951d680d56d8c32556437a86f6b42f221635b97f (patch)
tree9d8939b37ce1413784565c79b2d2461b2c57e19a /Modules/_decimal
parent0c59f440f4c9dca658e6b18db14b67b750e25a87 (diff)
downloadcpython-951d680d56d8c32556437a86f6b42f221635b97f.zip
cpython-951d680d56d8c32556437a86f6b42f221635b97f.tar.gz
cpython-951d680d56d8c32556437a86f6b42f221635b97f.tar.bz2
Add multicore support to deccheck.py. (GH-20731)
Diffstat (limited to 'Modules/_decimal')
-rw-r--r--Modules/_decimal/tests/deccheck.py131
1 files changed, 110 insertions, 21 deletions
diff --git a/Modules/_decimal/tests/deccheck.py b/Modules/_decimal/tests/deccheck.py
index 5cd5db5..5d9179e 100644
--- a/Modules/_decimal/tests/deccheck.py
+++ b/Modules/_decimal/tests/deccheck.py
@@ -29,9 +29,20 @@
# Usage: python deccheck.py [--short|--medium|--long|--all]
#
-import sys, random
+
+import sys
+import os
+import time
+import random
from copy import copy
from collections import defaultdict
+
+import argparse
+import subprocess
+from subprocess import PIPE, STDOUT
+from queue import Queue, Empty
+from threading import Thread, Event, Lock
+
from test.support import import_fresh_module
from randdec import randfloat, all_unary, all_binary, all_ternary
from randdec import unary_optarg, binary_optarg, ternary_optarg
@@ -1124,18 +1135,35 @@ def check_untested(funcdict, c_cls, p_cls):
funcdict['untested'] = tuple(sorted(intersect-tested))
- #for key in ('untested', 'c_only', 'p_only'):
- # s = 'Context' if c_cls == C.Context else 'Decimal'
- # print("\n%s %s:\n%s" % (s, key, funcdict[key]))
+ # for key in ('untested', 'c_only', 'p_only'):
+ # s = 'Context' if c_cls == C.Context else 'Decimal'
+ # print("\n%s %s:\n%s" % (s, key, funcdict[key]))
if __name__ == '__main__':
- import time
+ parser = argparse.ArgumentParser(prog="deccheck.py")
+
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument('--short', dest='time', action="store_const", const='short', default='short', help="short test (default)")
+ group.add_argument('--medium', dest='time', action="store_const", const='medium', default='short', help="medium test (reasonable run time)")
+ group.add_argument('--long', dest='time', action="store_const", const='long', default='short', help="long test (long run time)")
+ group.add_argument('--all', dest='time', action="store_const", const='all', default='short', help="all tests (excessive run time)")
+
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument('--single', dest='single', nargs=1, default=False, metavar="TEST", help="run a single test")
+ group.add_argument('--multicore', dest='multicore', action="store_true", default=False, help="use all available cores")
+
+ args = parser.parse_args()
+ assert args.single is False or args.multicore is False
+ if args.single:
+ args.single = args.single[0]
+
randseed = int(time.time())
random.seed(randseed)
+
# Set up the testspecs list. A testspec is simply a dictionary
# that determines the amount of different contexts that 'test_method'
# will generate.
@@ -1168,17 +1196,17 @@ if __name__ == '__main__':
{'prec': [34], 'expts': [(-6143, 6144)], 'clamp': 1, 'iter': None}
]
- if '--medium' in sys.argv:
+ if args.time == 'medium':
base['expts'].append(('rand', 'rand'))
# 5 random precisions
base['samples'] = 5
testspecs = [small] + ieee + [base]
- if '--long' in sys.argv:
+ elif args.time == 'long':
base['expts'].append(('rand', 'rand'))
# 10 random precisions
base['samples'] = 10
testspecs = [small] + ieee + [base]
- elif '--all' in sys.argv:
+ elif args.time == 'all':
base['expts'].append(('rand', 'rand'))
# All precisions in [1, 100]
base['samples'] = 100
@@ -1195,39 +1223,100 @@ if __name__ == '__main__':
small['expts'] = [(-prec, prec)]
testspecs = [small, rand_ieee, base]
+
check_untested(Functions, C.Decimal, P.Decimal)
check_untested(ContextFunctions, C.Context, P.Context)
- log("\n\nRandom seed: %d\n\n", randseed)
+ if args.multicore:
+ q = Queue()
+ elif args.single:
+ log("Random seed: %d", randseed)
+ else:
+ log("\n\nRandom seed: %d\n\n", randseed)
+
+
+ FOUND_METHOD = False
+ def do_single(method, f):
+ global FOUND_METHOD
+ if args.multicore:
+ q.put(method)
+ elif not args.single or args.single == method:
+ FOUND_METHOD = True
+ f()
# Decimal methods:
for method in Functions['unary'] + Functions['unary_ctx'] + \
Functions['unary_rnd_ctx']:
- test_method(method, testspecs, test_unary)
+ do_single(method, lambda: test_method(method, testspecs, test_unary))
for method in Functions['binary'] + Functions['binary_ctx']:
- test_method(method, testspecs, test_binary)
+ do_single(method, lambda: test_method(method, testspecs, test_binary))
for method in Functions['ternary'] + Functions['ternary_ctx']:
- test_method(method, testspecs, test_ternary)
+ name = '__powmod__' if method == '__pow__' else method
+ do_single(name, lambda: test_method(method, testspecs, test_ternary))
- test_method('__format__', testspecs, test_format)
- test_method('__round__', testspecs, test_round)
- test_method('from_float', testspecs, test_from_float)
- test_method('quantize', testspecs, test_quantize_api)
+ do_single('__format__', lambda: test_method('__format__', testspecs, test_format))
+ do_single('__round__', lambda: test_method('__round__', testspecs, test_round))
+ do_single('from_float', lambda: test_method('from_float', testspecs, test_from_float))
+ do_single('quantize_api', lambda: test_method('quantize', testspecs, test_quantize_api))
# Context methods:
for method in ContextFunctions['unary']:
- test_method(method, testspecs, test_unary)
+ do_single(method, lambda: test_method(method, testspecs, test_unary))
for method in ContextFunctions['binary']:
- test_method(method, testspecs, test_binary)
+ do_single(method, lambda: test_method(method, testspecs, test_binary))
for method in ContextFunctions['ternary']:
- test_method(method, testspecs, test_ternary)
+ name = 'context.powmod' if method == 'context.power' else method
+ do_single(name, lambda: test_method(method, testspecs, test_ternary))
+
+ do_single('context.create_decimal_from_float',
+ lambda: test_method('context.create_decimal_from_float',
+ testspecs, test_from_float))
+
+ if args.multicore:
+ error = Event()
+ write_lock = Lock()
- test_method('context.create_decimal_from_float', testspecs, test_from_float)
+ def write_output(out, returncode):
+ if returncode != 0:
+ error.set()
+
+ with write_lock:
+ sys.stdout.buffer.write(out + b"\n")
+ sys.stdout.buffer.flush()
+
+ def tfunc():
+ while not error.is_set():
+ try:
+ test = q.get(block=False, timeout=-1)
+ except Empty:
+ return
+ cmd = [sys.executable, "deccheck.py", "--%s" % args.time, "--single", test]
+ p = subprocess.Popen(cmd, stdout=PIPE, stderr=STDOUT)
+ out, _ = p.communicate()
+ write_output(out, p.returncode)
- sys.exit(EXIT_STATUS)
+ N = os.cpu_count()
+ t = N * [None]
+
+ for i in range(N):
+ t[i] = Thread(target=tfunc)
+ t[i].start()
+
+ for i in range(N):
+ t[i].join()
+
+ sys.exit(1 if error.is_set() else 0)
+
+ elif args.single:
+ if not FOUND_METHOD:
+ log("\nerror: cannot find method \"%s\"" % args.single)
+ EXIT_STATUS = 1
+ sys.exit(EXIT_STATUS)
+ else:
+ sys.exit(EXIT_STATUS)