| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | # | 
					
						
							|  |  |  | # Copyright (c) 2008-2012 Stefan Krah. All rights reserved. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  | # modification, are permitted provided that the following conditions | 
					
						
							|  |  |  | # are met: | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # 1. Redistributions of source code must retain the above copyright | 
					
						
							|  |  |  | #    notice, this list of conditions and the following disclaimer. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # 2. Redistributions in binary form must reproduce the above copyright | 
					
						
							|  |  |  | #    notice, this list of conditions and the following disclaimer in the | 
					
						
							|  |  |  | #    documentation and/or other materials provided with the distribution. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND | 
					
						
							|  |  |  | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
					
						
							|  |  |  | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
					
						
							|  |  |  | # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | 
					
						
							|  |  |  | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
					
						
							|  |  |  | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
					
						
							|  |  |  | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
					
						
							|  |  |  | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
					
						
							|  |  |  | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
					
						
							|  |  |  | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
					
						
							|  |  |  | # SUCH DAMAGE. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Usage: python deccheck.py [--short|--medium|--long|--all] | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-11 21:14:51 +02:00
										 |  |  | import random | 
					
						
							|  |  |  | import time | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RANDSEED = int(time.time()) | 
					
						
							|  |  |  | random.seed(RANDSEED) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  | import sys | 
					
						
							|  |  |  | import os | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | from copy import copy | 
					
						
							|  |  |  | from collections import defaultdict | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import argparse | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | from subprocess import PIPE, STDOUT | 
					
						
							|  |  |  | from queue import Queue, Empty | 
					
						
							|  |  |  | from threading import Thread, Event, Lock | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-01 13:49:46 +00:00
										 |  |  | from test.support.import_helper import import_fresh_module | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | from randdec import randfloat, all_unary, all_binary, all_ternary | 
					
						
							| 
									
										
										
										
											2012-12-15 22:33:33 +01:00
										 |  |  | from randdec import unary_optarg, binary_optarg, ternary_optarg | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | from formathelper import rand_format, rand_locale | 
					
						
							| 
									
										
										
										
											2014-09-10 17:58:15 +02:00
										 |  |  | from _pydecimal import _dec_from_triple | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-21 17:27:54 +01:00
										 |  |  | C = import_fresh_module('decimal', fresh=['_decimal']) | 
					
						
							|  |  |  | P = import_fresh_module('decimal', blocked=['_decimal']) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | EXIT_STATUS = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Contains all categories of Decimal methods. | 
					
						
							|  |  |  | Functions = { | 
					
						
							|  |  |  |     # Plain unary: | 
					
						
							|  |  |  |     'unary': ( | 
					
						
							|  |  |  |         '__abs__', '__bool__', '__ceil__', '__complex__', '__copy__', | 
					
						
							|  |  |  |         '__floor__', '__float__', '__hash__', '__int__', '__neg__', | 
					
						
							|  |  |  |         '__pos__', '__reduce__', '__repr__', '__str__', '__trunc__', | 
					
						
							| 
									
										
										
										
											2015-12-28 23:02:02 +01:00
										 |  |  |         'adjusted', 'as_integer_ratio', 'as_tuple', 'canonical', 'conjugate', | 
					
						
							|  |  |  |         'copy_abs', 'copy_negate', 'is_canonical', 'is_finite', 'is_infinite', | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         'is_nan', 'is_qnan', 'is_signed', 'is_snan', 'is_zero', 'radix' | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     # Unary with optional context: | 
					
						
							|  |  |  |     'unary_ctx': ( | 
					
						
							|  |  |  |         'exp', 'is_normal', 'is_subnormal', 'ln', 'log10', 'logb', | 
					
						
							|  |  |  |         'logical_invert', 'next_minus', 'next_plus', 'normalize', | 
					
						
							|  |  |  |         'number_class', 'sqrt', 'to_eng_string' | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     # Unary with optional rounding mode and context: | 
					
						
							|  |  |  |     'unary_rnd_ctx': ('to_integral', 'to_integral_exact', 'to_integral_value'), | 
					
						
							|  |  |  |     # Plain binary: | 
					
						
							|  |  |  |     'binary': ( | 
					
						
							|  |  |  |         '__add__', '__divmod__', '__eq__', '__floordiv__', '__ge__', '__gt__', | 
					
						
							|  |  |  |         '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__pow__', | 
					
						
							|  |  |  |         '__radd__', '__rdivmod__', '__rfloordiv__', '__rmod__', '__rmul__', | 
					
						
							|  |  |  |         '__rpow__', '__rsub__', '__rtruediv__', '__sub__', '__truediv__', | 
					
						
							|  |  |  |         'compare_total', 'compare_total_mag', 'copy_sign', 'quantize', | 
					
						
							|  |  |  |         'same_quantum' | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     # Binary with optional context: | 
					
						
							|  |  |  |     'binary_ctx': ( | 
					
						
							|  |  |  |         'compare', 'compare_signal', 'logical_and', 'logical_or', 'logical_xor', | 
					
						
							|  |  |  |         'max', 'max_mag', 'min', 'min_mag', 'next_toward', 'remainder_near', | 
					
						
							|  |  |  |         'rotate', 'scaleb', 'shift' | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     # Plain ternary: | 
					
						
							|  |  |  |     'ternary': ('__pow__',), | 
					
						
							|  |  |  |     # Ternary with optional context: | 
					
						
							|  |  |  |     'ternary_ctx': ('fma',), | 
					
						
							|  |  |  |     # Special: | 
					
						
							|  |  |  |     'special': ('__format__', '__reduce_ex__', '__round__', 'from_float', | 
					
						
							|  |  |  |                 'quantize'), | 
					
						
							|  |  |  |     # Properties: | 
					
						
							|  |  |  |     'property': ('real', 'imag') | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Contains all categories of Context methods. The n-ary classification | 
					
						
							|  |  |  | # applies to the number of Decimal arguments. | 
					
						
							|  |  |  | ContextFunctions = { | 
					
						
							|  |  |  |     # Plain nullary: | 
					
						
							|  |  |  |     'nullary': ('context.__hash__', 'context.__reduce__', 'context.radix'), | 
					
						
							|  |  |  |     # Plain unary: | 
					
						
							|  |  |  |     'unary': ('context.abs', 'context.canonical', 'context.copy_abs', | 
					
						
							|  |  |  |               'context.copy_decimal', 'context.copy_negate', | 
					
						
							|  |  |  |               'context.create_decimal', 'context.exp', 'context.is_canonical', | 
					
						
							|  |  |  |               'context.is_finite', 'context.is_infinite', 'context.is_nan', | 
					
						
							|  |  |  |               'context.is_normal', 'context.is_qnan', 'context.is_signed', | 
					
						
							|  |  |  |               'context.is_snan', 'context.is_subnormal', 'context.is_zero', | 
					
						
							|  |  |  |               'context.ln', 'context.log10', 'context.logb', | 
					
						
							|  |  |  |               'context.logical_invert', 'context.minus', 'context.next_minus', | 
					
						
							|  |  |  |               'context.next_plus', 'context.normalize', 'context.number_class', | 
					
						
							|  |  |  |               'context.plus', 'context.sqrt', 'context.to_eng_string', | 
					
						
							|  |  |  |               'context.to_integral', 'context.to_integral_exact', | 
					
						
							|  |  |  |               'context.to_integral_value', 'context.to_sci_string' | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     # Plain binary: | 
					
						
							|  |  |  |     'binary': ('context.add', 'context.compare', 'context.compare_signal', | 
					
						
							|  |  |  |                'context.compare_total', 'context.compare_total_mag', | 
					
						
							|  |  |  |                'context.copy_sign', 'context.divide', 'context.divide_int', | 
					
						
							|  |  |  |                'context.divmod', 'context.logical_and', 'context.logical_or', | 
					
						
							|  |  |  |                'context.logical_xor', 'context.max', 'context.max_mag', | 
					
						
							|  |  |  |                'context.min', 'context.min_mag', 'context.multiply', | 
					
						
							|  |  |  |                'context.next_toward', 'context.power', 'context.quantize', | 
					
						
							|  |  |  |                'context.remainder', 'context.remainder_near', 'context.rotate', | 
					
						
							|  |  |  |                'context.same_quantum', 'context.scaleb', 'context.shift', | 
					
						
							|  |  |  |                'context.subtract' | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     # Plain ternary: | 
					
						
							|  |  |  |     'ternary': ('context.fma', 'context.power'), | 
					
						
							|  |  |  |     # Special: | 
					
						
							|  |  |  |     'special': ('context.__reduce_ex__', 'context.create_decimal_from_float') | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  | # Functions that set no context flags but whose result can differ depending | 
					
						
							|  |  |  | # on prec, Emin and Emax. | 
					
						
							|  |  |  | MaxContextSkip = ['is_normal', 'is_subnormal', 'logical_invert', 'next_minus', | 
					
						
							|  |  |  |                   'next_plus', 'number_class', 'logical_and', 'logical_or', | 
					
						
							|  |  |  |                   'logical_xor', 'next_toward', 'rotate', 'shift'] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | # Functions that require a restricted exponent range for reasonable runtimes. | 
					
						
							|  |  |  | UnaryRestricted = [ | 
					
						
							| 
									
										
										
										
											2015-03-12 21:56:08 +02:00
										 |  |  |   '__ceil__', '__floor__', '__int__', '__trunc__', | 
					
						
							| 
									
										
										
										
											2015-12-28 23:02:02 +01:00
										 |  |  |   'as_integer_ratio', 'to_integral', 'to_integral_value' | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BinaryRestricted = ['__round__'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TernaryRestricted = ['__pow__', 'context.power'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | #                            Unified Context | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Translate symbols. | 
					
						
							|  |  |  | CondMap = { | 
					
						
							|  |  |  |         C.Clamped:             P.Clamped, | 
					
						
							|  |  |  |         C.ConversionSyntax:    P.ConversionSyntax, | 
					
						
							|  |  |  |         C.DivisionByZero:      P.DivisionByZero, | 
					
						
							|  |  |  |         C.DivisionImpossible:  P.InvalidOperation, | 
					
						
							|  |  |  |         C.DivisionUndefined:   P.DivisionUndefined, | 
					
						
							|  |  |  |         C.Inexact:             P.Inexact, | 
					
						
							|  |  |  |         C.InvalidContext:      P.InvalidContext, | 
					
						
							|  |  |  |         C.InvalidOperation:    P.InvalidOperation, | 
					
						
							|  |  |  |         C.Overflow:            P.Overflow, | 
					
						
							|  |  |  |         C.Rounded:             P.Rounded, | 
					
						
							|  |  |  |         C.Subnormal:           P.Subnormal, | 
					
						
							|  |  |  |         C.Underflow:           P.Underflow, | 
					
						
							|  |  |  |         C.FloatOperation:      P.FloatOperation, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-16 12:58:59 +01:00
										 |  |  | RoundModes = [C.ROUND_UP, C.ROUND_DOWN, C.ROUND_CEILING, C.ROUND_FLOOR, | 
					
						
							|  |  |  |               C.ROUND_HALF_UP, C.ROUND_HALF_DOWN, C.ROUND_HALF_EVEN, | 
					
						
							|  |  |  |               C.ROUND_05UP] | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Context(object): | 
					
						
							|  |  |  |     """Provides a convenient way of syncing the C and P contexts""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __slots__ = ['c', 'p'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, c_ctx=None, p_ctx=None): | 
					
						
							|  |  |  |         """Initialization is from the C context""" | 
					
						
							|  |  |  |         self.c = C.getcontext() if c_ctx is None else c_ctx | 
					
						
							|  |  |  |         self.p = P.getcontext() if p_ctx is None else p_ctx | 
					
						
							|  |  |  |         self.p.prec = self.c.prec | 
					
						
							|  |  |  |         self.p.Emin = self.c.Emin | 
					
						
							|  |  |  |         self.p.Emax = self.c.Emax | 
					
						
							| 
									
										
										
										
											2013-01-16 12:58:59 +01:00
										 |  |  |         self.p.rounding = self.c.rounding | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         self.p.capitals = self.c.capitals | 
					
						
							|  |  |  |         self.settraps([sig for sig in self.c.traps if self.c.traps[sig]]) | 
					
						
							|  |  |  |         self.setstatus([sig for sig in self.c.flags if self.c.flags[sig]]) | 
					
						
							|  |  |  |         self.p.clamp = self.c.clamp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __str__(self): | 
					
						
							|  |  |  |         return str(self.c) + '\n' + str(self.p) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getprec(self): | 
					
						
							|  |  |  |         assert(self.c.prec == self.p.prec) | 
					
						
							|  |  |  |         return self.c.prec | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setprec(self, val): | 
					
						
							|  |  |  |         self.c.prec = val | 
					
						
							|  |  |  |         self.p.prec = val | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getemin(self): | 
					
						
							|  |  |  |         assert(self.c.Emin == self.p.Emin) | 
					
						
							|  |  |  |         return self.c.Emin | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setemin(self, val): | 
					
						
							|  |  |  |         self.c.Emin = val | 
					
						
							|  |  |  |         self.p.Emin = val | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getemax(self): | 
					
						
							|  |  |  |         assert(self.c.Emax == self.p.Emax) | 
					
						
							|  |  |  |         return self.c.Emax | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setemax(self, val): | 
					
						
							|  |  |  |         self.c.Emax = val | 
					
						
							|  |  |  |         self.p.Emax = val | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getround(self): | 
					
						
							| 
									
										
										
										
											2013-01-16 12:58:59 +01:00
										 |  |  |         assert(self.c.rounding == self.p.rounding) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         return self.c.rounding | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setround(self, val): | 
					
						
							|  |  |  |         self.c.rounding = val | 
					
						
							| 
									
										
										
										
											2013-01-16 12:58:59 +01:00
										 |  |  |         self.p.rounding = val | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getcapitals(self): | 
					
						
							|  |  |  |         assert(self.c.capitals == self.p.capitals) | 
					
						
							|  |  |  |         return self.c.capitals | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setcapitals(self, val): | 
					
						
							|  |  |  |         self.c.capitals = val | 
					
						
							|  |  |  |         self.p.capitals = val | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getclamp(self): | 
					
						
							|  |  |  |         assert(self.c.clamp == self.p.clamp) | 
					
						
							|  |  |  |         return self.c.clamp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setclamp(self, val): | 
					
						
							|  |  |  |         self.c.clamp = val | 
					
						
							|  |  |  |         self.p.clamp = val | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     prec = property(getprec, setprec) | 
					
						
							|  |  |  |     Emin = property(getemin, setemin) | 
					
						
							|  |  |  |     Emax = property(getemax, setemax) | 
					
						
							|  |  |  |     rounding = property(getround, setround) | 
					
						
							|  |  |  |     clamp = property(getclamp, setclamp) | 
					
						
							|  |  |  |     capitals = property(getcapitals, setcapitals) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def clear_traps(self): | 
					
						
							|  |  |  |         self.c.clear_traps() | 
					
						
							|  |  |  |         for trap in self.p.traps: | 
					
						
							|  |  |  |             self.p.traps[trap] = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def clear_status(self): | 
					
						
							|  |  |  |         self.c.clear_flags() | 
					
						
							|  |  |  |         self.p.clear_flags() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def settraps(self, lst): | 
					
						
							|  |  |  |         """lst: C signal list""" | 
					
						
							|  |  |  |         self.clear_traps() | 
					
						
							|  |  |  |         for signal in lst: | 
					
						
							|  |  |  |             self.c.traps[signal] = True | 
					
						
							|  |  |  |             self.p.traps[CondMap[signal]] = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setstatus(self, lst): | 
					
						
							|  |  |  |         """lst: C signal list""" | 
					
						
							|  |  |  |         self.clear_status() | 
					
						
							|  |  |  |         for signal in lst: | 
					
						
							|  |  |  |             self.c.flags[signal] = True | 
					
						
							|  |  |  |             self.p.flags[CondMap[signal]] = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def assert_eq_status(self): | 
					
						
							|  |  |  |         """assert equality of C and P status""" | 
					
						
							|  |  |  |         for signal in self.c.flags: | 
					
						
							|  |  |  |             if self.c.flags[signal] == (not self.p.flags[CondMap[signal]]): | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # We don't want exceptions so that we can compare the status flags. | 
					
						
							|  |  |  | context = Context() | 
					
						
							|  |  |  | context.Emin = C.MIN_EMIN | 
					
						
							|  |  |  | context.Emax = C.MAX_EMAX | 
					
						
							|  |  |  | context.clear_traps() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # When creating decimals, _decimal is ultimately limited by the maximum | 
					
						
							|  |  |  | # context values. We emulate this restriction for decimal.py. | 
					
						
							|  |  |  | maxcontext = P.Context( | 
					
						
							|  |  |  |     prec=C.MAX_PREC, | 
					
						
							|  |  |  |     Emin=C.MIN_EMIN, | 
					
						
							|  |  |  |     Emax=C.MAX_EMAX, | 
					
						
							|  |  |  |     rounding=P.ROUND_HALF_UP, | 
					
						
							|  |  |  |     capitals=1 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | maxcontext.clamp = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def RestrictedDecimal(value): | 
					
						
							|  |  |  |     maxcontext.traps = copy(context.p.traps) | 
					
						
							|  |  |  |     maxcontext.clear_flags() | 
					
						
							|  |  |  |     if isinstance(value, str): | 
					
						
							|  |  |  |         value = value.strip() | 
					
						
							|  |  |  |     dec = maxcontext.create_decimal(value) | 
					
						
							|  |  |  |     if maxcontext.flags[P.Inexact] or \ | 
					
						
							|  |  |  |        maxcontext.flags[P.Rounded] or \ | 
					
						
							| 
									
										
										
										
											2012-04-05 15:21:58 +02:00
										 |  |  |        maxcontext.flags[P.Clamped] or \ | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |        maxcontext.flags[P.InvalidOperation]: | 
					
						
							|  |  |  |         return context.p._raise_error(P.InvalidOperation) | 
					
						
							|  |  |  |     if maxcontext.flags[P.FloatOperation]: | 
					
						
							|  |  |  |         context.p.flags[P.FloatOperation] = True | 
					
						
							|  |  |  |     return dec | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | #      TestSet: Organize data and events during a single test case | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RestrictedList(list): | 
					
						
							|  |  |  |     """List that can only be modified by appending items.""" | 
					
						
							|  |  |  |     def __getattribute__(self, name): | 
					
						
							|  |  |  |         if name != 'append': | 
					
						
							|  |  |  |             raise AttributeError("unsupported operation") | 
					
						
							|  |  |  |         return list.__getattribute__(self, name) | 
					
						
							|  |  |  |     def unsupported(self, *_): | 
					
						
							|  |  |  |         raise AttributeError("unsupported operation") | 
					
						
							|  |  |  |     __add__ = __delattr__ = __delitem__ = __iadd__ = __imul__ = unsupported | 
					
						
							|  |  |  |     __mul__ = __reversed__ = __rmul__ = __setattr__ = __setitem__ = unsupported | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestSet(object): | 
					
						
							|  |  |  |     """A TestSet contains the original input operands, converted operands,
 | 
					
						
							|  |  |  |        Python exceptions that occurred either during conversion or during | 
					
						
							|  |  |  |        execution of the actual function, and the final results. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |        For safety, most attributes are lists that only support the append | 
					
						
							|  |  |  |        operation. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |        If a function name is prefixed with 'context.', the corresponding | 
					
						
							|  |  |  |        context method is called. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     def __init__(self, funcname, operands): | 
					
						
							|  |  |  |         if funcname.startswith("context."): | 
					
						
							|  |  |  |             self.funcname = funcname.replace("context.", "") | 
					
						
							|  |  |  |             self.contextfunc = True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.funcname = funcname | 
					
						
							|  |  |  |             self.contextfunc = False | 
					
						
							|  |  |  |         self.op = operands               # raw operand tuple | 
					
						
							|  |  |  |         self.context = context           # context used for the operation | 
					
						
							|  |  |  |         self.cop = RestrictedList()      # converted C.Decimal operands | 
					
						
							|  |  |  |         self.cex = RestrictedList()      # Python exceptions for C.Decimal | 
					
						
							|  |  |  |         self.cresults = RestrictedList() # C.Decimal results | 
					
						
							|  |  |  |         self.pop = RestrictedList()      # converted P.Decimal operands | 
					
						
							|  |  |  |         self.pex = RestrictedList()      # Python exceptions for P.Decimal | 
					
						
							|  |  |  |         self.presults = RestrictedList() # P.Decimal results | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |         # If the above results are exact, unrounded and not clamped, repeat | 
					
						
							|  |  |  |         # the operation with a maxcontext to ensure that huge intermediate | 
					
						
							|  |  |  |         # values do not cause a MemoryError. | 
					
						
							|  |  |  |         self.with_maxcontext = False | 
					
						
							|  |  |  |         self.maxcontext = context.c.copy() | 
					
						
							|  |  |  |         self.maxcontext.prec = C.MAX_PREC | 
					
						
							|  |  |  |         self.maxcontext.Emax = C.MAX_EMAX | 
					
						
							|  |  |  |         self.maxcontext.Emin = C.MIN_EMIN | 
					
						
							|  |  |  |         self.maxcontext.clear_flags() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.maxop = RestrictedList()       # converted C.Decimal operands | 
					
						
							|  |  |  |         self.maxex = RestrictedList()       # Python exceptions for C.Decimal | 
					
						
							|  |  |  |         self.maxresults = RestrictedList()  # C.Decimal results | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | #                SkipHandler: skip known discrepancies | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SkipHandler: | 
					
						
							|  |  |  |     """Handle known discrepancies between decimal.py and _decimal.so.
 | 
					
						
							|  |  |  |        These are either ULP differences in the power function or | 
					
						
							|  |  |  |        extremely minor issues."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self.ulpdiff = 0 | 
					
						
							|  |  |  |         self.powmod_zeros = 0 | 
					
						
							|  |  |  |         self.maxctx = P.Context(Emax=10**18, Emin=-10**18) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def default(self, t): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     __ge__ =  __gt__ = __le__ = __lt__ = __ne__ = __eq__ = default | 
					
						
							|  |  |  |     __reduce__ = __format__ = __repr__ = __str__ = default | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def harrison_ulp(self, dec): | 
					
						
							|  |  |  |         """ftp://ftp.inria.fr/INRIA/publication/publi-pdf/RR/RR-5504.pdf""" | 
					
						
							|  |  |  |         a = dec.next_plus() | 
					
						
							|  |  |  |         b = dec.next_minus() | 
					
						
							|  |  |  |         return abs(a - b) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def standard_ulp(self, dec, prec): | 
					
						
							| 
									
										
										
										
											2014-09-10 17:58:15 +02:00
										 |  |  |         return _dec_from_triple(0, '1', dec._exp+len(dec._int)-prec) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def rounding_direction(self, x, mode): | 
					
						
							|  |  |  |         """Determine the effective direction of the rounding when
 | 
					
						
							|  |  |  |            the exact result x is rounded according to mode. | 
					
						
							|  |  |  |            Return -1 for downwards, 0 for undirected, 1 for upwards, | 
					
						
							|  |  |  |            2 for ROUND_05UP."""
 | 
					
						
							|  |  |  |         cmp = 1 if x.compare_total(P.Decimal("+0")) >= 0 else -1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if mode in (P.ROUND_HALF_EVEN, P.ROUND_HALF_UP, P.ROUND_HALF_DOWN): | 
					
						
							|  |  |  |             return 0 | 
					
						
							|  |  |  |         elif mode == P.ROUND_CEILING: | 
					
						
							|  |  |  |             return 1 | 
					
						
							|  |  |  |         elif mode == P.ROUND_FLOOR: | 
					
						
							|  |  |  |             return -1 | 
					
						
							|  |  |  |         elif mode == P.ROUND_UP: | 
					
						
							|  |  |  |             return cmp | 
					
						
							|  |  |  |         elif mode == P.ROUND_DOWN: | 
					
						
							|  |  |  |             return -cmp | 
					
						
							|  |  |  |         elif mode == P.ROUND_05UP: | 
					
						
							|  |  |  |             return 2 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise ValueError("Unexpected rounding mode: %s" % mode) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_ulpdiff(self, exact, rounded): | 
					
						
							|  |  |  |         # current precision | 
					
						
							|  |  |  |         p = context.p.prec | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Convert infinities to the largest representable number + 1. | 
					
						
							|  |  |  |         x = exact | 
					
						
							|  |  |  |         if exact.is_infinite(): | 
					
						
							| 
									
										
										
										
											2014-09-10 17:58:15 +02:00
										 |  |  |             x = _dec_from_triple(exact._sign, '10', context.p.Emax) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         y = rounded | 
					
						
							|  |  |  |         if rounded.is_infinite(): | 
					
						
							| 
									
										
										
										
											2014-09-10 17:58:15 +02:00
										 |  |  |             y = _dec_from_triple(rounded._sign, '10', context.p.Emax) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # err = (rounded - exact) / ulp(rounded) | 
					
						
							|  |  |  |         self.maxctx.prec = p * 2 | 
					
						
							|  |  |  |         t = self.maxctx.subtract(y, x) | 
					
						
							|  |  |  |         if context.c.flags[C.Clamped] or \ | 
					
						
							|  |  |  |            context.c.flags[C.Underflow]: | 
					
						
							|  |  |  |             # The standard ulp does not work in Underflow territory. | 
					
						
							|  |  |  |             ulp = self.harrison_ulp(y) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             ulp = self.standard_ulp(y, p) | 
					
						
							|  |  |  |         # Error in ulps. | 
					
						
							|  |  |  |         err = self.maxctx.divide(t, ulp) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dir = self.rounding_direction(x, context.p.rounding) | 
					
						
							|  |  |  |         if dir == 0: | 
					
						
							|  |  |  |             if P.Decimal("-0.6") < err < P.Decimal("0.6"): | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  |         elif dir == 1: # directed, upwards | 
					
						
							|  |  |  |             if P.Decimal("-0.1") < err < P.Decimal("1.1"): | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  |         elif dir == -1: # directed, downwards | 
					
						
							|  |  |  |             if P.Decimal("-1.1") < err < P.Decimal("0.1"): | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  |         else: # ROUND_05UP | 
					
						
							|  |  |  |             if P.Decimal("-1.1") < err < P.Decimal("1.1"): | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         print("ulp: %s  error: %s  exact: %s  c_rounded: %s" | 
					
						
							|  |  |  |               % (ulp, err, exact, rounded)) | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def bin_resolve_ulp(self, t): | 
					
						
							|  |  |  |         """Check if results of _decimal's power function are within the
 | 
					
						
							|  |  |  |            allowed ulp ranges."""
 | 
					
						
							|  |  |  |         # NaNs are beyond repair. | 
					
						
							|  |  |  |         if t.rc.is_nan() or t.rp.is_nan(): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # "exact" result, double precision, half_even | 
					
						
							|  |  |  |         self.maxctx.prec = context.p.prec * 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         op1, op2 = t.pop[0], t.pop[1] | 
					
						
							|  |  |  |         if t.contextfunc: | 
					
						
							|  |  |  |             exact = getattr(self.maxctx, t.funcname)(op1, op2) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             exact = getattr(op1, t.funcname)(op2, context=self.maxctx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # _decimal's rounded result | 
					
						
							|  |  |  |         rounded = P.Decimal(t.cresults[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.ulpdiff += 1 | 
					
						
							|  |  |  |         return self.check_ulpdiff(exact, rounded) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ############################ Correct rounding ############################# | 
					
						
							|  |  |  |     def resolve_underflow(self, t): | 
					
						
							|  |  |  |         """In extremely rare cases where the infinite precision result is just
 | 
					
						
							|  |  |  |            below etiny, cdecimal does not set Subnormal/Underflow. Example: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            setcontext(Context(prec=21, rounding=ROUND_UP, Emin=-55, Emax=85)) | 
					
						
							|  |  |  |            Decimal("1.00000000000000000000000000000000000000000000000" | 
					
						
							|  |  |  |                    "0000000100000000000000000000000000000000000000000" | 
					
						
							|  |  |  |                    "0000000000000025").ln() | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if t.cresults != t.presults: | 
					
						
							|  |  |  |             return False # Results must be identical. | 
					
						
							|  |  |  |         if context.c.flags[C.Rounded] and \ | 
					
						
							|  |  |  |            context.c.flags[C.Inexact] and \ | 
					
						
							|  |  |  |            context.p.flags[P.Rounded] and \ | 
					
						
							|  |  |  |            context.p.flags[P.Inexact]: | 
					
						
							|  |  |  |             return True # Subnormal/Underflow may be missing. | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def exp(self, t): | 
					
						
							|  |  |  |         """Resolve Underflow or ULP difference.""" | 
					
						
							|  |  |  |         return self.resolve_underflow(t) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def log10(self, t): | 
					
						
							|  |  |  |         """Resolve Underflow or ULP difference.""" | 
					
						
							|  |  |  |         return self.resolve_underflow(t) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def ln(self, t): | 
					
						
							|  |  |  |         """Resolve Underflow or ULP difference.""" | 
					
						
							|  |  |  |         return self.resolve_underflow(t) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __pow__(self, t): | 
					
						
							|  |  |  |         """Always calls the resolve function. C.Decimal does not have correct
 | 
					
						
							|  |  |  |            rounding for the power function."""
 | 
					
						
							|  |  |  |         if context.c.flags[C.Rounded] and \ | 
					
						
							|  |  |  |            context.c.flags[C.Inexact] and \ | 
					
						
							|  |  |  |            context.p.flags[P.Rounded] and \ | 
					
						
							|  |  |  |            context.p.flags[P.Inexact]: | 
					
						
							|  |  |  |             return self.bin_resolve_ulp(t) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |     power = __rpow__ = __pow__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ############################## Technicalities ############################# | 
					
						
							|  |  |  |     def __float__(self, t): | 
					
						
							|  |  |  |         """NaN comparison in the verify() function obviously gives an
 | 
					
						
							|  |  |  |            incorrect answer:  nan == nan -> False"""
 | 
					
						
							|  |  |  |         if t.cop[0].is_nan() and t.pop[0].is_nan(): | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     __complex__ = __float__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __radd__(self, t): | 
					
						
							|  |  |  |         """decimal.py gives precedence to the first NaN; this is
 | 
					
						
							|  |  |  |            not important, as __radd__ will not be called for | 
					
						
							|  |  |  |            two decimal arguments."""
 | 
					
						
							|  |  |  |         if t.rc.is_nan() and t.rp.is_nan(): | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     __rmul__ = __radd__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ################################ Various ################################## | 
					
						
							|  |  |  |     def __round__(self, t): | 
					
						
							|  |  |  |         """Exception: Decimal('1').__round__(-100000000000000000000000000)
 | 
					
						
							|  |  |  |            Should it really be InvalidOperation?"""
 | 
					
						
							|  |  |  |         if t.rc is None and t.rp.is_nan(): | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | shandler = SkipHandler() | 
					
						
							|  |  |  | def skip_error(t): | 
					
						
							|  |  |  |     return getattr(shandler, t.funcname, shandler.default)(t) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | #                      Handling verification errors | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class VerifyError(Exception): | 
					
						
							|  |  |  |     """Verification failed.""" | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def function_as_string(t): | 
					
						
							|  |  |  |     if t.contextfunc: | 
					
						
							|  |  |  |         cargs = t.cop | 
					
						
							|  |  |  |         pargs = t.pop | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |         maxargs = t.maxop | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         cfunc = "c_func: %s(" % t.funcname | 
					
						
							|  |  |  |         pfunc = "p_func: %s(" % t.funcname | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |         maxfunc = "max_func: %s(" % t.funcname | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |     else: | 
					
						
							|  |  |  |         cself, cargs = t.cop[0], t.cop[1:] | 
					
						
							|  |  |  |         pself, pargs = t.pop[0], t.pop[1:] | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |         maxself, maxargs = t.maxop[0], t.maxop[1:] | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         cfunc = "c_func: %s.%s(" % (repr(cself), t.funcname) | 
					
						
							|  |  |  |         pfunc = "p_func: %s.%s(" % (repr(pself), t.funcname) | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |         maxfunc = "max_func: %s.%s(" % (repr(maxself), t.funcname) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     err = cfunc | 
					
						
							|  |  |  |     for arg in cargs: | 
					
						
							|  |  |  |         err += "%s, " % repr(arg) | 
					
						
							|  |  |  |     err = err.rstrip(", ") | 
					
						
							|  |  |  |     err += ")\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     err += pfunc | 
					
						
							|  |  |  |     for arg in pargs: | 
					
						
							|  |  |  |         err += "%s, " % repr(arg) | 
					
						
							|  |  |  |     err = err.rstrip(", ") | 
					
						
							|  |  |  |     err += ")" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |     if t.with_maxcontext: | 
					
						
							|  |  |  |         err += "\n" | 
					
						
							|  |  |  |         err += maxfunc | 
					
						
							|  |  |  |         for arg in maxargs: | 
					
						
							|  |  |  |             err += "%s, " % repr(arg) | 
					
						
							|  |  |  |         err = err.rstrip(", ") | 
					
						
							|  |  |  |         err += ")" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |     return err | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def raise_error(t): | 
					
						
							|  |  |  |     global EXIT_STATUS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if skip_error(t): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     EXIT_STATUS = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     err = "Error in %s:\n\n" % t.funcname | 
					
						
							|  |  |  |     err += "input operands: %s\n\n" % (t.op,) | 
					
						
							|  |  |  |     err += function_as_string(t) | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     err += "\n\nc_result: %s\np_result: %s\n" % (t.cresults, t.presults) | 
					
						
							|  |  |  |     if t.with_maxcontext: | 
					
						
							|  |  |  |         err += "max_result: %s\n\n" % (t.maxresults) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         err += "\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     err += "c_exceptions: %s\np_exceptions: %s\n" % (t.cex, t.pex) | 
					
						
							|  |  |  |     if t.with_maxcontext: | 
					
						
							|  |  |  |         err += "max_exceptions: %s\n\n" % t.maxex | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         err += "\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     err += "%s\n" % str(t.context) | 
					
						
							|  |  |  |     if t.with_maxcontext: | 
					
						
							|  |  |  |         err += "%s\n" % str(t.maxcontext) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         err += "\n" | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     raise VerifyError(err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | #                        Main testing functions | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  The procedure is always (t is the TestSet): | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #   convert(t) -> Initialize the TestSet as necessary. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #                 Return 0 for early abortion (e.g. if a TypeError | 
					
						
							|  |  |  | #                 occurs during conversion, there is nothing to test). | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #                 Return 1 for continuing with the test case. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #   callfuncs(t) -> Call the relevant function for each implementation | 
					
						
							|  |  |  | #                   and record the results in the TestSet. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #   verify(t) -> Verify the results. If verification fails, details | 
					
						
							|  |  |  | #                are printed to stdout. | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  | def all_nan(a): | 
					
						
							|  |  |  |     if isinstance(a, C.Decimal): | 
					
						
							|  |  |  |         return a.is_nan() | 
					
						
							|  |  |  |     elif isinstance(a, tuple): | 
					
						
							|  |  |  |         return all(all_nan(v) for v in a) | 
					
						
							|  |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | def convert(t, convstr=True): | 
					
						
							|  |  |  |     """ t is the testset. At this stage the testset contains a tuple of
 | 
					
						
							|  |  |  |         operands t.op of various types. For decimal methods the first | 
					
						
							|  |  |  |         operand (self) is always converted to Decimal. If 'convstr' is | 
					
						
							|  |  |  |         true, string operands are converted as well. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Context operands are of type deccheck.Context, rounding mode | 
					
						
							|  |  |  |         operands are given as a tuple (C.rounding, P.rounding). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Other types (float, int, etc.) are left unchanged. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     for i, op in enumerate(t.op): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         context.clear_status() | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |         t.maxcontext.clear_flags() | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-16 12:58:59 +01:00
										 |  |  |         if op in RoundModes: | 
					
						
							|  |  |  |             t.cop.append(op) | 
					
						
							|  |  |  |             t.pop.append(op) | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |             t.maxop.append(op) | 
					
						
							| 
									
										
										
										
											2013-01-16 12:58:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         elif not t.contextfunc and i == 0 or \ | 
					
						
							|  |  |  |              convstr and isinstance(op, str): | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 c = C.Decimal(op) | 
					
						
							|  |  |  |                 cex = None | 
					
						
							|  |  |  |             except (TypeError, ValueError, OverflowError) as e: | 
					
						
							|  |  |  |                 c = None | 
					
						
							|  |  |  |                 cex = e.__class__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 p = RestrictedDecimal(op) | 
					
						
							|  |  |  |                 pex = None | 
					
						
							|  |  |  |             except (TypeError, ValueError, OverflowError) as e: | 
					
						
							|  |  |  |                 p = None | 
					
						
							|  |  |  |                 pex = e.__class__ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 C.setcontext(t.maxcontext) | 
					
						
							|  |  |  |                 maxop = C.Decimal(op) | 
					
						
							|  |  |  |                 maxex = None | 
					
						
							|  |  |  |             except (TypeError, ValueError, OverflowError) as e: | 
					
						
							|  |  |  |                 maxop = None | 
					
						
							|  |  |  |                 maxex = e.__class__ | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 C.setcontext(context.c) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |             t.cop.append(c) | 
					
						
							|  |  |  |             t.cex.append(cex) | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |             t.pop.append(p) | 
					
						
							|  |  |  |             t.pex.append(pex) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |             t.maxop.append(maxop) | 
					
						
							|  |  |  |             t.maxex.append(maxex) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |             if cex is pex: | 
					
						
							|  |  |  |                 if str(c) != str(p) or not context.assert_eq_status(): | 
					
						
							|  |  |  |                     raise_error(t) | 
					
						
							|  |  |  |                 if cex and pex: | 
					
						
							|  |  |  |                     # nothing to test | 
					
						
							|  |  |  |                     return 0 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise_error(t) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |             # The exceptions in the maxcontext operation can legitimately | 
					
						
							|  |  |  |             # differ, only test that maxex implies cex: | 
					
						
							|  |  |  |             if maxex is not None and cex is not maxex: | 
					
						
							|  |  |  |                 raise_error(t) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         elif isinstance(op, Context): | 
					
						
							|  |  |  |             t.context = op | 
					
						
							|  |  |  |             t.cop.append(op.c) | 
					
						
							|  |  |  |             t.pop.append(op.p) | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |             t.maxop.append(t.maxcontext) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             t.cop.append(op) | 
					
						
							|  |  |  |             t.pop.append(op) | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |             t.maxop.append(op) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def callfuncs(t): | 
					
						
							|  |  |  |     """ t is the testset. At this stage the testset contains operand lists
 | 
					
						
							|  |  |  |         t.cop and t.pop for the C and Python versions of decimal. | 
					
						
							|  |  |  |         For Decimal methods, the first operands are of type C.Decimal and | 
					
						
							|  |  |  |         P.Decimal respectively. The remaining operands can have various types. | 
					
						
							|  |  |  |         For Context methods, all operands can have any type. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         t.rc and t.rp are the results of the operation. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     context.clear_status() | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |     t.maxcontext.clear_flags() | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         if t.contextfunc: | 
					
						
							|  |  |  |             cargs = t.cop | 
					
						
							|  |  |  |             t.rc = getattr(context.c, t.funcname)(*cargs) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             cself = t.cop[0] | 
					
						
							|  |  |  |             cargs = t.cop[1:] | 
					
						
							|  |  |  |             t.rc = getattr(cself, t.funcname)(*cargs) | 
					
						
							|  |  |  |         t.cex.append(None) | 
					
						
							|  |  |  |     except (TypeError, ValueError, OverflowError, MemoryError) as e: | 
					
						
							|  |  |  |         t.rc = None | 
					
						
							|  |  |  |         t.cex.append(e.__class__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         if t.contextfunc: | 
					
						
							|  |  |  |             pargs = t.pop | 
					
						
							|  |  |  |             t.rp = getattr(context.p, t.funcname)(*pargs) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             pself = t.pop[0] | 
					
						
							|  |  |  |             pargs = t.pop[1:] | 
					
						
							|  |  |  |             t.rp = getattr(pself, t.funcname)(*pargs) | 
					
						
							|  |  |  |         t.pex.append(None) | 
					
						
							|  |  |  |     except (TypeError, ValueError, OverflowError, MemoryError) as e: | 
					
						
							|  |  |  |         t.rp = None | 
					
						
							|  |  |  |         t.pex.append(e.__class__) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |     # If the above results are exact, unrounded, normal etc., repeat the | 
					
						
							|  |  |  |     # operation with a maxcontext to ensure that huge intermediate values | 
					
						
							|  |  |  |     # do not cause a MemoryError. | 
					
						
							|  |  |  |     if (t.funcname not in MaxContextSkip and | 
					
						
							|  |  |  |         not context.c.flags[C.InvalidOperation] and | 
					
						
							|  |  |  |         not context.c.flags[C.Inexact] and | 
					
						
							|  |  |  |         not context.c.flags[C.Rounded] and | 
					
						
							|  |  |  |         not context.c.flags[C.Subnormal] and | 
					
						
							|  |  |  |         not context.c.flags[C.Clamped] and | 
					
						
							|  |  |  |         not context.clamp and # results are padded to context.prec if context.clamp==1. | 
					
						
							|  |  |  |         not any(isinstance(v, C.Context) for v in t.cop)): # another context is used. | 
					
						
							|  |  |  |         t.with_maxcontext = True | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if t.contextfunc: | 
					
						
							|  |  |  |                 maxargs = t.maxop | 
					
						
							|  |  |  |                 t.rmax = getattr(t.maxcontext, t.funcname)(*maxargs) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 maxself = t.maxop[0] | 
					
						
							|  |  |  |                 maxargs = t.maxop[1:] | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     C.setcontext(t.maxcontext) | 
					
						
							|  |  |  |                     t.rmax = getattr(maxself, t.funcname)(*maxargs) | 
					
						
							|  |  |  |                 finally: | 
					
						
							|  |  |  |                     C.setcontext(context.c) | 
					
						
							|  |  |  |             t.maxex.append(None) | 
					
						
							|  |  |  |         except (TypeError, ValueError, OverflowError, MemoryError) as e: | 
					
						
							|  |  |  |             t.rmax = None | 
					
						
							|  |  |  |             t.maxex.append(e.__class__) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | def verify(t, stat): | 
					
						
							|  |  |  |     """ t is the testset. At this stage the testset contains the following
 | 
					
						
							|  |  |  |         tuples: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             t.op: original operands | 
					
						
							|  |  |  |             t.cop: C.Decimal operands (see convert for details) | 
					
						
							|  |  |  |             t.pop: P.Decimal operands (see convert for details) | 
					
						
							|  |  |  |             t.rc: C result | 
					
						
							|  |  |  |             t.rp: Python result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         t.rc and t.rp can have various types. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     t.cresults.append(str(t.rc)) | 
					
						
							|  |  |  |     t.presults.append(str(t.rp)) | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |     if t.with_maxcontext: | 
					
						
							|  |  |  |         t.maxresults.append(str(t.rmax)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |     if isinstance(t.rc, C.Decimal) and isinstance(t.rp, P.Decimal): | 
					
						
							|  |  |  |         # General case: both results are Decimals. | 
					
						
							|  |  |  |         t.cresults.append(t.rc.to_eng_string()) | 
					
						
							|  |  |  |         t.cresults.append(t.rc.as_tuple()) | 
					
						
							|  |  |  |         t.cresults.append(str(t.rc.imag)) | 
					
						
							|  |  |  |         t.cresults.append(str(t.rc.real)) | 
					
						
							|  |  |  |         t.presults.append(t.rp.to_eng_string()) | 
					
						
							|  |  |  |         t.presults.append(t.rp.as_tuple()) | 
					
						
							|  |  |  |         t.presults.append(str(t.rp.imag)) | 
					
						
							|  |  |  |         t.presults.append(str(t.rp.real)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |         if t.with_maxcontext and isinstance(t.rmax, C.Decimal): | 
					
						
							|  |  |  |             t.maxresults.append(t.rmax.to_eng_string()) | 
					
						
							|  |  |  |             t.maxresults.append(t.rmax.as_tuple()) | 
					
						
							|  |  |  |             t.maxresults.append(str(t.rmax.imag)) | 
					
						
							|  |  |  |             t.maxresults.append(str(t.rmax.real)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         nc = t.rc.number_class().lstrip('+-s') | 
					
						
							|  |  |  |         stat[nc] += 1 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         # Results from e.g. __divmod__ can only be compared as strings. | 
					
						
							|  |  |  |         if not isinstance(t.rc, tuple) and not isinstance(t.rp, tuple): | 
					
						
							|  |  |  |             if t.rc != t.rp: | 
					
						
							|  |  |  |                 raise_error(t) | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |             if t.with_maxcontext and not isinstance(t.rmax, tuple): | 
					
						
							|  |  |  |                 if t.rmax != t.rc: | 
					
						
							|  |  |  |                     raise_error(t) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         stat[type(t.rc).__name__] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # The return value lists must be equal. | 
					
						
							|  |  |  |     if t.cresults != t.presults: | 
					
						
							|  |  |  |         raise_error(t) | 
					
						
							|  |  |  |     # The Python exception lists (TypeError, etc.) must be equal. | 
					
						
							|  |  |  |     if t.cex != t.pex: | 
					
						
							|  |  |  |         raise_error(t) | 
					
						
							|  |  |  |     # The context flags must be equal. | 
					
						
							|  |  |  |     if not t.context.assert_eq_status(): | 
					
						
							|  |  |  |         raise_error(t) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 01:52:47 +01:00
										 |  |  |     if t.with_maxcontext: | 
					
						
							|  |  |  |         # NaN payloads etc. depend on precision and clamp. | 
					
						
							|  |  |  |         if all_nan(t.rc) and all_nan(t.rmax): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         # The return value lists must be equal. | 
					
						
							|  |  |  |         if t.maxresults != t.cresults: | 
					
						
							|  |  |  |             raise_error(t) | 
					
						
							|  |  |  |         # The Python exception lists (TypeError, etc.) must be equal. | 
					
						
							|  |  |  |         if t.maxex != t.cex: | 
					
						
							|  |  |  |             raise_error(t) | 
					
						
							|  |  |  |         # The context flags must be equal. | 
					
						
							|  |  |  |         if t.maxcontext.flags != t.context.c.flags: | 
					
						
							|  |  |  |             raise_error(t) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | #                           Main test loops | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  test_method(method, testspecs, testfunc) -> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #     Loop through various context settings. The degree of | 
					
						
							|  |  |  | #     thoroughness is determined by 'testspec'. For each | 
					
						
							|  |  |  | #     setting, call 'testfunc'. Generally, 'testfunc' itself | 
					
						
							|  |  |  | #     a loop, iterating through many test cases generated | 
					
						
							|  |  |  | #     by the functions in randdec.py. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  test_n-ary(method, prec, exp_range, restricted_range, itr, stat) -> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #     'test_unary', 'test_binary' and 'test_ternary' are the | 
					
						
							|  |  |  | #     main test functions passed to 'test_method'. They deal | 
					
						
							|  |  |  | #     with the regular cases. The thoroughness of testing is | 
					
						
							|  |  |  | #     determined by 'itr'. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #     'prec', 'exp_range' and 'restricted_range' are passed | 
					
						
							|  |  |  | #     to the test-generating functions and limit the generated | 
					
						
							|  |  |  | #     values. In some cases, for reasonable run times a | 
					
						
							|  |  |  | #     maximum exponent of 9999 is required. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #     The 'stat' parameter is passed down to the 'verify' | 
					
						
							|  |  |  | #     function, which records statistics for the result values. | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def log(fmt, args=None): | 
					
						
							|  |  |  |     if args: | 
					
						
							|  |  |  |         sys.stdout.write(''.join((fmt, '\n')) % args) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         sys.stdout.write(''.join((str(fmt), '\n'))) | 
					
						
							|  |  |  |     sys.stdout.flush() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_method(method, testspecs, testfunc): | 
					
						
							|  |  |  |     """Iterate a test function through many context settings.""" | 
					
						
							|  |  |  |     log("testing %s ...", method) | 
					
						
							|  |  |  |     stat = defaultdict(int) | 
					
						
							|  |  |  |     for spec in testspecs: | 
					
						
							|  |  |  |         if 'samples' in spec: | 
					
						
							|  |  |  |             spec['prec'] = sorted(random.sample(range(1, 101), | 
					
						
							|  |  |  |                                   spec['samples'])) | 
					
						
							|  |  |  |         for prec in spec['prec']: | 
					
						
							|  |  |  |             context.prec = prec | 
					
						
							|  |  |  |             for expts in spec['expts']: | 
					
						
							|  |  |  |                 emin, emax = expts | 
					
						
							|  |  |  |                 if emin == 'rand': | 
					
						
							|  |  |  |                     context.Emin = random.randrange(-1000, 0) | 
					
						
							|  |  |  |                     context.Emax = random.randrange(prec, 1000) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     context.Emin, context.Emax = emin, emax | 
					
						
							|  |  |  |                 if prec > context.Emax: continue | 
					
						
							|  |  |  |                 log("    prec: %d  emin: %d  emax: %d", | 
					
						
							|  |  |  |                     (context.prec, context.Emin, context.Emax)) | 
					
						
							|  |  |  |                 restr_range = 9999 if context.Emax > 9999 else context.Emax+99 | 
					
						
							| 
									
										
										
										
											2013-01-16 12:58:59 +01:00
										 |  |  |                 for rounding in RoundModes: | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |                     context.rounding = rounding | 
					
						
							|  |  |  |                     context.capitals = random.randrange(2) | 
					
						
							|  |  |  |                     if spec['clamp'] == 'rand': | 
					
						
							|  |  |  |                         context.clamp = random.randrange(2) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         context.clamp = spec['clamp'] | 
					
						
							|  |  |  |                     exprange = context.c.Emax | 
					
						
							|  |  |  |                     testfunc(method, prec, exprange, restr_range, | 
					
						
							|  |  |  |                              spec['iter'], stat) | 
					
						
							|  |  |  |     log("    result types: %s" % sorted([t for t in stat.items()])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_unary(method, prec, exp_range, restricted_range, itr, stat): | 
					
						
							|  |  |  |     """Iterate a unary function through many test cases.""" | 
					
						
							|  |  |  |     if method in UnaryRestricted: | 
					
						
							|  |  |  |         exp_range = restricted_range | 
					
						
							|  |  |  |     for op in all_unary(prec, exp_range, itr): | 
					
						
							|  |  |  |         t = TestSet(method, op) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if not convert(t): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             callfuncs(t) | 
					
						
							|  |  |  |             verify(t, stat) | 
					
						
							|  |  |  |         except VerifyError as err: | 
					
						
							|  |  |  |             log(err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-15 22:33:33 +01:00
										 |  |  |     if not method.startswith('__'): | 
					
						
							|  |  |  |         for op in unary_optarg(prec, exp_range, itr): | 
					
						
							|  |  |  |             t = TestSet(method, op) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if not convert(t): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 callfuncs(t) | 
					
						
							|  |  |  |                 verify(t, stat) | 
					
						
							|  |  |  |             except VerifyError as err: | 
					
						
							|  |  |  |                 log(err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | def test_binary(method, prec, exp_range, restricted_range, itr, stat): | 
					
						
							|  |  |  |     """Iterate a binary function through many test cases.""" | 
					
						
							|  |  |  |     if method in BinaryRestricted: | 
					
						
							|  |  |  |         exp_range = restricted_range | 
					
						
							|  |  |  |     for op in all_binary(prec, exp_range, itr): | 
					
						
							|  |  |  |         t = TestSet(method, op) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if not convert(t): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             callfuncs(t) | 
					
						
							|  |  |  |             verify(t, stat) | 
					
						
							|  |  |  |         except VerifyError as err: | 
					
						
							|  |  |  |             log(err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-15 22:33:33 +01:00
										 |  |  |     if not method.startswith('__'): | 
					
						
							|  |  |  |         for op in binary_optarg(prec, exp_range, itr): | 
					
						
							|  |  |  |             t = TestSet(method, op) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if not convert(t): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 callfuncs(t) | 
					
						
							|  |  |  |                 verify(t, stat) | 
					
						
							|  |  |  |             except VerifyError as err: | 
					
						
							|  |  |  |                 log(err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | def test_ternary(method, prec, exp_range, restricted_range, itr, stat): | 
					
						
							|  |  |  |     """Iterate a ternary function through many test cases.""" | 
					
						
							|  |  |  |     if method in TernaryRestricted: | 
					
						
							|  |  |  |         exp_range = restricted_range | 
					
						
							|  |  |  |     for op in all_ternary(prec, exp_range, itr): | 
					
						
							|  |  |  |         t = TestSet(method, op) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if not convert(t): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             callfuncs(t) | 
					
						
							|  |  |  |             verify(t, stat) | 
					
						
							|  |  |  |         except VerifyError as err: | 
					
						
							|  |  |  |             log(err) | 
					
						
							| 
									
										
										
										
											2012-12-15 22:33:33 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if not method.startswith('__'): | 
					
						
							|  |  |  |         for op in ternary_optarg(prec, exp_range, itr): | 
					
						
							|  |  |  |             t = TestSet(method, op) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if not convert(t): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 callfuncs(t) | 
					
						
							|  |  |  |                 verify(t, stat) | 
					
						
							|  |  |  |             except VerifyError as err: | 
					
						
							|  |  |  |                 log(err) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | def test_format(method, prec, exp_range, restricted_range, itr, stat): | 
					
						
							|  |  |  |     """Iterate the __format__ method through many test cases.""" | 
					
						
							|  |  |  |     for op in all_unary(prec, exp_range, itr): | 
					
						
							| 
									
										
										
										
											2013-05-29 15:45:38 +02:00
										 |  |  |         fmt1 = rand_format(chr(random.randrange(0, 128)), 'EeGgn') | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         fmt2 = rand_locale() | 
					
						
							|  |  |  |         for fmt in (fmt1, fmt2): | 
					
						
							|  |  |  |             fmtop = (op[0], fmt) | 
					
						
							|  |  |  |             t = TestSet(method, fmtop) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if not convert(t, convstr=False): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 callfuncs(t) | 
					
						
							|  |  |  |                 verify(t, stat) | 
					
						
							|  |  |  |             except VerifyError as err: | 
					
						
							|  |  |  |                 log(err) | 
					
						
							|  |  |  |     for op in all_unary(prec, 9999, itr): | 
					
						
							| 
									
										
										
										
											2013-05-29 15:45:38 +02:00
										 |  |  |         fmt1 = rand_format(chr(random.randrange(0, 128)), 'Ff%') | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         fmt2 = rand_locale() | 
					
						
							|  |  |  |         for fmt in (fmt1, fmt2): | 
					
						
							|  |  |  |             fmtop = (op[0], fmt) | 
					
						
							|  |  |  |             t = TestSet(method, fmtop) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if not convert(t, convstr=False): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 callfuncs(t) | 
					
						
							|  |  |  |                 verify(t, stat) | 
					
						
							|  |  |  |             except VerifyError as err: | 
					
						
							|  |  |  |                 log(err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_round(method, prec, exprange, restricted_range, itr, stat): | 
					
						
							|  |  |  |     """Iterate the __round__ method through many test cases.""" | 
					
						
							|  |  |  |     for op in all_unary(prec, 9999, itr): | 
					
						
							|  |  |  |         n = random.randrange(10) | 
					
						
							|  |  |  |         roundop = (op[0], n) | 
					
						
							|  |  |  |         t = TestSet(method, roundop) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if not convert(t): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             callfuncs(t) | 
					
						
							|  |  |  |             verify(t, stat) | 
					
						
							|  |  |  |         except VerifyError as err: | 
					
						
							|  |  |  |             log(err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_from_float(method, prec, exprange, restricted_range, itr, stat): | 
					
						
							|  |  |  |     """Iterate the __float__ method through many test cases.""" | 
					
						
							| 
									
										
										
										
											2013-01-16 12:58:59 +01:00
										 |  |  |     for rounding in RoundModes: | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         context.rounding = rounding | 
					
						
							|  |  |  |         for i in range(1000): | 
					
						
							|  |  |  |             f = randfloat() | 
					
						
							|  |  |  |             op = (f,) if method.startswith("context.") else ("sNaN", f) | 
					
						
							|  |  |  |             t = TestSet(method, op) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if not convert(t): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 callfuncs(t) | 
					
						
							|  |  |  |                 verify(t, stat) | 
					
						
							|  |  |  |             except VerifyError as err: | 
					
						
							|  |  |  |                 log(err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def randcontext(exprange): | 
					
						
							|  |  |  |     c = Context(C.Context(), P.Context()) | 
					
						
							|  |  |  |     c.Emax = random.randrange(1, exprange+1) | 
					
						
							|  |  |  |     c.Emin = random.randrange(-exprange, 0) | 
					
						
							|  |  |  |     maxprec = 100 if c.Emax >= 100 else c.Emax | 
					
						
							|  |  |  |     c.prec = random.randrange(1, maxprec+1) | 
					
						
							|  |  |  |     c.clamp = random.randrange(2) | 
					
						
							|  |  |  |     c.clear_traps() | 
					
						
							|  |  |  |     return c | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_quantize_api(method, prec, exprange, restricted_range, itr, stat): | 
					
						
							|  |  |  |     """Iterate the 'quantize' method through many test cases, using
 | 
					
						
							|  |  |  |        the optional arguments."""
 | 
					
						
							|  |  |  |     for op in all_binary(prec, restricted_range, itr): | 
					
						
							|  |  |  |         for rounding in RoundModes: | 
					
						
							|  |  |  |             c = randcontext(exprange) | 
					
						
							|  |  |  |             quantizeop = (op[0], op[1], rounding, c) | 
					
						
							|  |  |  |             t = TestSet(method, quantizeop) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if not convert(t): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 callfuncs(t) | 
					
						
							|  |  |  |                 verify(t, stat) | 
					
						
							|  |  |  |             except VerifyError as err: | 
					
						
							|  |  |  |                 log(err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def check_untested(funcdict, c_cls, p_cls): | 
					
						
							|  |  |  |     """Determine untested, C-only and Python-only attributes.
 | 
					
						
							|  |  |  |        Uncomment print lines for debugging."""
 | 
					
						
							|  |  |  |     c_attr = set(dir(c_cls)) | 
					
						
							|  |  |  |     p_attr = set(dir(p_cls)) | 
					
						
							|  |  |  |     intersect = c_attr & p_attr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     funcdict['c_only'] = tuple(sorted(c_attr-intersect)) | 
					
						
							|  |  |  |     funcdict['p_only'] = tuple(sorted(p_attr-intersect)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tested = set() | 
					
						
							|  |  |  |     for lst in funcdict.values(): | 
					
						
							|  |  |  |         for v in lst: | 
					
						
							|  |  |  |             v = v.replace("context.", "") if c_cls == C.Context else v | 
					
						
							|  |  |  |             tested.add(v) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     funcdict['untested'] = tuple(sorted(intersect-tested)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |     # 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])) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |     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] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Set up the testspecs list. A testspec is simply a dictionary | 
					
						
							|  |  |  |     # that determines the amount of different contexts that 'test_method' | 
					
						
							|  |  |  |     # will generate. | 
					
						
							|  |  |  |     base_expts = [(C.MIN_EMIN, C.MAX_EMAX)] | 
					
						
							|  |  |  |     if C.MAX_EMAX == 999999999999999999: | 
					
						
							|  |  |  |         base_expts.append((-999999999, 999999999)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Basic contexts. | 
					
						
							|  |  |  |     base = { | 
					
						
							|  |  |  |         'expts': base_expts, | 
					
						
							|  |  |  |         'prec': [], | 
					
						
							|  |  |  |         'clamp': 'rand', | 
					
						
							|  |  |  |         'iter': None, | 
					
						
							|  |  |  |         'samples': None, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     # Contexts with small values for prec, emin, emax. | 
					
						
							|  |  |  |     small = { | 
					
						
							|  |  |  |         'prec': [1, 2, 3, 4, 5], | 
					
						
							|  |  |  |         'expts': [(-1, 1), (-2, 2), (-3, 3), (-4, 4), (-5, 5)], | 
					
						
							|  |  |  |         'clamp': 'rand', | 
					
						
							|  |  |  |         'iter': None | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     # IEEE interchange format. | 
					
						
							|  |  |  |     ieee = [ | 
					
						
							|  |  |  |         # DECIMAL32 | 
					
						
							|  |  |  |         {'prec': [7], 'expts': [(-95, 96)], 'clamp': 1, 'iter': None}, | 
					
						
							|  |  |  |         # DECIMAL64 | 
					
						
							|  |  |  |         {'prec': [16], 'expts': [(-383, 384)], 'clamp': 1, 'iter': None}, | 
					
						
							|  |  |  |         # DECIMAL128 | 
					
						
							|  |  |  |         {'prec': [34], 'expts': [(-6143, 6144)], 'clamp': 1, 'iter': None} | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |     if args.time == 'medium': | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         base['expts'].append(('rand', 'rand')) | 
					
						
							|  |  |  |         # 5 random precisions | 
					
						
							|  |  |  |         base['samples'] = 5 | 
					
						
							|  |  |  |         testspecs = [small] + ieee + [base] | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |     elif args.time == 'long': | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         base['expts'].append(('rand', 'rand')) | 
					
						
							|  |  |  |         # 10 random precisions | 
					
						
							|  |  |  |         base['samples'] = 10 | 
					
						
							|  |  |  |         testspecs = [small] + ieee + [base] | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |     elif args.time == 'all': | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |         base['expts'].append(('rand', 'rand')) | 
					
						
							|  |  |  |         # All precisions in [1, 100] | 
					
						
							|  |  |  |         base['samples'] = 100 | 
					
						
							|  |  |  |         testspecs = [small] + ieee + [base] | 
					
						
							|  |  |  |     else: # --short | 
					
						
							|  |  |  |         rand_ieee = random.choice(ieee) | 
					
						
							|  |  |  |         base['iter'] = small['iter'] = rand_ieee['iter'] = 1 | 
					
						
							|  |  |  |         # 1 random precision and exponent pair | 
					
						
							|  |  |  |         base['samples'] = 1 | 
					
						
							|  |  |  |         base['expts'] = [random.choice(base_expts)] | 
					
						
							|  |  |  |         # 1 random precision and exponent pair | 
					
						
							|  |  |  |         prec = random.randrange(1, 6) | 
					
						
							|  |  |  |         small['prec'] = [prec] | 
					
						
							|  |  |  |         small['expts'] = [(-prec, prec)] | 
					
						
							|  |  |  |         testspecs = [small, rand_ieee, base] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  |     check_untested(Functions, C.Decimal, P.Decimal) | 
					
						
							|  |  |  |     check_untested(ContextFunctions, C.Context, P.Context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |     if args.multicore: | 
					
						
							|  |  |  |         q = Queue() | 
					
						
							|  |  |  |     elif args.single: | 
					
						
							| 
									
										
										
										
											2020-08-11 21:14:51 +02:00
										 |  |  |         log("Random seed: %d", RANDSEED) | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2020-08-11 21:14:51 +02:00
										 |  |  |         log("\n\nRandom seed: %d\n\n", RANDSEED) | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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() | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Decimal methods: | 
					
						
							|  |  |  |     for method in Functions['unary'] + Functions['unary_ctx'] + \ | 
					
						
							|  |  |  |                   Functions['unary_rnd_ctx']: | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |         do_single(method, lambda: test_method(method, testspecs, test_unary)) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for method in Functions['binary'] + Functions['binary_ctx']: | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |         do_single(method, lambda: test_method(method, testspecs, test_binary)) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for method in Functions['ternary'] + Functions['ternary_ctx']: | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |         name = '__powmod__' if method == '__pow__' else method | 
					
						
							|  |  |  |         do_single(name, lambda: test_method(method, testspecs, test_ternary)) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |     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)) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Context methods: | 
					
						
							|  |  |  |     for method in ContextFunctions['unary']: | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |         do_single(method, lambda: test_method(method, testspecs, test_unary)) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for method in ContextFunctions['binary']: | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |         do_single(method, lambda: test_method(method, testspecs, test_binary)) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for method in ContextFunctions['ternary']: | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |         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() | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |                 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) | 
					
						
							| 
									
										
										
										
											2012-03-21 18:25:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 03:14:57 +02:00
										 |  |  |         N = os.process_cpu_count() | 
					
						
							| 
									
										
										
										
											2020-06-08 19:33:12 +02:00
										 |  |  |         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) |