diff --git a/.gitattributes b/.gitattributes index d654721239318e..e9cf494f485e0d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -26,7 +26,7 @@ PC/classicAppCompat.* binary Lib/test/cjkencodings/* noeol Lib/test/tokenizedata/coding20731.py noeol -Lib/test/decimaltestdata/*.decTest noeol +Lib/test/test_decimal/data/*.decTest noeol Lib/test/test_email/data/*.txt noeol Lib/test/xmltestdata/* noeol diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py deleted file mode 100644 index b520b062ebc685..00000000000000 --- a/Lib/test/test_decimal.py +++ /dev/null @@ -1,6042 +0,0 @@ -# Copyright (c) 2004 Python Software Foundation. -# All rights reserved. - -# Written by Eric Price -# and Facundo Batista -# and Raymond Hettinger -# and Aahz (aahz at pobox.com) -# and Tim Peters - -""" -These are the test cases for the Decimal module. - -There are two groups of tests, Arithmetic and Behaviour. The former test -the Decimal arithmetic using the tests provided by Mike Cowlishaw. The latter -test the pythonic behaviour according to PEP 327. - -Cowlishaw's tests can be downloaded from: - - http://speleotrove.com/decimal/dectest.zip - -This test module can be called from command line with one parameter (Arithmetic -or Behaviour) to test each part, or without parameter to test both parts. If -you're working through IDLE, you can import this test module and call test() -with the corresponding argument. -""" - -import logging -import math -import os, sys -import operator -import pickle, copy -import unittest -import numbers -import locale -from test.support import (is_resource_enabled, - requires_IEEE_754, requires_docstrings, - check_disallow_instantiation) -from test.support import (TestFailed, - run_with_locale, cpython_only, - darwin_malloc_err_warning) -from test.support.import_helper import import_fresh_module -from test.support import threading_helper -from test.support import warnings_helper -import random -import inspect -import threading -import contextvars - - -if sys.platform == 'darwin': - darwin_malloc_err_warning('test_decimal') - - -C = import_fresh_module('decimal', fresh=['_decimal']) -P = import_fresh_module('decimal', blocked=['_decimal']) -import decimal as orig_sys_decimal - -# fractions module must import the correct decimal module. -cfractions = import_fresh_module('fractions', fresh=['fractions']) -sys.modules['decimal'] = P -pfractions = import_fresh_module('fractions', fresh=['fractions']) -sys.modules['decimal'] = C -fractions = {C:cfractions, P:pfractions} -sys.modules['decimal'] = orig_sys_decimal - -requires_cdecimal = unittest.skipUnless(C, "test requires C version") - -# Useful Test Constant -Signals = { - C: tuple(C.getcontext().flags.keys()) if C else None, - P: tuple(P.getcontext().flags.keys()) -} -# Signals ordered with respect to precedence: when an operation -# produces multiple signals, signals occurring later in the list -# should be handled before those occurring earlier in the list. -OrderedSignals = { - C: [C.Clamped, C.Rounded, C.Inexact, C.Subnormal, C.Underflow, - C.Overflow, C.DivisionByZero, C.InvalidOperation, - C.FloatOperation] if C else None, - P: [P.Clamped, P.Rounded, P.Inexact, P.Subnormal, P.Underflow, - P.Overflow, P.DivisionByZero, P.InvalidOperation, - P.FloatOperation] -} -def assert_signals(cls, context, attr, expected): - d = getattr(context, attr) - cls.assertTrue(all(d[s] if s in expected else not d[s] for s in d)) - -ROUND_UP = P.ROUND_UP -ROUND_DOWN = P.ROUND_DOWN -ROUND_CEILING = P.ROUND_CEILING -ROUND_FLOOR = P.ROUND_FLOOR -ROUND_HALF_UP = P.ROUND_HALF_UP -ROUND_HALF_DOWN = P.ROUND_HALF_DOWN -ROUND_HALF_EVEN = P.ROUND_HALF_EVEN -ROUND_05UP = P.ROUND_05UP - -RoundingModes = [ - ROUND_UP, ROUND_DOWN, ROUND_CEILING, ROUND_FLOOR, - ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN, - ROUND_05UP -] - -# Tests are built around these assumed context defaults. -# test() restores the original context. -ORIGINAL_CONTEXT = { - C: C.getcontext().copy() if C else None, - P: P.getcontext().copy() -} -def init(m): - if not m: return - DefaultTestContext = m.Context( - prec=9, rounding=ROUND_HALF_EVEN, traps=dict.fromkeys(Signals[m], 0) - ) - m.setcontext(DefaultTestContext) - -TESTDATADIR = 'decimaltestdata' -if __name__ == '__main__': - file = sys.argv[0] -else: - file = __file__ -testdir = os.path.dirname(file) or os.curdir -directory = testdir + os.sep + TESTDATADIR + os.sep - -skip_expected = not os.path.isdir(directory) - -# Make sure it actually raises errors when not expected and caught in flags -# Slower, since it runs some things several times. -EXTENDEDERRORTEST = False - -# Test extra functionality in the C version (-DEXTRA_FUNCTIONALITY). -EXTRA_FUNCTIONALITY = True if hasattr(C, 'DecClamped') else False -requires_extra_functionality = unittest.skipUnless( - EXTRA_FUNCTIONALITY, "test requires build with -DEXTRA_FUNCTIONALITY") -skip_if_extra_functionality = unittest.skipIf( - EXTRA_FUNCTIONALITY, "test requires regular build") - - -class IBMTestCases: - """Class which tests the Decimal class against the IBM test cases.""" - - def setUp(self): - self.context = self.decimal.Context() - self.readcontext = self.decimal.Context() - self.ignore_list = ['#'] - - # List of individual .decTest test ids that correspond to tests that - # we're skipping for one reason or another. - self.skipped_test_ids = set([ - # Skip implementation-specific scaleb tests. - 'scbx164', - 'scbx165', - - # For some operations (currently exp, ln, log10, power), the decNumber - # reference implementation imposes additional restrictions on the context - # and operands. These restrictions are not part of the specification; - # however, the effect of these restrictions does show up in some of the - # testcases. We skip testcases that violate these restrictions, since - # Decimal behaves differently from decNumber for these testcases so these - # testcases would otherwise fail. - 'expx901', - 'expx902', - 'expx903', - 'expx905', - 'lnx901', - 'lnx902', - 'lnx903', - 'lnx905', - 'logx901', - 'logx902', - 'logx903', - 'logx905', - 'powx1183', - 'powx1184', - 'powx4001', - 'powx4002', - 'powx4003', - 'powx4005', - 'powx4008', - 'powx4010', - 'powx4012', - 'powx4014', - ]) - - if self.decimal == C: - # status has additional Subnormal, Underflow - self.skipped_test_ids.add('pwsx803') - self.skipped_test_ids.add('pwsx805') - # Correct rounding (skipped for decNumber, too) - self.skipped_test_ids.add('powx4302') - self.skipped_test_ids.add('powx4303') - self.skipped_test_ids.add('powx4342') - self.skipped_test_ids.add('powx4343') - # http://bugs.python.org/issue7049 - self.skipped_test_ids.add('pwmx325') - self.skipped_test_ids.add('pwmx326') - - # Map test directives to setter functions. - self.ChangeDict = {'precision' : self.change_precision, - 'rounding' : self.change_rounding_method, - 'maxexponent' : self.change_max_exponent, - 'minexponent' : self.change_min_exponent, - 'clamp' : self.change_clamp} - - # Name adapter to be able to change the Decimal and Context - # interface without changing the test files from Cowlishaw. - self.NameAdapter = {'and':'logical_and', - 'apply':'_apply', - 'class':'number_class', - 'comparesig':'compare_signal', - 'comparetotal':'compare_total', - 'comparetotmag':'compare_total_mag', - 'copy':'copy_decimal', - 'copyabs':'copy_abs', - 'copynegate':'copy_negate', - 'copysign':'copy_sign', - 'divideint':'divide_int', - 'invert':'logical_invert', - 'iscanonical':'is_canonical', - 'isfinite':'is_finite', - 'isinfinite':'is_infinite', - 'isnan':'is_nan', - 'isnormal':'is_normal', - 'isqnan':'is_qnan', - 'issigned':'is_signed', - 'issnan':'is_snan', - 'issubnormal':'is_subnormal', - 'iszero':'is_zero', - 'maxmag':'max_mag', - 'minmag':'min_mag', - 'nextminus':'next_minus', - 'nextplus':'next_plus', - 'nexttoward':'next_toward', - 'or':'logical_or', - 'reduce':'normalize', - 'remaindernear':'remainder_near', - 'samequantum':'same_quantum', - 'squareroot':'sqrt', - 'toeng':'to_eng_string', - 'tointegral':'to_integral_value', - 'tointegralx':'to_integral_exact', - 'tosci':'to_sci_string', - 'xor':'logical_xor'} - - # Map test-case names to roundings. - self.RoundingDict = {'ceiling' : ROUND_CEILING, - 'down' : ROUND_DOWN, - 'floor' : ROUND_FLOOR, - 'half_down' : ROUND_HALF_DOWN, - 'half_even' : ROUND_HALF_EVEN, - 'half_up' : ROUND_HALF_UP, - 'up' : ROUND_UP, - '05up' : ROUND_05UP} - - # Map the test cases' error names to the actual errors. - self.ErrorNames = {'clamped' : self.decimal.Clamped, - 'conversion_syntax' : self.decimal.InvalidOperation, - 'division_by_zero' : self.decimal.DivisionByZero, - 'division_impossible' : self.decimal.InvalidOperation, - 'division_undefined' : self.decimal.InvalidOperation, - 'inexact' : self.decimal.Inexact, - 'invalid_context' : self.decimal.InvalidOperation, - 'invalid_operation' : self.decimal.InvalidOperation, - 'overflow' : self.decimal.Overflow, - 'rounded' : self.decimal.Rounded, - 'subnormal' : self.decimal.Subnormal, - 'underflow' : self.decimal.Underflow} - - # The following functions return True/False rather than a - # Decimal instance. - self.LogicalFunctions = ('is_canonical', - 'is_finite', - 'is_infinite', - 'is_nan', - 'is_normal', - 'is_qnan', - 'is_signed', - 'is_snan', - 'is_subnormal', - 'is_zero', - 'same_quantum') - - def read_unlimited(self, v, context): - """Work around the limitations of the 32-bit _decimal version. The - guaranteed maximum values for prec, Emax etc. are 425000000, - but higher values usually work, except for rare corner cases. - In particular, all of the IBM tests pass with maximum values - of 1070000000.""" - if self.decimal == C and self.decimal.MAX_EMAX == 425000000: - self.readcontext._unsafe_setprec(1070000000) - self.readcontext._unsafe_setemax(1070000000) - self.readcontext._unsafe_setemin(-1070000000) - return self.readcontext.create_decimal(v) - else: - return self.decimal.Decimal(v, context) - - def eval_file(self, file): - global skip_expected - if skip_expected: - raise unittest.SkipTest - with open(file, encoding="utf-8") as f: - for line in f: - line = line.replace('\r\n', '').replace('\n', '') - #print line - try: - t = self.eval_line(line) - except self.decimal.DecimalException as exception: - #Exception raised where there shouldn't have been one. - self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line) - - - def eval_line(self, s): - if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'): - s = (s.split('->')[0] + '->' + - s.split('->')[1].split('--')[0]).strip() - else: - s = s.split('--')[0].strip() - - for ignore in self.ignore_list: - if s.find(ignore) >= 0: - #print s.split()[0], 'NotImplemented--', ignore - return - if not s: - return - elif ':' in s: - return self.eval_directive(s) - else: - return self.eval_equation(s) - - def eval_directive(self, s): - funct, value = (x.strip().lower() for x in s.split(':')) - if funct == 'rounding': - value = self.RoundingDict[value] - else: - try: - value = int(value) - except ValueError: - pass - - funct = self.ChangeDict.get(funct, (lambda *args: None)) - funct(value) - - def eval_equation(self, s): - - if not TEST_ALL and random.random() < 0.90: - return - - self.context.clear_flags() - - try: - Sides = s.split('->') - L = Sides[0].strip().split() - id = L[0] - if DEBUG: - print("Test ", id, end=" ") - funct = L[1].lower() - valstemp = L[2:] - L = Sides[1].strip().split() - ans = L[0] - exceptions = L[1:] - except (TypeError, AttributeError, IndexError): - raise self.decimal.InvalidOperation - def FixQuotes(val): - val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote') - val = val.replace("'", '').replace('"', '') - val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"') - return val - - if id in self.skipped_test_ids: - return - - fname = self.NameAdapter.get(funct, funct) - if fname == 'rescale': - return - funct = getattr(self.context, fname) - vals = [] - conglomerate = '' - quote = 0 - theirexceptions = [self.ErrorNames[x.lower()] for x in exceptions] - - for exception in Signals[self.decimal]: - self.context.traps[exception] = 1 #Catch these bugs... - for exception in theirexceptions: - self.context.traps[exception] = 0 - for i, val in enumerate(valstemp): - if val.count("'") % 2 == 1: - quote = 1 - quote - if quote: - conglomerate = conglomerate + ' ' + val - continue - else: - val = conglomerate + val - conglomerate = '' - v = FixQuotes(val) - if fname in ('to_sci_string', 'to_eng_string'): - if EXTENDEDERRORTEST: - for error in theirexceptions: - self.context.traps[error] = 1 - try: - funct(self.context.create_decimal(v)) - except error: - pass - except Signals[self.decimal] as e: - self.fail("Raised %s in %s when %s disabled" % \ - (e, s, error)) - else: - self.fail("Did not raise %s in %s" % (error, s)) - self.context.traps[error] = 0 - v = self.context.create_decimal(v) - else: - v = self.read_unlimited(v, self.context) - vals.append(v) - - ans = FixQuotes(ans) - - if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'): - for error in theirexceptions: - self.context.traps[error] = 1 - try: - funct(*vals) - except error: - pass - except Signals[self.decimal] as e: - self.fail("Raised %s in %s when %s disabled" % \ - (e, s, error)) - else: - self.fail("Did not raise %s in %s" % (error, s)) - self.context.traps[error] = 0 - - # as above, but add traps cumulatively, to check precedence - ordered_errors = [e for e in OrderedSignals[self.decimal] if e in theirexceptions] - for error in ordered_errors: - self.context.traps[error] = 1 - try: - funct(*vals) - except error: - pass - except Signals[self.decimal] as e: - self.fail("Raised %s in %s; expected %s" % - (type(e), s, error)) - else: - self.fail("Did not raise %s in %s" % (error, s)) - # reset traps - for error in ordered_errors: - self.context.traps[error] = 0 - - - if DEBUG: - print("--", self.context) - try: - result = str(funct(*vals)) - if fname in self.LogicalFunctions: - result = str(int(eval(result))) # 'True', 'False' -> '1', '0' - except Signals[self.decimal] as error: - self.fail("Raised %s in %s" % (error, s)) - except: #Catch any error long enough to state the test case. - print("ERROR:", s) - raise - - myexceptions = self.getexceptions() - - myexceptions.sort(key=repr) - theirexceptions.sort(key=repr) - - self.assertEqual(result, ans, - 'Incorrect answer for ' + s + ' -- got ' + result) - - self.assertEqual(myexceptions, theirexceptions, - 'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions)) - - def getexceptions(self): - return [e for e in Signals[self.decimal] if self.context.flags[e]] - - def change_precision(self, prec): - if self.decimal == C and self.decimal.MAX_PREC == 425000000: - self.context._unsafe_setprec(prec) - else: - self.context.prec = prec - def change_rounding_method(self, rounding): - self.context.rounding = rounding - def change_min_exponent(self, exp): - if self.decimal == C and self.decimal.MAX_PREC == 425000000: - self.context._unsafe_setemin(exp) - else: - self.context.Emin = exp - def change_max_exponent(self, exp): - if self.decimal == C and self.decimal.MAX_PREC == 425000000: - self.context._unsafe_setemax(exp) - else: - self.context.Emax = exp - def change_clamp(self, clamp): - self.context.clamp = clamp - - -# The following classes test the behaviour of Decimal according to PEP 327 - -class ExplicitConstructionTest: - '''Unit tests for Explicit Construction cases of Decimal.''' - - def test_explicit_empty(self): - Decimal = self.decimal.Decimal - self.assertEqual(Decimal(), Decimal("0")) - - def test_explicit_from_None(self): - Decimal = self.decimal.Decimal - self.assertRaises(TypeError, Decimal, None) - - def test_explicit_from_int(self): - Decimal = self.decimal.Decimal - - #positive - d = Decimal(45) - self.assertEqual(str(d), '45') - - #very large positive - d = Decimal(500000123) - self.assertEqual(str(d), '500000123') - - #negative - d = Decimal(-45) - self.assertEqual(str(d), '-45') - - #zero - d = Decimal(0) - self.assertEqual(str(d), '0') - - # single word longs - for n in range(0, 32): - for sign in (-1, 1): - for x in range(-5, 5): - i = sign * (2**n + x) - d = Decimal(i) - self.assertEqual(str(d), str(i)) - - def test_explicit_from_string(self): - Decimal = self.decimal.Decimal - InvalidOperation = self.decimal.InvalidOperation - localcontext = self.decimal.localcontext - - #empty - self.assertEqual(str(Decimal('')), 'NaN') - - #int - self.assertEqual(str(Decimal('45')), '45') - - #float - self.assertEqual(str(Decimal('45.34')), '45.34') - - #engineer notation - self.assertEqual(str(Decimal('45e2')), '4.5E+3') - - #just not a number - self.assertEqual(str(Decimal('ugly')), 'NaN') - - #leading and trailing whitespace permitted - self.assertEqual(str(Decimal('1.3E4 \n')), '1.3E+4') - self.assertEqual(str(Decimal(' -7.89')), '-7.89') - self.assertEqual(str(Decimal(" 3.45679 ")), '3.45679') - - # underscores - self.assertEqual(str(Decimal('1_3.3e4_0')), '1.33E+41') - self.assertEqual(str(Decimal('1_0_0_0')), '1000') - - # unicode whitespace - for lead in ["", ' ', '\u00a0', '\u205f']: - for trail in ["", ' ', '\u00a0', '\u205f']: - self.assertEqual(str(Decimal(lead + '9.311E+28' + trail)), - '9.311E+28') - - with localcontext() as c: - c.traps[InvalidOperation] = True - # Invalid string - self.assertRaises(InvalidOperation, Decimal, "xyz") - # Two arguments max - self.assertRaises(TypeError, Decimal, "1234", "x", "y") - - # space within the numeric part - self.assertRaises(InvalidOperation, Decimal, "1\u00a02\u00a03") - self.assertRaises(InvalidOperation, Decimal, "\u00a01\u00a02\u00a0") - - # unicode whitespace - self.assertRaises(InvalidOperation, Decimal, "\u00a0") - self.assertRaises(InvalidOperation, Decimal, "\u00a0\u00a0") - - # embedded NUL - self.assertRaises(InvalidOperation, Decimal, "12\u00003") - - # underscores don't prevent errors - self.assertRaises(InvalidOperation, Decimal, "1_2_\u00003") - - def test_explicit_from_tuples(self): - Decimal = self.decimal.Decimal - - #zero - d = Decimal( (0, (0,), 0) ) - self.assertEqual(str(d), '0') - - #int - d = Decimal( (1, (4, 5), 0) ) - self.assertEqual(str(d), '-45') - - #float - d = Decimal( (0, (4, 5, 3, 4), -2) ) - self.assertEqual(str(d), '45.34') - - #weird - d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) - self.assertEqual(str(d), '-4.34913534E-17') - - #inf - d = Decimal( (0, (), "F") ) - self.assertEqual(str(d), 'Infinity') - - #wrong number of items - self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) ) - - #bad sign - self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) ) - self.assertRaises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) ) - self.assertRaises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2)) - - #bad exp - self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') ) - self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) ) - self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') ) - - #bad coefficients - self.assertRaises(ValueError, Decimal, (1, "xyz", 2) ) - self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) ) - self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) ) - self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) ) - self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) ) - - def test_explicit_from_list(self): - Decimal = self.decimal.Decimal - - d = Decimal([0, [0], 0]) - self.assertEqual(str(d), '0') - - d = Decimal([1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25]) - self.assertEqual(str(d), '-4.34913534E-17') - - d = Decimal([1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25]) - self.assertEqual(str(d), '-4.34913534E-17') - - d = Decimal((1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25)) - self.assertEqual(str(d), '-4.34913534E-17') - - def test_explicit_from_bool(self): - Decimal = self.decimal.Decimal - - self.assertIs(bool(Decimal(0)), False) - self.assertIs(bool(Decimal(1)), True) - self.assertEqual(Decimal(False), Decimal(0)) - self.assertEqual(Decimal(True), Decimal(1)) - - def test_explicit_from_Decimal(self): - Decimal = self.decimal.Decimal - - #positive - d = Decimal(45) - e = Decimal(d) - self.assertEqual(str(e), '45') - - #very large positive - d = Decimal(500000123) - e = Decimal(d) - self.assertEqual(str(e), '500000123') - - #negative - d = Decimal(-45) - e = Decimal(d) - self.assertEqual(str(e), '-45') - - #zero - d = Decimal(0) - e = Decimal(d) - self.assertEqual(str(e), '0') - - @requires_IEEE_754 - def test_explicit_from_float(self): - - Decimal = self.decimal.Decimal - - r = Decimal(0.1) - self.assertEqual(type(r), Decimal) - self.assertEqual(str(r), - '0.1000000000000000055511151231257827021181583404541015625') - self.assertTrue(Decimal(float('nan')).is_qnan()) - self.assertTrue(Decimal(float('inf')).is_infinite()) - self.assertTrue(Decimal(float('-inf')).is_infinite()) - self.assertEqual(str(Decimal(float('nan'))), - str(Decimal('NaN'))) - self.assertEqual(str(Decimal(float('inf'))), - str(Decimal('Infinity'))) - self.assertEqual(str(Decimal(float('-inf'))), - str(Decimal('-Infinity'))) - self.assertEqual(str(Decimal(float('-0.0'))), - str(Decimal('-0'))) - for i in range(200): - x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) - self.assertEqual(x, float(Decimal(x))) # roundtrip - - def test_explicit_context_create_decimal(self): - Decimal = self.decimal.Decimal - InvalidOperation = self.decimal.InvalidOperation - Rounded = self.decimal.Rounded - - nc = copy.copy(self.decimal.getcontext()) - nc.prec = 3 - - # empty - d = Decimal() - self.assertEqual(str(d), '0') - d = nc.create_decimal() - self.assertEqual(str(d), '0') - - # from None - self.assertRaises(TypeError, nc.create_decimal, None) - - # from int - d = nc.create_decimal(456) - self.assertIsInstance(d, Decimal) - self.assertEqual(nc.create_decimal(45678), - nc.create_decimal('457E+2')) - - # from string - d = Decimal('456789') - self.assertEqual(str(d), '456789') - d = nc.create_decimal('456789') - self.assertEqual(str(d), '4.57E+5') - # leading and trailing whitespace should result in a NaN; - # spaces are already checked in Cowlishaw's test-suite, so - # here we just check that a trailing newline results in a NaN - self.assertEqual(str(nc.create_decimal('3.14\n')), 'NaN') - - # from tuples - d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) - self.assertEqual(str(d), '-4.34913534E-17') - d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) - self.assertEqual(str(d), '-4.35E-17') - - # from Decimal - prevdec = Decimal(500000123) - d = Decimal(prevdec) - self.assertEqual(str(d), '500000123') - d = nc.create_decimal(prevdec) - self.assertEqual(str(d), '5.00E+8') - - # more integers - nc.prec = 28 - nc.traps[InvalidOperation] = True - - for v in [-2**63-1, -2**63, -2**31-1, -2**31, 0, - 2**31-1, 2**31, 2**63-1, 2**63]: - d = nc.create_decimal(v) - self.assertIsInstance(d, Decimal) - self.assertEqual(int(d), v) - - nc.prec = 3 - nc.traps[Rounded] = True - self.assertRaises(Rounded, nc.create_decimal, 1234) - - # from string - nc.prec = 28 - self.assertEqual(str(nc.create_decimal('0E-017')), '0E-17') - self.assertEqual(str(nc.create_decimal('45')), '45') - self.assertEqual(str(nc.create_decimal('-Inf')), '-Infinity') - self.assertEqual(str(nc.create_decimal('NaN123')), 'NaN123') - - # invalid arguments - self.assertRaises(InvalidOperation, nc.create_decimal, "xyz") - self.assertRaises(ValueError, nc.create_decimal, (1, "xyz", -25)) - self.assertRaises(TypeError, nc.create_decimal, "1234", "5678") - # no whitespace and underscore stripping is done with this method - self.assertRaises(InvalidOperation, nc.create_decimal, " 1234") - self.assertRaises(InvalidOperation, nc.create_decimal, "12_34") - - # too many NaN payload digits - nc.prec = 3 - self.assertRaises(InvalidOperation, nc.create_decimal, 'NaN12345') - self.assertRaises(InvalidOperation, nc.create_decimal, - Decimal('NaN12345')) - - nc.traps[InvalidOperation] = False - self.assertEqual(str(nc.create_decimal('NaN12345')), 'NaN') - self.assertTrue(nc.flags[InvalidOperation]) - - nc.flags[InvalidOperation] = False - self.assertEqual(str(nc.create_decimal(Decimal('NaN12345'))), 'NaN') - self.assertTrue(nc.flags[InvalidOperation]) - - def test_explicit_context_create_from_float(self): - - Decimal = self.decimal.Decimal - - nc = self.decimal.Context() - r = nc.create_decimal(0.1) - self.assertEqual(type(r), Decimal) - self.assertEqual(str(r), '0.1000000000000000055511151231') - self.assertTrue(nc.create_decimal(float('nan')).is_qnan()) - self.assertTrue(nc.create_decimal(float('inf')).is_infinite()) - self.assertTrue(nc.create_decimal(float('-inf')).is_infinite()) - self.assertEqual(str(nc.create_decimal(float('nan'))), - str(nc.create_decimal('NaN'))) - self.assertEqual(str(nc.create_decimal(float('inf'))), - str(nc.create_decimal('Infinity'))) - self.assertEqual(str(nc.create_decimal(float('-inf'))), - str(nc.create_decimal('-Infinity'))) - self.assertEqual(str(nc.create_decimal(float('-0.0'))), - str(nc.create_decimal('-0'))) - nc.prec = 100 - for i in range(200): - x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) - self.assertEqual(x, float(nc.create_decimal(x))) # roundtrip - - def test_from_number(self, cls=None): - Decimal = self.decimal.Decimal - if cls is None: - cls = Decimal - - def check(arg, expected): - d = cls.from_number(arg) - self.assertIs(type(d), cls) - self.assertEqual(d, expected) - - check(314, Decimal(314)) - check(3.14, Decimal.from_float(3.14)) - check(Decimal('3.14'), Decimal('3.14')) - self.assertRaises(TypeError, cls.from_number, 3+4j) - self.assertRaises(TypeError, cls.from_number, '314') - self.assertRaises(TypeError, cls.from_number, (0, (3, 1, 4), 0)) - self.assertRaises(TypeError, cls.from_number, object()) - - def test_from_number_subclass(self, cls=None): - class DecimalSubclass(self.decimal.Decimal): - pass - self.test_from_number(DecimalSubclass) - - def test_unicode_digits(self): - Decimal = self.decimal.Decimal - - test_values = { - '\uff11': '1', - '\u0660.\u0660\u0663\u0667\u0662e-\u0663' : '0.0000372', - '-nan\u0c68\u0c6a\u0c66\u0c66' : '-NaN2400', - } - for input, expected in test_values.items(): - self.assertEqual(str(Decimal(input)), expected) - -@requires_cdecimal -class CExplicitConstructionTest(ExplicitConstructionTest, unittest.TestCase): - decimal = C -class PyExplicitConstructionTest(ExplicitConstructionTest, unittest.TestCase): - decimal = P - -class ImplicitConstructionTest: - '''Unit tests for Implicit Construction cases of Decimal.''' - - def test_implicit_from_None(self): - Decimal = self.decimal.Decimal - self.assertRaises(TypeError, eval, 'Decimal(5) + None', locals()) - - def test_implicit_from_int(self): - Decimal = self.decimal.Decimal - - #normal - self.assertEqual(str(Decimal(5) + 45), '50') - #exceeding precision - self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000)) - - def test_implicit_from_string(self): - Decimal = self.decimal.Decimal - self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', locals()) - - def test_implicit_from_float(self): - Decimal = self.decimal.Decimal - self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', locals()) - - def test_implicit_from_Decimal(self): - Decimal = self.decimal.Decimal - self.assertEqual(Decimal(5) + Decimal(45), Decimal(50)) - - def test_rop(self): - Decimal = self.decimal.Decimal - - # Allow other classes to be trained to interact with Decimals - class E: - def __divmod__(self, other): - return 'divmod ' + str(other) - def __rdivmod__(self, other): - return str(other) + ' rdivmod' - def __lt__(self, other): - return 'lt ' + str(other) - def __gt__(self, other): - return 'gt ' + str(other) - def __le__(self, other): - return 'le ' + str(other) - def __ge__(self, other): - return 'ge ' + str(other) - def __eq__(self, other): - return 'eq ' + str(other) - def __ne__(self, other): - return 'ne ' + str(other) - - self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10') - self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod') - self.assertEqual(eval('Decimal(10) < E()'), 'gt 10') - self.assertEqual(eval('Decimal(10) > E()'), 'lt 10') - self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10') - self.assertEqual(eval('Decimal(10) >= E()'), 'le 10') - self.assertEqual(eval('Decimal(10) == E()'), 'eq 10') - self.assertEqual(eval('Decimal(10) != E()'), 'ne 10') - - # insert operator methods and then exercise them - oplist = [ - ('+', '__add__', '__radd__'), - ('-', '__sub__', '__rsub__'), - ('*', '__mul__', '__rmul__'), - ('/', '__truediv__', '__rtruediv__'), - ('%', '__mod__', '__rmod__'), - ('//', '__floordiv__', '__rfloordiv__'), - ('**', '__pow__', '__rpow__') - ] - - for sym, lop, rop in oplist: - setattr(E, lop, lambda self, other: 'str' + lop + str(other)) - setattr(E, rop, lambda self, other: str(other) + rop + 'str') - self.assertEqual(eval('E()' + sym + 'Decimal(10)'), - 'str' + lop + '10') - self.assertEqual(eval('Decimal(10)' + sym + 'E()'), - '10' + rop + 'str') - -@requires_cdecimal -class CImplicitConstructionTest(ImplicitConstructionTest, unittest.TestCase): - decimal = C -class PyImplicitConstructionTest(ImplicitConstructionTest, unittest.TestCase): - decimal = P - -class FormatTest: - '''Unit tests for the format function.''' - def test_formatting(self): - Decimal = self.decimal.Decimal - - # triples giving a format, a Decimal, and the expected result - test_values = [ - ('e', '0E-15', '0e-15'), - ('e', '2.3E-15', '2.3e-15'), - ('e', '2.30E+2', '2.30e+2'), # preserve significant zeros - ('e', '2.30000E-15', '2.30000e-15'), - ('e', '1.23456789123456789e40', '1.23456789123456789e+40'), - ('e', '1.5', '1.5e+0'), - ('e', '0.15', '1.5e-1'), - ('e', '0.015', '1.5e-2'), - ('e', '0.0000000000015', '1.5e-12'), - ('e', '15.0', '1.50e+1'), - ('e', '-15', '-1.5e+1'), - ('e', '0', '0e+0'), - ('e', '0E1', '0e+1'), - ('e', '0.0', '0e-1'), - ('e', '0.00', '0e-2'), - ('.6e', '0E-15', '0.000000e-9'), - ('.6e', '0', '0.000000e+6'), - ('.6e', '9.999999', '9.999999e+0'), - ('.6e', '9.9999999', '1.000000e+1'), - ('.6e', '-1.23e5', '-1.230000e+5'), - ('.6e', '1.23456789e-3', '1.234568e-3'), - ('f', '0', '0'), - ('f', '0.0', '0.0'), - ('f', '0E-2', '0.00'), - ('f', '0.00E-8', '0.0000000000'), - ('f', '0E1', '0'), # loses exponent information - ('f', '3.2E1', '32'), - ('f', '3.2E2', '320'), - ('f', '3.20E2', '320'), - ('f', '3.200E2', '320.0'), - ('f', '3.2E-6', '0.0000032'), - ('.6f', '0E-15', '0.000000'), # all zeros treated equally - ('.6f', '0E1', '0.000000'), - ('.6f', '0', '0.000000'), - ('.0f', '0', '0'), # no decimal point - ('.0f', '0e-2', '0'), - ('.0f', '3.14159265', '3'), - ('.1f', '3.14159265', '3.1'), - ('.01f', '3.14159265', '3.1'), # leading zero in precision - ('.4f', '3.14159265', '3.1416'), - ('.6f', '3.14159265', '3.141593'), - ('.7f', '3.14159265', '3.1415926'), # round-half-even! - ('.8f', '3.14159265', '3.14159265'), - ('.9f', '3.14159265', '3.141592650'), - - ('g', '0', '0'), - ('g', '0.0', '0.0'), - ('g', '0E1', '0e+1'), - ('G', '0E1', '0E+1'), - ('g', '0E-5', '0.00000'), - ('g', '0E-6', '0.000000'), - ('g', '0E-7', '0e-7'), - ('g', '-0E2', '-0e+2'), - ('.0g', '3.14159265', '3'), # 0 sig fig -> 1 sig fig - ('.0n', '3.14159265', '3'), # same for 'n' - ('.1g', '3.14159265', '3'), - ('.2g', '3.14159265', '3.1'), - ('.5g', '3.14159265', '3.1416'), - ('.7g', '3.14159265', '3.141593'), - ('.8g', '3.14159265', '3.1415926'), # round-half-even! - ('.9g', '3.14159265', '3.14159265'), - ('.10g', '3.14159265', '3.14159265'), # don't pad - - ('%', '0E1', '0%'), - ('%', '0E0', '0%'), - ('%', '0E-1', '0%'), - ('%', '0E-2', '0%'), - ('%', '0E-3', '0.0%'), - ('%', '0E-4', '0.00%'), - - ('.3%', '0', '0.000%'), # all zeros treated equally - ('.3%', '0E10', '0.000%'), - ('.3%', '0E-10', '0.000%'), - ('.3%', '2.34', '234.000%'), - ('.3%', '1.234567', '123.457%'), - ('.0%', '1.23', '123%'), - - ('e', 'NaN', 'NaN'), - ('f', '-NaN123', '-NaN123'), - ('+g', 'NaN456', '+NaN456'), - ('.3e', 'Inf', 'Infinity'), - ('.16f', '-Inf', '-Infinity'), - ('.0g', '-sNaN', '-sNaN'), - - ('', '1.00', '1.00'), - - # test alignment and padding - ('6', '123', ' 123'), - ('<6', '123', '123 '), - ('>6', '123', ' 123'), - ('^6', '123', ' 123 '), - ('=+6', '123', '+ 123'), - ('#<10', 'NaN', 'NaN#######'), - ('#<10', '-4.3', '-4.3######'), - ('#<+10', '0.0130', '+0.0130###'), - ('#< 10', '0.0130', ' 0.0130###'), - ('@>10', '-Inf', '@-Infinity'), - ('#>5', '-Inf', '-Infinity'), - ('?^5', '123', '?123?'), - ('%^6', '123', '%123%%'), - (' ^6', '-45.6', '-45.6 '), - ('/=10', '-45.6', '-/////45.6'), - ('/=+10', '45.6', '+/////45.6'), - ('/= 10', '45.6', ' /////45.6'), - ('\x00=10', '-inf', '-\x00Infinity'), - ('\x00^16', '-inf', '\x00\x00\x00-Infinity\x00\x00\x00\x00'), - ('\x00>10', '1.2345', '\x00\x00\x00\x001.2345'), - ('\x00<10', '1.2345', '1.2345\x00\x00\x00\x00'), - - # thousands separator - (',', '1234567', '1,234,567'), - (',', '123456', '123,456'), - (',', '12345', '12,345'), - (',', '1234', '1,234'), - (',', '123', '123'), - (',', '12', '12'), - (',', '1', '1'), - (',', '0', '0'), - (',', '-1234567', '-1,234,567'), - (',', '-123456', '-123,456'), - ('7,', '123456', '123,456'), - ('8,', '123456', ' 123,456'), - ('08,', '123456', '0,123,456'), # special case: extra 0 needed - ('+08,', '123456', '+123,456'), # but not if there's a sign - ('008,', '123456', '0,123,456'), # leading zero in width - (' 08,', '123456', ' 123,456'), - ('08,', '-123456', '-123,456'), - ('+09,', '123456', '+0,123,456'), - # ... with fractional part... - ('07,', '1234.56', '1,234.56'), - ('08,', '1234.56', '1,234.56'), - ('09,', '1234.56', '01,234.56'), - ('010,', '1234.56', '001,234.56'), - ('011,', '1234.56', '0,001,234.56'), - ('012,', '1234.56', '0,001,234.56'), - ('08,.1f', '1234.5', '01,234.5'), - # no thousands separators in fraction part - (',', '1.23456789', '1.23456789'), - (',%', '123.456789', '12,345.6789%'), - (',e', '123456', '1.23456e+5'), - (',E', '123456', '1.23456E+5'), - # ... with '_' instead - ('_', '1234567', '1_234_567'), - ('07_', '1234.56', '1_234.56'), - ('_', '1.23456789', '1.23456789'), - ('_%', '123.456789', '12_345.6789%'), - # and now for something completely different... - ('.,', '1.23456789', '1.234,567,89'), - ('._', '1.23456789', '1.234_567_89'), - ('.6_f', '12345.23456789', '12345.234_568'), - (',._%', '123.456789', '12,345.678_9%'), - (',._e', '123456', '1.234_56e+5'), - (',.4_e', '123456', '1.234_6e+5'), - (',.3_e', '123456', '1.235e+5'), - (',._E', '123456', '1.234_56E+5'), - - # negative zero: default behavior - ('.1f', '-0', '-0.0'), - ('.1f', '-.0', '-0.0'), - ('.1f', '-.01', '-0.0'), - - # negative zero: z option - ('z.1f', '0.', '0.0'), - ('z6.1f', '0.', ' 0.0'), - ('z6.1f', '-1.', ' -1.0'), - ('z.1f', '-0.', '0.0'), - ('z.1f', '.01', '0.0'), - ('z.1f', '-.01', '0.0'), - ('z.2f', '0.', '0.00'), - ('z.2f', '-0.', '0.00'), - ('z.2f', '.001', '0.00'), - ('z.2f', '-.001', '0.00'), - - ('z.1e', '0.', '0.0e+1'), - ('z.1e', '-0.', '0.0e+1'), - ('z.1E', '0.', '0.0E+1'), - ('z.1E', '-0.', '0.0E+1'), - - ('z.2e', '-0.001', '-1.00e-3'), # tests for mishandled rounding - ('z.2g', '-0.001', '-0.001'), - ('z.2%', '-0.001', '-0.10%'), - - ('zf', '-0.0000', '0.0000'), # non-normalized form is preserved - - ('z.1f', '-00000.000001', '0.0'), - ('z.1f', '-00000.', '0.0'), - ('z.1f', '-.0000000000', '0.0'), - - ('z.2f', '-00000.000001', '0.00'), - ('z.2f', '-00000.', '0.00'), - ('z.2f', '-.0000000000', '0.00'), - - ('z.1f', '.09', '0.1'), - ('z.1f', '-.09', '-0.1'), - - (' z.0f', '-0.', ' 0'), - ('+z.0f', '-0.', '+0'), - ('-z.0f', '-0.', '0'), - (' z.0f', '-1.', '-1'), - ('+z.0f', '-1.', '-1'), - ('-z.0f', '-1.', '-1'), - - ('z>6.1f', '-0.', 'zz-0.0'), - ('z>z6.1f', '-0.', 'zzz0.0'), - ('x>z6.1f', '-0.', 'xxx0.0'), - ('🖤>z6.1f', '-0.', '🖤🖤🖤0.0'), # multi-byte fill char - ('\x00>z6.1f', '-0.', '\x00\x00\x000.0'), # null fill char - - # issue 114563 ('z' format on F type in cdecimal) - ('z3,.10F', '-6.24E-323', '0.0000000000'), - - # issue 91060 ('#' format in cdecimal) - ('#', '0', '0.'), - - # issue 6850 - ('a=-7.0', '0.12345', 'aaaa0.1'), - - # issue 22090 - ('<^+15.20%', 'inf', '<<+Infinity%<<<'), - ('\x07>,%', 'sNaN1234567', 'sNaN1234567%'), - ('=10.10%', 'NaN123', ' NaN123%'), - ] - for fmt, d, result in test_values: - self.assertEqual(format(Decimal(d), fmt), result) - - # bytes format argument - self.assertRaises(TypeError, Decimal(1).__format__, b'-020') - - # precision or fractional part separator should follow after dot - self.assertRaises(ValueError, format, Decimal(1), '.f') - self.assertRaises(ValueError, format, Decimal(1), '._6f') - - def test_negative_zero_format_directed_rounding(self): - with self.decimal.localcontext() as ctx: - ctx.rounding = ROUND_CEILING - self.assertEqual(format(self.decimal.Decimal('-0.001'), 'z.2f'), - '0.00') - - def test_negative_zero_bad_format(self): - self.assertRaises(ValueError, format, self.decimal.Decimal('1.23'), 'fz') - - def test_n_format(self): - Decimal = self.decimal.Decimal - - try: - from locale import CHAR_MAX - except ImportError: - self.skipTest('locale.CHAR_MAX not available') - - def make_grouping(lst): - return ''.join([chr(x) for x in lst]) if self.decimal == C else lst - - def get_fmt(x, override=None, fmt='n'): - if self.decimal == C: - return Decimal(x).__format__(fmt, override) - else: - return Decimal(x).__format__(fmt, _localeconv=override) - - # Set up some localeconv-like dictionaries - en_US = { - 'decimal_point' : '.', - 'grouping' : make_grouping([3, 3, 0]), - 'thousands_sep' : ',' - } - - fr_FR = { - 'decimal_point' : ',', - 'grouping' : make_grouping([CHAR_MAX]), - 'thousands_sep' : '' - } - - ru_RU = { - 'decimal_point' : ',', - 'grouping': make_grouping([3, 3, 0]), - 'thousands_sep' : ' ' - } - - crazy = { - 'decimal_point' : '&', - 'grouping': make_grouping([1, 4, 2, CHAR_MAX]), - 'thousands_sep' : '-' - } - - dotsep_wide = { - 'decimal_point' : b'\xc2\xbf'.decode('utf-8'), - 'grouping': make_grouping([3, 3, 0]), - 'thousands_sep' : b'\xc2\xb4'.decode('utf-8') - } - - self.assertEqual(get_fmt(Decimal('12.7'), en_US), '12.7') - self.assertEqual(get_fmt(Decimal('12.7'), fr_FR), '12,7') - self.assertEqual(get_fmt(Decimal('12.7'), ru_RU), '12,7') - self.assertEqual(get_fmt(Decimal('12.7'), crazy), '1-2&7') - - self.assertEqual(get_fmt(123456789, en_US), '123,456,789') - self.assertEqual(get_fmt(123456789, fr_FR), '123456789') - self.assertEqual(get_fmt(123456789, ru_RU), '123 456 789') - self.assertEqual(get_fmt(1234567890123, crazy), '123456-78-9012-3') - - self.assertEqual(get_fmt(123456789, en_US, '.6n'), '1.23457e+8') - self.assertEqual(get_fmt(123456789, fr_FR, '.6n'), '1,23457e+8') - self.assertEqual(get_fmt(123456789, ru_RU, '.6n'), '1,23457e+8') - self.assertEqual(get_fmt(123456789, crazy, '.6n'), '1&23457e+8') - - # zero padding - self.assertEqual(get_fmt(1234, fr_FR, '03n'), '1234') - self.assertEqual(get_fmt(1234, fr_FR, '04n'), '1234') - self.assertEqual(get_fmt(1234, fr_FR, '05n'), '01234') - self.assertEqual(get_fmt(1234, fr_FR, '06n'), '001234') - - self.assertEqual(get_fmt(12345, en_US, '05n'), '12,345') - self.assertEqual(get_fmt(12345, en_US, '06n'), '12,345') - self.assertEqual(get_fmt(12345, en_US, '07n'), '012,345') - self.assertEqual(get_fmt(12345, en_US, '08n'), '0,012,345') - self.assertEqual(get_fmt(12345, en_US, '09n'), '0,012,345') - self.assertEqual(get_fmt(12345, en_US, '010n'), '00,012,345') - - self.assertEqual(get_fmt(123456, crazy, '06n'), '1-2345-6') - self.assertEqual(get_fmt(123456, crazy, '07n'), '1-2345-6') - self.assertEqual(get_fmt(123456, crazy, '08n'), '1-2345-6') - self.assertEqual(get_fmt(123456, crazy, '09n'), '01-2345-6') - self.assertEqual(get_fmt(123456, crazy, '010n'), '0-01-2345-6') - self.assertEqual(get_fmt(123456, crazy, '011n'), '0-01-2345-6') - self.assertEqual(get_fmt(123456, crazy, '012n'), '00-01-2345-6') - self.assertEqual(get_fmt(123456, crazy, '013n'), '000-01-2345-6') - - # wide char separator and decimal point - self.assertEqual(get_fmt(Decimal('-1.5'), dotsep_wide, '020n'), - '-0\u00b4000\u00b4000\u00b4000\u00b4001\u00bf5') - - def test_deprecated_N_format(self): - Decimal = self.decimal.Decimal - h = Decimal('6.62607015e-34') - if self.decimal == C: - with self.assertWarns(DeprecationWarning) as cm: - r = format(h, 'N') - self.assertEqual(cm.filename, __file__) - self.assertEqual(r, format(h, 'n').upper()) - with self.assertWarns(DeprecationWarning) as cm: - r = format(h, '010.3N') - self.assertEqual(cm.filename, __file__) - self.assertEqual(r, format(h, '010.3n').upper()) - else: - self.assertRaises(ValueError, format, h, 'N') - self.assertRaises(ValueError, format, h, '010.3N') - with warnings_helper.check_no_warnings(self): - self.assertEqual(format(h, 'N>10.3'), 'NN6.63E-34') - self.assertEqual(format(h, 'N>10.3n'), 'NN6.63e-34') - self.assertEqual(format(h, 'N>10.3e'), 'N6.626e-34') - self.assertEqual(format(h, 'N>10.3f'), 'NNNNN0.000') - self.assertRaises(ValueError, format, h, '>Nf') - self.assertRaises(ValueError, format, h, '10Nf') - self.assertRaises(ValueError, format, h, 'Nx') - - @run_with_locale('LC_ALL', 'ps_AF', '') - def test_wide_char_separator_decimal_point(self): - # locale with wide char separator and decimal point - Decimal = self.decimal.Decimal - - decimal_point = locale.localeconv()['decimal_point'] - thousands_sep = locale.localeconv()['thousands_sep'] - if decimal_point != '\u066b': - self.skipTest('inappropriate decimal point separator ' - '({!a} not {!a})'.format(decimal_point, '\u066b')) - if thousands_sep != '\u066c': - self.skipTest('inappropriate thousands separator ' - '({!a} not {!a})'.format(thousands_sep, '\u066c')) - - self.assertEqual(format(Decimal('100000000.123'), 'n'), - '100\u066c000\u066c000\u066b123') - - def test_decimal_from_float_argument_type(self): - class A(self.decimal.Decimal): - def __init__(self, a): - self.a_type = type(a) - a = A.from_float(42.5) - self.assertEqual(self.decimal.Decimal, a.a_type) - - a = A.from_float(42) - self.assertEqual(self.decimal.Decimal, a.a_type) - -@requires_cdecimal -class CFormatTest(FormatTest, unittest.TestCase): - decimal = C -class PyFormatTest(FormatTest, unittest.TestCase): - decimal = P - -class ArithmeticOperatorsTest: - '''Unit tests for all arithmetic operators, binary and unary.''' - - def test_addition(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('-11.1') - d2 = Decimal('22.2') - - #two Decimals - self.assertEqual(d1+d2, Decimal('11.1')) - self.assertEqual(d2+d1, Decimal('11.1')) - - #with other type, left - c = d1 + 5 - self.assertEqual(c, Decimal('-6.1')) - self.assertEqual(type(c), type(d1)) - - #with other type, right - c = 5 + d1 - self.assertEqual(c, Decimal('-6.1')) - self.assertEqual(type(c), type(d1)) - - #inline with decimal - d1 += d2 - self.assertEqual(d1, Decimal('11.1')) - - #inline with other type - d1 += 5 - self.assertEqual(d1, Decimal('16.1')) - - def test_subtraction(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('-11.1') - d2 = Decimal('22.2') - - #two Decimals - self.assertEqual(d1-d2, Decimal('-33.3')) - self.assertEqual(d2-d1, Decimal('33.3')) - - #with other type, left - c = d1 - 5 - self.assertEqual(c, Decimal('-16.1')) - self.assertEqual(type(c), type(d1)) - - #with other type, right - c = 5 - d1 - self.assertEqual(c, Decimal('16.1')) - self.assertEqual(type(c), type(d1)) - - #inline with decimal - d1 -= d2 - self.assertEqual(d1, Decimal('-33.3')) - - #inline with other type - d1 -= 5 - self.assertEqual(d1, Decimal('-38.3')) - - def test_multiplication(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('-5') - d2 = Decimal('3') - - #two Decimals - self.assertEqual(d1*d2, Decimal('-15')) - self.assertEqual(d2*d1, Decimal('-15')) - - #with other type, left - c = d1 * 5 - self.assertEqual(c, Decimal('-25')) - self.assertEqual(type(c), type(d1)) - - #with other type, right - c = 5 * d1 - self.assertEqual(c, Decimal('-25')) - self.assertEqual(type(c), type(d1)) - - #inline with decimal - d1 *= d2 - self.assertEqual(d1, Decimal('-15')) - - #inline with other type - d1 *= 5 - self.assertEqual(d1, Decimal('-75')) - - def test_division(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('-5') - d2 = Decimal('2') - - #two Decimals - self.assertEqual(d1/d2, Decimal('-2.5')) - self.assertEqual(d2/d1, Decimal('-0.4')) - - #with other type, left - c = d1 / 4 - self.assertEqual(c, Decimal('-1.25')) - self.assertEqual(type(c), type(d1)) - - #with other type, right - c = 4 / d1 - self.assertEqual(c, Decimal('-0.8')) - self.assertEqual(type(c), type(d1)) - - #inline with decimal - d1 /= d2 - self.assertEqual(d1, Decimal('-2.5')) - - #inline with other type - d1 /= 4 - self.assertEqual(d1, Decimal('-0.625')) - - def test_floor_division(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('5') - d2 = Decimal('2') - - #two Decimals - self.assertEqual(d1//d2, Decimal('2')) - self.assertEqual(d2//d1, Decimal('0')) - - #with other type, left - c = d1 // 4 - self.assertEqual(c, Decimal('1')) - self.assertEqual(type(c), type(d1)) - - #with other type, right - c = 7 // d1 - self.assertEqual(c, Decimal('1')) - self.assertEqual(type(c), type(d1)) - - #inline with decimal - d1 //= d2 - self.assertEqual(d1, Decimal('2')) - - #inline with other type - d1 //= 2 - self.assertEqual(d1, Decimal('1')) - - def test_powering(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('5') - d2 = Decimal('2') - - #two Decimals - self.assertEqual(d1**d2, Decimal('25')) - self.assertEqual(d2**d1, Decimal('32')) - - #with other type, left - c = d1 ** 4 - self.assertEqual(c, Decimal('625')) - self.assertEqual(type(c), type(d1)) - - #with other type, right - c = 7 ** d1 - self.assertEqual(c, Decimal('16807')) - self.assertEqual(type(c), type(d1)) - - #inline with decimal - d1 **= d2 - self.assertEqual(d1, Decimal('25')) - - #inline with other type - d1 **= 4 - self.assertEqual(d1, Decimal('390625')) - - def test_module(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('5') - d2 = Decimal('2') - - #two Decimals - self.assertEqual(d1%d2, Decimal('1')) - self.assertEqual(d2%d1, Decimal('2')) - - #with other type, left - c = d1 % 4 - self.assertEqual(c, Decimal('1')) - self.assertEqual(type(c), type(d1)) - - #with other type, right - c = 7 % d1 - self.assertEqual(c, Decimal('2')) - self.assertEqual(type(c), type(d1)) - - #inline with decimal - d1 %= d2 - self.assertEqual(d1, Decimal('1')) - - #inline with other type - d1 %= 4 - self.assertEqual(d1, Decimal('1')) - - def test_floor_div_module(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('5') - d2 = Decimal('2') - - #two Decimals - (p, q) = divmod(d1, d2) - self.assertEqual(p, Decimal('2')) - self.assertEqual(q, Decimal('1')) - self.assertEqual(type(p), type(d1)) - self.assertEqual(type(q), type(d1)) - - #with other type, left - (p, q) = divmod(d1, 4) - self.assertEqual(p, Decimal('1')) - self.assertEqual(q, Decimal('1')) - self.assertEqual(type(p), type(d1)) - self.assertEqual(type(q), type(d1)) - - #with other type, right - (p, q) = divmod(7, d1) - self.assertEqual(p, Decimal('1')) - self.assertEqual(q, Decimal('2')) - self.assertEqual(type(p), type(d1)) - self.assertEqual(type(q), type(d1)) - - def test_unary_operators(self): - Decimal = self.decimal.Decimal - - self.assertEqual(+Decimal(45), Decimal(+45)) # + - self.assertEqual(-Decimal(45), Decimal(-45)) # - - self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs - - def test_nan_comparisons(self): - # comparisons involving signaling nans signal InvalidOperation - - # order comparisons (<, <=, >, >=) involving only quiet nans - # also signal InvalidOperation - - # equality comparisons (==, !=) involving only quiet nans - # don't signal, but return False or True respectively. - Decimal = self.decimal.Decimal - InvalidOperation = self.decimal.InvalidOperation - localcontext = self.decimal.localcontext - - n = Decimal('NaN') - s = Decimal('sNaN') - i = Decimal('Inf') - f = Decimal('2') - - qnan_pairs = (n, n), (n, i), (i, n), (n, f), (f, n) - snan_pairs = (s, n), (n, s), (s, i), (i, s), (s, f), (f, s), (s, s) - order_ops = operator.lt, operator.le, operator.gt, operator.ge - equality_ops = operator.eq, operator.ne - - # results when InvalidOperation is not trapped - with localcontext() as ctx: - ctx.traps[InvalidOperation] = 0 - - for x, y in qnan_pairs + snan_pairs: - for op in order_ops + equality_ops: - got = op(x, y) - expected = True if op is operator.ne else False - self.assertIs(expected, got, - "expected {0!r} for operator.{1}({2!r}, {3!r}); " - "got {4!r}".format( - expected, op.__name__, x, y, got)) - - # repeat the above, but this time trap the InvalidOperation - with localcontext() as ctx: - ctx.traps[InvalidOperation] = 1 - - for x, y in qnan_pairs: - for op in equality_ops: - got = op(x, y) - expected = True if op is operator.ne else False - self.assertIs(expected, got, - "expected {0!r} for " - "operator.{1}({2!r}, {3!r}); " - "got {4!r}".format( - expected, op.__name__, x, y, got)) - - for x, y in snan_pairs: - for op in equality_ops: - self.assertRaises(InvalidOperation, operator.eq, x, y) - self.assertRaises(InvalidOperation, operator.ne, x, y) - - for x, y in qnan_pairs + snan_pairs: - for op in order_ops: - self.assertRaises(InvalidOperation, op, x, y) - - def test_copy_sign(self): - Decimal = self.decimal.Decimal - - d = Decimal(1).copy_sign(Decimal(-2)) - self.assertEqual(Decimal(1).copy_sign(-2), d) - self.assertRaises(TypeError, Decimal(1).copy_sign, '-2') - -@requires_cdecimal -class CArithmeticOperatorsTest(ArithmeticOperatorsTest, unittest.TestCase): - decimal = C -class PyArithmeticOperatorsTest(ArithmeticOperatorsTest, unittest.TestCase): - decimal = P - -# The following are two functions used to test threading in the next class - -def thfunc1(cls): - Decimal = cls.decimal.Decimal - InvalidOperation = cls.decimal.InvalidOperation - DivisionByZero = cls.decimal.DivisionByZero - Overflow = cls.decimal.Overflow - Underflow = cls.decimal.Underflow - Inexact = cls.decimal.Inexact - getcontext = cls.decimal.getcontext - localcontext = cls.decimal.localcontext - - d1 = Decimal(1) - d3 = Decimal(3) - test1 = d1/d3 - - cls.finish1.set() - cls.synchro.wait() - - test2 = d1/d3 - with localcontext() as c2: - cls.assertTrue(c2.flags[Inexact]) - cls.assertRaises(DivisionByZero, c2.divide, d1, 0) - cls.assertTrue(c2.flags[DivisionByZero]) - with localcontext() as c3: - cls.assertTrue(c3.flags[Inexact]) - cls.assertTrue(c3.flags[DivisionByZero]) - cls.assertRaises(InvalidOperation, c3.compare, d1, Decimal('sNaN')) - cls.assertTrue(c3.flags[InvalidOperation]) - del c3 - cls.assertFalse(c2.flags[InvalidOperation]) - del c2 - - cls.assertEqual(test1, Decimal('0.333333333333333333333333')) - cls.assertEqual(test2, Decimal('0.333333333333333333333333')) - - c1 = getcontext() - cls.assertTrue(c1.flags[Inexact]) - for sig in Overflow, Underflow, DivisionByZero, InvalidOperation: - cls.assertFalse(c1.flags[sig]) - -def thfunc2(cls): - Decimal = cls.decimal.Decimal - InvalidOperation = cls.decimal.InvalidOperation - DivisionByZero = cls.decimal.DivisionByZero - Overflow = cls.decimal.Overflow - Underflow = cls.decimal.Underflow - Inexact = cls.decimal.Inexact - getcontext = cls.decimal.getcontext - localcontext = cls.decimal.localcontext - - d1 = Decimal(1) - d3 = Decimal(3) - test1 = d1/d3 - - thiscontext = getcontext() - thiscontext.prec = 18 - test2 = d1/d3 - - with localcontext() as c2: - cls.assertTrue(c2.flags[Inexact]) - cls.assertRaises(Overflow, c2.multiply, Decimal('1e425000000'), 999) - cls.assertTrue(c2.flags[Overflow]) - with localcontext(thiscontext) as c3: - cls.assertTrue(c3.flags[Inexact]) - cls.assertFalse(c3.flags[Overflow]) - c3.traps[Underflow] = True - cls.assertRaises(Underflow, c3.divide, Decimal('1e-425000000'), 999) - cls.assertTrue(c3.flags[Underflow]) - del c3 - cls.assertFalse(c2.flags[Underflow]) - cls.assertFalse(c2.traps[Underflow]) - del c2 - - cls.synchro.set() - cls.finish2.set() - - cls.assertEqual(test1, Decimal('0.333333333333333333333333')) - cls.assertEqual(test2, Decimal('0.333333333333333333')) - - cls.assertFalse(thiscontext.traps[Underflow]) - cls.assertTrue(thiscontext.flags[Inexact]) - for sig in Overflow, Underflow, DivisionByZero, InvalidOperation: - cls.assertFalse(thiscontext.flags[sig]) - - -@threading_helper.requires_working_threading() -class ThreadingTest: - '''Unit tests for thread local contexts in Decimal.''' - - # Take care executing this test from IDLE, there's an issue in threading - # that hangs IDLE and I couldn't find it - - def test_threading(self): - DefaultContext = self.decimal.DefaultContext - - if self.decimal == C and not self.decimal.HAVE_THREADS: - self.skipTest("compiled without threading") - # Test the "threading isolation" of a Context. Also test changing - # the DefaultContext, which acts as a template for the thread-local - # contexts. - save_prec = DefaultContext.prec - save_emax = DefaultContext.Emax - save_emin = DefaultContext.Emin - DefaultContext.prec = 24 - DefaultContext.Emax = 425000000 - DefaultContext.Emin = -425000000 - - self.synchro = threading.Event() - self.finish1 = threading.Event() - self.finish2 = threading.Event() - - # This test wants to start threads with an empty context, no matter - # the setting of sys.flags.thread_inherit_context. We pass the - # 'context' argument explicitly with an empty context instance. - th1 = threading.Thread(target=thfunc1, args=(self,), - context=contextvars.Context()) - th2 = threading.Thread(target=thfunc2, args=(self,), - context=contextvars.Context()) - - th1.start() - th2.start() - - self.finish1.wait() - self.finish2.wait() - - for sig in Signals[self.decimal]: - self.assertFalse(DefaultContext.flags[sig]) - - th1.join() - th2.join() - - DefaultContext.prec = save_prec - DefaultContext.Emax = save_emax - DefaultContext.Emin = save_emin - - -@requires_cdecimal -class CThreadingTest(ThreadingTest, unittest.TestCase): - decimal = C - -class PyThreadingTest(ThreadingTest, unittest.TestCase): - decimal = P - -class UsabilityTest: - '''Unit tests for Usability cases of Decimal.''' - - def test_comparison_operators(self): - - Decimal = self.decimal.Decimal - - da = Decimal('23.42') - db = Decimal('23.42') - dc = Decimal('45') - - #two Decimals - self.assertGreater(dc, da) - self.assertGreaterEqual(dc, da) - self.assertLess(da, dc) - self.assertLessEqual(da, dc) - self.assertEqual(da, db) - self.assertNotEqual(da, dc) - self.assertLessEqual(da, db) - self.assertGreaterEqual(da, db) - - #a Decimal and an int - self.assertGreater(dc, 23) - self.assertLess(23, dc) - self.assertEqual(dc, 45) - - #a Decimal and uncomparable - self.assertNotEqual(da, 'ugly') - self.assertNotEqual(da, 32.7) - self.assertNotEqual(da, object()) - self.assertNotEqual(da, object) - - # sortable - a = list(map(Decimal, range(100))) - b = a[:] - random.shuffle(a) - a.sort() - self.assertEqual(a, b) - - def test_decimal_float_comparison(self): - Decimal = self.decimal.Decimal - - da = Decimal('0.25') - db = Decimal('3.0') - self.assertLess(da, 3.0) - self.assertLessEqual(da, 3.0) - self.assertGreater(db, 0.25) - self.assertGreaterEqual(db, 0.25) - self.assertNotEqual(da, 1.5) - self.assertEqual(da, 0.25) - self.assertGreater(3.0, da) - self.assertGreaterEqual(3.0, da) - self.assertLess(0.25, db) - self.assertLessEqual(0.25, db) - self.assertNotEqual(0.25, db) - self.assertEqual(3.0, db) - self.assertNotEqual(0.1, Decimal('0.1')) - - def test_decimal_complex_comparison(self): - Decimal = self.decimal.Decimal - - da = Decimal('0.25') - db = Decimal('3.0') - self.assertNotEqual(da, (1.5+0j)) - self.assertNotEqual((1.5+0j), da) - self.assertEqual(da, (0.25+0j)) - self.assertEqual((0.25+0j), da) - self.assertEqual((3.0+0j), db) - self.assertEqual(db, (3.0+0j)) - - self.assertNotEqual(db, (3.0+1j)) - self.assertNotEqual((3.0+1j), db) - - self.assertIs(db.__lt__(3.0+0j), NotImplemented) - self.assertIs(db.__le__(3.0+0j), NotImplemented) - self.assertIs(db.__gt__(3.0+0j), NotImplemented) - self.assertIs(db.__le__(3.0+0j), NotImplemented) - - def test_decimal_fraction_comparison(self): - D = self.decimal.Decimal - F = fractions[self.decimal].Fraction - Context = self.decimal.Context - localcontext = self.decimal.localcontext - InvalidOperation = self.decimal.InvalidOperation - - - emax = C.MAX_EMAX if C else 999999999 - emin = C.MIN_EMIN if C else -999999999 - etiny = C.MIN_ETINY if C else -1999999997 - c = Context(Emax=emax, Emin=emin) - - with localcontext(c): - c.prec = emax - self.assertLess(D(0), F(1,9999999999999999999999999999999999999)) - self.assertLess(F(-1,9999999999999999999999999999999999999), D(0)) - self.assertLess(F(0,1), D("1e" + str(etiny))) - self.assertLess(D("-1e" + str(etiny)), F(0,1)) - self.assertLess(F(0,9999999999999999999999999), D("1e" + str(etiny))) - self.assertLess(D("-1e" + str(etiny)), F(0,9999999999999999999999999)) - - self.assertEqual(D("0.1"), F(1,10)) - self.assertEqual(F(1,10), D("0.1")) - - c.prec = 300 - self.assertNotEqual(D(1)/3, F(1,3)) - self.assertNotEqual(F(1,3), D(1)/3) - - self.assertLessEqual(F(120984237, 9999999999), D("9e" + str(emax))) - self.assertGreaterEqual(D("9e" + str(emax)), F(120984237, 9999999999)) - - self.assertGreater(D('inf'), F(99999999999,123)) - self.assertGreater(D('inf'), F(-99999999999,123)) - self.assertLess(D('-inf'), F(99999999999,123)) - self.assertLess(D('-inf'), F(-99999999999,123)) - - self.assertRaises(InvalidOperation, D('nan').__gt__, F(-9,123)) - self.assertIs(NotImplemented, F(-9,123).__lt__(D('nan'))) - self.assertNotEqual(D('nan'), F(-9,123)) - self.assertNotEqual(F(-9,123), D('nan')) - - def test_copy_and_deepcopy_methods(self): - Decimal = self.decimal.Decimal - - d = Decimal('43.24') - c = copy.copy(d) - self.assertEqual(id(c), id(d)) - dc = copy.deepcopy(d) - self.assertEqual(id(dc), id(d)) - - def test_hash_method(self): - - Decimal = self.decimal.Decimal - localcontext = self.decimal.localcontext - - def hashit(d): - a = hash(d) - b = d.__hash__() - self.assertEqual(a, b) - return a - - #just that it's hashable - hashit(Decimal(23)) - hashit(Decimal('Infinity')) - hashit(Decimal('-Infinity')) - hashit(Decimal('nan123')) - hashit(Decimal('-NaN')) - - test_values = [Decimal(sign*(2**m + n)) - for m in [0, 14, 15, 16, 17, 30, 31, - 32, 33, 61, 62, 63, 64, 65, 66] - for n in range(-10, 10) - for sign in [-1, 1]] - test_values.extend([ - Decimal("-1"), # ==> -2 - Decimal("-0"), # zeros - Decimal("0.00"), - Decimal("-0.000"), - Decimal("0E10"), - Decimal("-0E12"), - Decimal("10.0"), # negative exponent - Decimal("-23.00000"), - Decimal("1230E100"), # positive exponent - Decimal("-4.5678E50"), - # a value for which hash(n) != hash(n % (2**64-1)) - # in Python pre-2.6 - Decimal(2**64 + 2**32 - 1), - # selection of values which fail with the old (before - # version 2.6) long.__hash__ - Decimal("1.634E100"), - Decimal("90.697E100"), - Decimal("188.83E100"), - Decimal("1652.9E100"), - Decimal("56531E100"), - ]) - - # check that hash(d) == hash(int(d)) for integral values - for value in test_values: - self.assertEqual(hashit(value), hash(int(value))) - - # check that the hashes of a Decimal float match when they - # represent exactly the same values - test_strings = ['inf', '-Inf', '0.0', '-.0e1', - '34.0', '2.5', '112390.625', '-0.515625'] - for s in test_strings: - f = float(s) - d = Decimal(s) - self.assertEqual(hashit(d), hash(f)) - - with localcontext() as c: - # check that the value of the hash doesn't depend on the - # current context (issue #1757) - x = Decimal("123456789.1") - - c.prec = 6 - h1 = hashit(x) - c.prec = 10 - h2 = hashit(x) - c.prec = 16 - h3 = hashit(x) - - self.assertEqual(h1, h2) - self.assertEqual(h1, h3) - - c.prec = 10000 - x = 1100 ** 1248 - self.assertEqual(hashit(Decimal(x)), hashit(x)) - - def test_hash_method_nan(self): - Decimal = self.decimal.Decimal - self.assertRaises(TypeError, hash, Decimal('sNaN')) - value = Decimal('NaN') - self.assertEqual(hash(value), object.__hash__(value)) - class H: - def __hash__(self): - return 42 - class D(Decimal, H): - pass - value = D('NaN') - self.assertEqual(hash(value), object.__hash__(value)) - - def test_min_and_max_methods(self): - Decimal = self.decimal.Decimal - - d1 = Decimal('15.32') - d2 = Decimal('28.5') - l1 = 15 - l2 = 28 - - #between Decimals - self.assertIs(min(d1,d2), d1) - self.assertIs(min(d2,d1), d1) - self.assertIs(max(d1,d2), d2) - self.assertIs(max(d2,d1), d2) - - #between Decimal and int - self.assertIs(min(d1,l2), d1) - self.assertIs(min(l2,d1), d1) - self.assertIs(max(l1,d2), d2) - self.assertIs(max(d2,l1), d2) - - def test_as_nonzero(self): - Decimal = self.decimal.Decimal - - #as false - self.assertFalse(Decimal(0)) - #as true - self.assertTrue(Decimal('0.372')) - - def test_tostring_methods(self): - #Test str and repr methods. - Decimal = self.decimal.Decimal - - d = Decimal('15.32') - self.assertEqual(str(d), '15.32') # str - self.assertEqual(repr(d), "Decimal('15.32')") # repr - - def test_tonum_methods(self): - #Test float and int methods. - Decimal = self.decimal.Decimal - - d1 = Decimal('66') - d2 = Decimal('15.32') - - #int - self.assertEqual(int(d1), 66) - self.assertEqual(int(d2), 15) - - #float - self.assertEqual(float(d1), 66) - self.assertEqual(float(d2), 15.32) - - #floor - test_pairs = [ - ('123.00', 123), - ('3.2', 3), - ('3.54', 3), - ('3.899', 3), - ('-2.3', -3), - ('-11.0', -11), - ('0.0', 0), - ('-0E3', 0), - ('89891211712379812736.1', 89891211712379812736), - ] - for d, i in test_pairs: - self.assertEqual(math.floor(Decimal(d)), i) - self.assertRaises(ValueError, math.floor, Decimal('-NaN')) - self.assertRaises(ValueError, math.floor, Decimal('sNaN')) - self.assertRaises(ValueError, math.floor, Decimal('NaN123')) - self.assertRaises(OverflowError, math.floor, Decimal('Inf')) - self.assertRaises(OverflowError, math.floor, Decimal('-Inf')) - - #ceiling - test_pairs = [ - ('123.00', 123), - ('3.2', 4), - ('3.54', 4), - ('3.899', 4), - ('-2.3', -2), - ('-11.0', -11), - ('0.0', 0), - ('-0E3', 0), - ('89891211712379812736.1', 89891211712379812737), - ] - for d, i in test_pairs: - self.assertEqual(math.ceil(Decimal(d)), i) - self.assertRaises(ValueError, math.ceil, Decimal('-NaN')) - self.assertRaises(ValueError, math.ceil, Decimal('sNaN')) - self.assertRaises(ValueError, math.ceil, Decimal('NaN123')) - self.assertRaises(OverflowError, math.ceil, Decimal('Inf')) - self.assertRaises(OverflowError, math.ceil, Decimal('-Inf')) - - #round, single argument - test_pairs = [ - ('123.00', 123), - ('3.2', 3), - ('3.54', 4), - ('3.899', 4), - ('-2.3', -2), - ('-11.0', -11), - ('0.0', 0), - ('-0E3', 0), - ('-3.5', -4), - ('-2.5', -2), - ('-1.5', -2), - ('-0.5', 0), - ('0.5', 0), - ('1.5', 2), - ('2.5', 2), - ('3.5', 4), - ] - for d, i in test_pairs: - self.assertEqual(round(Decimal(d)), i) - self.assertRaises(ValueError, round, Decimal('-NaN')) - self.assertRaises(ValueError, round, Decimal('sNaN')) - self.assertRaises(ValueError, round, Decimal('NaN123')) - self.assertRaises(OverflowError, round, Decimal('Inf')) - self.assertRaises(OverflowError, round, Decimal('-Inf')) - - #round, two arguments; this is essentially equivalent - #to quantize, which is already extensively tested - test_triples = [ - ('123.456', -4, '0E+4'), - ('-123.456', -4, '-0E+4'), - ('123.456', -3, '0E+3'), - ('-123.456', -3, '-0E+3'), - ('123.456', -2, '1E+2'), - ('123.456', -1, '1.2E+2'), - ('123.456', 0, '123'), - ('123.456', 1, '123.5'), - ('123.456', 2, '123.46'), - ('123.456', 3, '123.456'), - ('123.456', 4, '123.4560'), - ('123.455', 2, '123.46'), - ('123.445', 2, '123.44'), - ('Inf', 4, 'NaN'), - ('-Inf', -23, 'NaN'), - ('sNaN314', 3, 'NaN314'), - ] - for d, n, r in test_triples: - self.assertEqual(str(round(Decimal(d), n)), r) - - def test_nan_to_float(self): - # Test conversions of decimal NANs to float. - # See http://bugs.python.org/issue15544 - Decimal = self.decimal.Decimal - for s in ('nan', 'nan1234', '-nan', '-nan2468'): - f = float(Decimal(s)) - self.assertTrue(math.isnan(f)) - sign = math.copysign(1.0, f) - self.assertEqual(sign, -1.0 if s.startswith('-') else 1.0) - - def test_snan_to_float(self): - Decimal = self.decimal.Decimal - for s in ('snan', '-snan', 'snan1357', '-snan1234'): - d = Decimal(s) - self.assertRaises(ValueError, float, d) - - def test_eval_round_trip(self): - Decimal = self.decimal.Decimal - - #with zero - d = Decimal( (0, (0,), 0) ) - self.assertEqual(d, eval(repr(d))) - - #int - d = Decimal( (1, (4, 5), 0) ) - self.assertEqual(d, eval(repr(d))) - - #float - d = Decimal( (0, (4, 5, 3, 4), -2) ) - self.assertEqual(d, eval(repr(d))) - - #weird - d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) - self.assertEqual(d, eval(repr(d))) - - def test_as_tuple(self): - Decimal = self.decimal.Decimal - - #with zero - d = Decimal(0) - self.assertEqual(d.as_tuple(), (0, (0,), 0) ) - - #int - d = Decimal(-45) - self.assertEqual(d.as_tuple(), (1, (4, 5), 0) ) - - #complicated string - d = Decimal("-4.34913534E-17") - self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) - - # The '0' coefficient is implementation specific to decimal.py. - # It has no meaning in the C-version and is ignored there. - d = Decimal("Infinity") - self.assertEqual(d.as_tuple(), (0, (0,), 'F') ) - - #leading zeros in coefficient should be stripped - d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), -2) ) - self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), -2) ) - d = Decimal( (1, (0, 0, 0), 37) ) - self.assertEqual(d.as_tuple(), (1, (0,), 37)) - d = Decimal( (1, (), 37) ) - self.assertEqual(d.as_tuple(), (1, (0,), 37)) - - #leading zeros in NaN diagnostic info should be stripped - d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), 'n') ) - self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), 'n') ) - d = Decimal( (1, (0, 0, 0), 'N') ) - self.assertEqual(d.as_tuple(), (1, (), 'N') ) - d = Decimal( (1, (), 'n') ) - self.assertEqual(d.as_tuple(), (1, (), 'n') ) - - # For infinities, decimal.py has always silently accepted any - # coefficient tuple. - d = Decimal( (0, (0,), 'F') ) - self.assertEqual(d.as_tuple(), (0, (0,), 'F')) - d = Decimal( (0, (4, 5, 3, 4), 'F') ) - self.assertEqual(d.as_tuple(), (0, (0,), 'F')) - d = Decimal( (1, (0, 2, 7, 1), 'F') ) - self.assertEqual(d.as_tuple(), (1, (0,), 'F')) - - def test_as_integer_ratio(self): - Decimal = self.decimal.Decimal - - # exceptional cases - self.assertRaises(OverflowError, - Decimal.as_integer_ratio, Decimal('inf')) - self.assertRaises(OverflowError, - Decimal.as_integer_ratio, Decimal('-inf')) - self.assertRaises(ValueError, - Decimal.as_integer_ratio, Decimal('-nan')) - self.assertRaises(ValueError, - Decimal.as_integer_ratio, Decimal('snan123')) - - for exp in range(-4, 2): - for coeff in range(1000): - for sign in '+', '-': - d = Decimal('%s%dE%d' % (sign, coeff, exp)) - pq = d.as_integer_ratio() - p, q = pq - - # check return type - self.assertIsInstance(pq, tuple) - self.assertIsInstance(p, int) - self.assertIsInstance(q, int) - - # check normalization: q should be positive; - # p should be relatively prime to q. - self.assertGreater(q, 0) - self.assertEqual(math.gcd(p, q), 1) - - # check that p/q actually gives the correct value - self.assertEqual(Decimal(p) / Decimal(q), d) - - def test_subclassing(self): - # Different behaviours when subclassing Decimal - Decimal = self.decimal.Decimal - - class MyDecimal(Decimal): - y = None - - d1 = MyDecimal(1) - d2 = MyDecimal(2) - d = d1 + d2 - self.assertIs(type(d), Decimal) - - d = d1.max(d2) - self.assertIs(type(d), Decimal) - - d = copy.copy(d1) - self.assertIs(type(d), MyDecimal) - self.assertEqual(d, d1) - - d = copy.deepcopy(d1) - self.assertIs(type(d), MyDecimal) - self.assertEqual(d, d1) - - # Decimal(Decimal) - d = Decimal('1.0') - x = Decimal(d) - self.assertIs(type(x), Decimal) - self.assertEqual(x, d) - - # MyDecimal(Decimal) - m = MyDecimal(d) - self.assertIs(type(m), MyDecimal) - self.assertEqual(m, d) - self.assertIs(m.y, None) - - # Decimal(MyDecimal) - x = Decimal(m) - self.assertIs(type(x), Decimal) - self.assertEqual(x, d) - - # MyDecimal(MyDecimal) - m.y = 9 - x = MyDecimal(m) - self.assertIs(type(x), MyDecimal) - self.assertEqual(x, d) - self.assertIs(x.y, None) - - def test_implicit_context(self): - Decimal = self.decimal.Decimal - getcontext = self.decimal.getcontext - - # Check results when context given implicitly. (Issue 2478) - c = getcontext() - self.assertEqual(str(Decimal(0).sqrt()), - str(c.sqrt(Decimal(0)))) - - def test_none_args(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - localcontext = self.decimal.localcontext - InvalidOperation = self.decimal.InvalidOperation - DivisionByZero = self.decimal.DivisionByZero - Overflow = self.decimal.Overflow - Underflow = self.decimal.Underflow - Subnormal = self.decimal.Subnormal - Inexact = self.decimal.Inexact - Rounded = self.decimal.Rounded - Clamped = self.decimal.Clamped - - with localcontext(Context()) as c: - c.prec = 7 - c.Emax = 999 - c.Emin = -999 - - x = Decimal("111") - y = Decimal("1e9999") - z = Decimal("1e-9999") - - ##### Unary functions - c.clear_flags() - self.assertEqual(str(x.exp(context=None)), '1.609487E+48') - self.assertTrue(c.flags[Inexact]) - self.assertTrue(c.flags[Rounded]) - c.clear_flags() - self.assertRaises(Overflow, y.exp, context=None) - self.assertTrue(c.flags[Overflow]) - - self.assertIs(z.is_normal(context=None), False) - self.assertIs(z.is_subnormal(context=None), True) - - c.clear_flags() - self.assertEqual(str(x.ln(context=None)), '4.709530') - self.assertTrue(c.flags[Inexact]) - self.assertTrue(c.flags[Rounded]) - c.clear_flags() - self.assertRaises(InvalidOperation, Decimal(-1).ln, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - self.assertEqual(str(x.log10(context=None)), '2.045323') - self.assertTrue(c.flags[Inexact]) - self.assertTrue(c.flags[Rounded]) - c.clear_flags() - self.assertRaises(InvalidOperation, Decimal(-1).log10, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - self.assertEqual(str(x.logb(context=None)), '2') - self.assertRaises(DivisionByZero, Decimal(0).logb, context=None) - self.assertTrue(c.flags[DivisionByZero]) - - c.clear_flags() - self.assertEqual(str(x.logical_invert(context=None)), '1111000') - self.assertRaises(InvalidOperation, y.logical_invert, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - self.assertEqual(str(y.next_minus(context=None)), '9.999999E+999') - self.assertRaises(InvalidOperation, Decimal('sNaN').next_minus, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - self.assertEqual(str(y.next_plus(context=None)), 'Infinity') - self.assertRaises(InvalidOperation, Decimal('sNaN').next_plus, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - self.assertEqual(str(z.normalize(context=None)), '0') - self.assertRaises(Overflow, y.normalize, context=None) - self.assertTrue(c.flags[Overflow]) - - self.assertEqual(str(z.number_class(context=None)), '+Subnormal') - - c.clear_flags() - self.assertEqual(str(z.sqrt(context=None)), '0E-1005') - self.assertTrue(c.flags[Clamped]) - self.assertTrue(c.flags[Inexact]) - self.assertTrue(c.flags[Rounded]) - self.assertTrue(c.flags[Subnormal]) - self.assertTrue(c.flags[Underflow]) - c.clear_flags() - self.assertRaises(Overflow, y.sqrt, context=None) - self.assertTrue(c.flags[Overflow]) - - c.capitals = 0 - self.assertEqual(str(z.to_eng_string(context=None)), '1e-9999') - c.capitals = 1 - - - ##### Binary functions - c.clear_flags() - ans = str(x.compare(Decimal('Nan891287828'), context=None)) - self.assertEqual(ans, 'NaN1287828') - self.assertRaises(InvalidOperation, x.compare, Decimal('sNaN'), context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.compare_signal(8224, context=None)) - self.assertEqual(ans, '-1') - self.assertRaises(InvalidOperation, x.compare_signal, Decimal('NaN'), context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.logical_and(101, context=None)) - self.assertEqual(ans, '101') - self.assertRaises(InvalidOperation, x.logical_and, 123, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.logical_or(101, context=None)) - self.assertEqual(ans, '111') - self.assertRaises(InvalidOperation, x.logical_or, 123, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.logical_xor(101, context=None)) - self.assertEqual(ans, '10') - self.assertRaises(InvalidOperation, x.logical_xor, 123, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.max(101, context=None)) - self.assertEqual(ans, '111') - self.assertRaises(InvalidOperation, x.max, Decimal('sNaN'), context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.max_mag(101, context=None)) - self.assertEqual(ans, '111') - self.assertRaises(InvalidOperation, x.max_mag, Decimal('sNaN'), context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.min(101, context=None)) - self.assertEqual(ans, '101') - self.assertRaises(InvalidOperation, x.min, Decimal('sNaN'), context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.min_mag(101, context=None)) - self.assertEqual(ans, '101') - self.assertRaises(InvalidOperation, x.min_mag, Decimal('sNaN'), context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.remainder_near(101, context=None)) - self.assertEqual(ans, '10') - self.assertRaises(InvalidOperation, y.remainder_near, 101, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.rotate(2, context=None)) - self.assertEqual(ans, '11100') - self.assertRaises(InvalidOperation, x.rotate, 101, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.scaleb(7, context=None)) - self.assertEqual(ans, '1.11E+9') - self.assertRaises(InvalidOperation, x.scaleb, 10000, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - ans = str(x.shift(2, context=None)) - self.assertEqual(ans, '11100') - self.assertRaises(InvalidOperation, x.shift, 10000, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - - ##### Ternary functions - c.clear_flags() - ans = str(x.fma(2, 3, context=None)) - self.assertEqual(ans, '225') - self.assertRaises(Overflow, x.fma, Decimal('1e9999'), 3, context=None) - self.assertTrue(c.flags[Overflow]) - - - ##### Special cases - c.rounding = ROUND_HALF_EVEN - ans = str(Decimal('1.5').to_integral(rounding=None, context=None)) - self.assertEqual(ans, '2') - c.rounding = ROUND_DOWN - ans = str(Decimal('1.5').to_integral(rounding=None, context=None)) - self.assertEqual(ans, '1') - ans = str(Decimal('1.5').to_integral(rounding=ROUND_UP, context=None)) - self.assertEqual(ans, '2') - c.clear_flags() - self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.rounding = ROUND_HALF_EVEN - ans = str(Decimal('1.5').to_integral_value(rounding=None, context=None)) - self.assertEqual(ans, '2') - c.rounding = ROUND_DOWN - ans = str(Decimal('1.5').to_integral_value(rounding=None, context=None)) - self.assertEqual(ans, '1') - ans = str(Decimal('1.5').to_integral_value(rounding=ROUND_UP, context=None)) - self.assertEqual(ans, '2') - c.clear_flags() - self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral_value, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.rounding = ROUND_HALF_EVEN - ans = str(Decimal('1.5').to_integral_exact(rounding=None, context=None)) - self.assertEqual(ans, '2') - c.rounding = ROUND_DOWN - ans = str(Decimal('1.5').to_integral_exact(rounding=None, context=None)) - self.assertEqual(ans, '1') - ans = str(Decimal('1.5').to_integral_exact(rounding=ROUND_UP, context=None)) - self.assertEqual(ans, '2') - c.clear_flags() - self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral_exact, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - c.rounding = ROUND_UP - ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=None, context=None)) - self.assertEqual(ans, '1.501') - c.rounding = ROUND_DOWN - ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=None, context=None)) - self.assertEqual(ans, '1.500') - ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=ROUND_UP, context=None)) - self.assertEqual(ans, '1.501') - c.clear_flags() - self.assertRaises(InvalidOperation, y.quantize, Decimal('1e-10'), rounding=ROUND_UP, context=None) - self.assertTrue(c.flags[InvalidOperation]) - - with localcontext(Context()) as context: - context.prec = 7 - context.Emax = 999 - context.Emin = -999 - with localcontext(ctx=None) as c: - self.assertEqual(c.prec, 7) - self.assertEqual(c.Emax, 999) - self.assertEqual(c.Emin, -999) - - def test_conversions_from_int(self): - # Check that methods taking a second Decimal argument will - # always accept an integer in place of a Decimal. - Decimal = self.decimal.Decimal - - self.assertEqual(Decimal(4).compare(3), - Decimal(4).compare(Decimal(3))) - self.assertEqual(Decimal(4).compare_signal(3), - Decimal(4).compare_signal(Decimal(3))) - self.assertEqual(Decimal(4).compare_total(3), - Decimal(4).compare_total(Decimal(3))) - self.assertEqual(Decimal(4).compare_total_mag(3), - Decimal(4).compare_total_mag(Decimal(3))) - self.assertEqual(Decimal(10101).logical_and(1001), - Decimal(10101).logical_and(Decimal(1001))) - self.assertEqual(Decimal(10101).logical_or(1001), - Decimal(10101).logical_or(Decimal(1001))) - self.assertEqual(Decimal(10101).logical_xor(1001), - Decimal(10101).logical_xor(Decimal(1001))) - self.assertEqual(Decimal(567).max(123), - Decimal(567).max(Decimal(123))) - self.assertEqual(Decimal(567).max_mag(123), - Decimal(567).max_mag(Decimal(123))) - self.assertEqual(Decimal(567).min(123), - Decimal(567).min(Decimal(123))) - self.assertEqual(Decimal(567).min_mag(123), - Decimal(567).min_mag(Decimal(123))) - self.assertEqual(Decimal(567).next_toward(123), - Decimal(567).next_toward(Decimal(123))) - self.assertEqual(Decimal(1234).quantize(100), - Decimal(1234).quantize(Decimal(100))) - self.assertEqual(Decimal(768).remainder_near(1234), - Decimal(768).remainder_near(Decimal(1234))) - self.assertEqual(Decimal(123).rotate(1), - Decimal(123).rotate(Decimal(1))) - self.assertEqual(Decimal(1234).same_quantum(1000), - Decimal(1234).same_quantum(Decimal(1000))) - self.assertEqual(Decimal('9.123').scaleb(-100), - Decimal('9.123').scaleb(Decimal(-100))) - self.assertEqual(Decimal(456).shift(-1), - Decimal(456).shift(Decimal(-1))) - - self.assertEqual(Decimal(-12).fma(Decimal(45), 67), - Decimal(-12).fma(Decimal(45), Decimal(67))) - self.assertEqual(Decimal(-12).fma(45, 67), - Decimal(-12).fma(Decimal(45), Decimal(67))) - self.assertEqual(Decimal(-12).fma(45, Decimal(67)), - Decimal(-12).fma(Decimal(45), Decimal(67))) - -@requires_cdecimal -class CUsabilityTest(UsabilityTest, unittest.TestCase): - decimal = C -class PyUsabilityTest(UsabilityTest, unittest.TestCase): - decimal = P - - def setUp(self): - super().setUp() - self._previous_int_limit = sys.get_int_max_str_digits() - sys.set_int_max_str_digits(7000) - - def tearDown(self): - sys.set_int_max_str_digits(self._previous_int_limit) - super().tearDown() - -class PythonAPItests: - - def test_abc(self): - Decimal = self.decimal.Decimal - - self.assertIsSubclass(Decimal, numbers.Number) - self.assertNotIsSubclass(Decimal, numbers.Real) - self.assertIsInstance(Decimal(0), numbers.Number) - self.assertNotIsInstance(Decimal(0), numbers.Real) - - def test_pickle(self): - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - Decimal = self.decimal.Decimal - - savedecimal = sys.modules['decimal'] - - # Round trip - sys.modules['decimal'] = self.decimal - d = Decimal('-3.141590000') - p = pickle.dumps(d, proto) - e = pickle.loads(p) - self.assertEqual(d, e) - - if C: - # Test interchangeability - x = C.Decimal('-3.123e81723') - y = P.Decimal('-3.123e81723') - - sys.modules['decimal'] = C - sx = pickle.dumps(x, proto) - sys.modules['decimal'] = P - r = pickle.loads(sx) - self.assertIsInstance(r, P.Decimal) - self.assertEqual(r, y) - - sys.modules['decimal'] = P - sy = pickle.dumps(y, proto) - sys.modules['decimal'] = C - r = pickle.loads(sy) - self.assertIsInstance(r, C.Decimal) - self.assertEqual(r, x) - - x = C.Decimal('-3.123e81723').as_tuple() - y = P.Decimal('-3.123e81723').as_tuple() - - sys.modules['decimal'] = C - sx = pickle.dumps(x, proto) - sys.modules['decimal'] = P - r = pickle.loads(sx) - self.assertIsInstance(r, P.DecimalTuple) - self.assertEqual(r, y) - - sys.modules['decimal'] = P - sy = pickle.dumps(y, proto) - sys.modules['decimal'] = C - r = pickle.loads(sy) - self.assertIsInstance(r, C.DecimalTuple) - self.assertEqual(r, x) - - sys.modules['decimal'] = savedecimal - - def test_int(self): - Decimal = self.decimal.Decimal - - for x in range(-250, 250): - s = '%0.2f' % (x / 100.0) - # should work the same as for floats - self.assertEqual(int(Decimal(s)), int(float(s))) - # should work the same as to_integral in the ROUND_DOWN mode - d = Decimal(s) - r = d.to_integral(ROUND_DOWN) - self.assertEqual(Decimal(int(d)), r) - - self.assertRaises(ValueError, int, Decimal('-nan')) - self.assertRaises(ValueError, int, Decimal('snan')) - self.assertRaises(OverflowError, int, Decimal('inf')) - self.assertRaises(OverflowError, int, Decimal('-inf')) - - @cpython_only - def test_small_ints(self): - Decimal = self.decimal.Decimal - # bpo-46361 - for x in range(-5, 257): - self.assertIs(int(Decimal(x)), x) - - def test_trunc(self): - Decimal = self.decimal.Decimal - - for x in range(-250, 250): - s = '%0.2f' % (x / 100.0) - # should work the same as for floats - self.assertEqual(int(Decimal(s)), int(float(s))) - # should work the same as to_integral in the ROUND_DOWN mode - d = Decimal(s) - r = d.to_integral(ROUND_DOWN) - self.assertEqual(Decimal(math.trunc(d)), r) - - def test_from_float(self): - - Decimal = self.decimal.Decimal - - class MyDecimal(Decimal): - def __init__(self, _): - self.x = 'y' - - self.assertIsSubclass(MyDecimal, Decimal) - - r = MyDecimal.from_float(0.1) - self.assertEqual(type(r), MyDecimal) - self.assertEqual(str(r), - '0.1000000000000000055511151231257827021181583404541015625') - self.assertEqual(r.x, 'y') - - bigint = 12345678901234567890123456789 - self.assertEqual(MyDecimal.from_float(bigint), MyDecimal(bigint)) - self.assertTrue(MyDecimal.from_float(float('nan')).is_qnan()) - self.assertTrue(MyDecimal.from_float(float('inf')).is_infinite()) - self.assertTrue(MyDecimal.from_float(float('-inf')).is_infinite()) - self.assertEqual(str(MyDecimal.from_float(float('nan'))), - str(Decimal('NaN'))) - self.assertEqual(str(MyDecimal.from_float(float('inf'))), - str(Decimal('Infinity'))) - self.assertEqual(str(MyDecimal.from_float(float('-inf'))), - str(Decimal('-Infinity'))) - self.assertRaises(TypeError, MyDecimal.from_float, 'abc') - for i in range(200): - x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) - self.assertEqual(x, float(MyDecimal.from_float(x))) # roundtrip - - def test_create_decimal_from_float(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - Inexact = self.decimal.Inexact - - context = Context(prec=5, rounding=ROUND_DOWN) - self.assertEqual( - context.create_decimal_from_float(math.pi), - Decimal('3.1415') - ) - context = Context(prec=5, rounding=ROUND_UP) - self.assertEqual( - context.create_decimal_from_float(math.pi), - Decimal('3.1416') - ) - context = Context(prec=5, traps=[Inexact]) - self.assertRaises( - Inexact, - context.create_decimal_from_float, - math.pi - ) - self.assertEqual(repr(context.create_decimal_from_float(-0.0)), - "Decimal('-0')") - self.assertEqual(repr(context.create_decimal_from_float(1.0)), - "Decimal('1')") - self.assertEqual(repr(context.create_decimal_from_float(10)), - "Decimal('10')") - - def test_quantize(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - InvalidOperation = self.decimal.InvalidOperation - - c = Context(Emax=99999, Emin=-99999) - self.assertEqual( - Decimal('7.335').quantize(Decimal('.01')), - Decimal('7.34') - ) - self.assertEqual( - Decimal('7.335').quantize(Decimal('.01'), rounding=ROUND_DOWN), - Decimal('7.33') - ) - self.assertRaises( - InvalidOperation, - Decimal("10e99999").quantize, Decimal('1e100000'), context=c - ) - - c = Context() - d = Decimal("0.871831e800") - x = d.quantize(context=c, exp=Decimal("1e797"), rounding=ROUND_DOWN) - self.assertEqual(x, Decimal('8.71E+799')) - - def test_complex(self): - Decimal = self.decimal.Decimal - - x = Decimal("9.8182731e181273") - self.assertEqual(x.real, x) - self.assertEqual(x.imag, 0) - self.assertEqual(x.conjugate(), x) - - x = Decimal("1") - self.assertEqual(complex(x), complex(float(1))) - - self.assertRaises(AttributeError, setattr, x, 'real', 100) - self.assertRaises(AttributeError, setattr, x, 'imag', 100) - self.assertRaises(AttributeError, setattr, x, 'conjugate', 100) - self.assertRaises(AttributeError, setattr, x, '__complex__', 100) - - def test_named_parameters(self): - D = self.decimal.Decimal - Context = self.decimal.Context - localcontext = self.decimal.localcontext - InvalidOperation = self.decimal.InvalidOperation - Overflow = self.decimal.Overflow - - xc = Context() - xc.prec = 1 - xc.Emax = 1 - xc.Emin = -1 - - with localcontext() as c: - c.clear_flags() - - self.assertEqual(D(9, xc), 9) - self.assertEqual(D(9, context=xc), 9) - self.assertEqual(D(context=xc, value=9), 9) - self.assertEqual(D(context=xc), 0) - xc.clear_flags() - self.assertRaises(InvalidOperation, D, "xyz", context=xc) - self.assertTrue(xc.flags[InvalidOperation]) - self.assertFalse(c.flags[InvalidOperation]) - - xc.clear_flags() - self.assertEqual(D(2).exp(context=xc), 7) - self.assertRaises(Overflow, D(8).exp, context=xc) - self.assertTrue(xc.flags[Overflow]) - self.assertFalse(c.flags[Overflow]) - - xc.clear_flags() - self.assertEqual(D(2).ln(context=xc), D('0.7')) - self.assertRaises(InvalidOperation, D(-1).ln, context=xc) - self.assertTrue(xc.flags[InvalidOperation]) - self.assertFalse(c.flags[InvalidOperation]) - - self.assertEqual(D(0).log10(context=xc), D('-inf')) - self.assertEqual(D(-1).next_minus(context=xc), -2) - self.assertEqual(D(-1).next_plus(context=xc), D('-0.9')) - self.assertEqual(D("9.73").normalize(context=xc), D('1E+1')) - self.assertEqual(D("9999").to_integral(context=xc), 9999) - self.assertEqual(D("-2000").to_integral_exact(context=xc), -2000) - self.assertEqual(D("123").to_integral_value(context=xc), 123) - self.assertEqual(D("0.0625").sqrt(context=xc), D('0.2')) - - self.assertEqual(D("0.0625").compare(context=xc, other=3), -1) - xc.clear_flags() - self.assertRaises(InvalidOperation, - D("0").compare_signal, D('nan'), context=xc) - self.assertTrue(xc.flags[InvalidOperation]) - self.assertFalse(c.flags[InvalidOperation]) - self.assertEqual(D("0.01").max(D('0.0101'), context=xc), D('0.0')) - self.assertEqual(D("0.01").max(D('0.0101'), context=xc), D('0.0')) - self.assertEqual(D("0.2").max_mag(D('-0.3'), context=xc), - D('-0.3')) - self.assertEqual(D("0.02").min(D('-0.03'), context=xc), D('-0.0')) - self.assertEqual(D("0.02").min_mag(D('-0.03'), context=xc), - D('0.0')) - self.assertEqual(D("0.2").next_toward(D('-1'), context=xc), D('0.1')) - xc.clear_flags() - self.assertRaises(InvalidOperation, - D("0.2").quantize, D('1e10'), context=xc) - self.assertTrue(xc.flags[InvalidOperation]) - self.assertFalse(c.flags[InvalidOperation]) - self.assertEqual(D("9.99").remainder_near(D('1.5'), context=xc), - D('-0.5')) - - self.assertEqual(D("9.9").fma(third=D('0.9'), context=xc, other=7), - D('7E+1')) - - self.assertRaises(TypeError, D(1).is_canonical, context=xc) - self.assertRaises(TypeError, D(1).is_finite, context=xc) - self.assertRaises(TypeError, D(1).is_infinite, context=xc) - self.assertRaises(TypeError, D(1).is_nan, context=xc) - self.assertRaises(TypeError, D(1).is_qnan, context=xc) - self.assertRaises(TypeError, D(1).is_snan, context=xc) - self.assertRaises(TypeError, D(1).is_signed, context=xc) - self.assertRaises(TypeError, D(1).is_zero, context=xc) - - self.assertFalse(D("0.01").is_normal(context=xc)) - self.assertTrue(D("0.01").is_subnormal(context=xc)) - - self.assertRaises(TypeError, D(1).adjusted, context=xc) - self.assertRaises(TypeError, D(1).conjugate, context=xc) - self.assertRaises(TypeError, D(1).radix, context=xc) - - self.assertEqual(D(-111).logb(context=xc), 2) - self.assertEqual(D(0).logical_invert(context=xc), 1) - self.assertEqual(D('0.01').number_class(context=xc), '+Subnormal') - self.assertEqual(D('0.21').to_eng_string(context=xc), '0.21') - - self.assertEqual(D('11').logical_and(D('10'), context=xc), 0) - self.assertEqual(D('11').logical_or(D('10'), context=xc), 1) - self.assertEqual(D('01').logical_xor(D('10'), context=xc), 1) - self.assertEqual(D('23').rotate(1, context=xc), 3) - self.assertEqual(D('23').rotate(1, context=xc), 3) - xc.clear_flags() - self.assertRaises(Overflow, - D('23').scaleb, 1, context=xc) - self.assertTrue(xc.flags[Overflow]) - self.assertFalse(c.flags[Overflow]) - self.assertEqual(D('23').shift(-1, context=xc), 0) - - self.assertRaises(TypeError, D.from_float, 1.1, context=xc) - self.assertRaises(TypeError, D(0).as_tuple, context=xc) - - self.assertEqual(D(1).canonical(), 1) - self.assertRaises(TypeError, D("-1").copy_abs, context=xc) - self.assertRaises(TypeError, D("-1").copy_negate, context=xc) - self.assertRaises(TypeError, D(1).canonical, context="x") - self.assertRaises(TypeError, D(1).canonical, xyz="x") - - def test_exception_hierarchy(self): - - decimal = self.decimal - DecimalException = decimal.DecimalException - InvalidOperation = decimal.InvalidOperation - FloatOperation = decimal.FloatOperation - DivisionByZero = decimal.DivisionByZero - Overflow = decimal.Overflow - Underflow = decimal.Underflow - Subnormal = decimal.Subnormal - Inexact = decimal.Inexact - Rounded = decimal.Rounded - Clamped = decimal.Clamped - - self.assertIsSubclass(DecimalException, ArithmeticError) - - self.assertIsSubclass(InvalidOperation, DecimalException) - self.assertIsSubclass(FloatOperation, DecimalException) - self.assertIsSubclass(FloatOperation, TypeError) - self.assertIsSubclass(DivisionByZero, DecimalException) - self.assertIsSubclass(DivisionByZero, ZeroDivisionError) - self.assertIsSubclass(Overflow, Rounded) - self.assertIsSubclass(Overflow, Inexact) - self.assertIsSubclass(Overflow, DecimalException) - self.assertIsSubclass(Underflow, Inexact) - self.assertIsSubclass(Underflow, Rounded) - self.assertIsSubclass(Underflow, Subnormal) - self.assertIsSubclass(Underflow, DecimalException) - - self.assertIsSubclass(Subnormal, DecimalException) - self.assertIsSubclass(Inexact, DecimalException) - self.assertIsSubclass(Rounded, DecimalException) - self.assertIsSubclass(Clamped, DecimalException) - - self.assertIsSubclass(decimal.ConversionSyntax, InvalidOperation) - self.assertIsSubclass(decimal.DivisionImpossible, InvalidOperation) - self.assertIsSubclass(decimal.DivisionUndefined, InvalidOperation) - self.assertIsSubclass(decimal.DivisionUndefined, ZeroDivisionError) - self.assertIsSubclass(decimal.InvalidContext, InvalidOperation) - -@requires_cdecimal -class CPythonAPItests(PythonAPItests, unittest.TestCase): - decimal = C -class PyPythonAPItests(PythonAPItests, unittest.TestCase): - decimal = P - -class ContextAPItests: - - def test_none_args(self): - Context = self.decimal.Context - InvalidOperation = self.decimal.InvalidOperation - DivisionByZero = self.decimal.DivisionByZero - Overflow = self.decimal.Overflow - - c1 = Context() - c2 = Context(prec=None, rounding=None, Emax=None, Emin=None, - capitals=None, clamp=None, flags=None, traps=None) - for c in [c1, c2]: - self.assertEqual(c.prec, 28) - self.assertEqual(c.rounding, ROUND_HALF_EVEN) - self.assertEqual(c.Emax, 999999) - self.assertEqual(c.Emin, -999999) - self.assertEqual(c.capitals, 1) - self.assertEqual(c.clamp, 0) - assert_signals(self, c, 'flags', []) - assert_signals(self, c, 'traps', [InvalidOperation, DivisionByZero, - Overflow]) - - def test_pickle(self): - - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - Context = self.decimal.Context - - savedecimal = sys.modules['decimal'] - - # Round trip - sys.modules['decimal'] = self.decimal - c = Context() - e = pickle.loads(pickle.dumps(c, proto)) - - self.assertEqual(c.prec, e.prec) - self.assertEqual(c.Emin, e.Emin) - self.assertEqual(c.Emax, e.Emax) - self.assertEqual(c.rounding, e.rounding) - self.assertEqual(c.capitals, e.capitals) - self.assertEqual(c.clamp, e.clamp) - self.assertEqual(c.flags, e.flags) - self.assertEqual(c.traps, e.traps) - - # Test interchangeability - combinations = [(C, P), (P, C)] if C else [(P, P)] - for dumper, loader in combinations: - for ri, _ in enumerate(RoundingModes): - for fi, _ in enumerate(OrderedSignals[dumper]): - for ti, _ in enumerate(OrderedSignals[dumper]): - - prec = random.randrange(1, 100) - emin = random.randrange(-100, 0) - emax = random.randrange(1, 100) - caps = random.randrange(2) - clamp = random.randrange(2) - - # One module dumps - sys.modules['decimal'] = dumper - c = dumper.Context( - prec=prec, Emin=emin, Emax=emax, - rounding=RoundingModes[ri], - capitals=caps, clamp=clamp, - flags=OrderedSignals[dumper][:fi], - traps=OrderedSignals[dumper][:ti] - ) - s = pickle.dumps(c, proto) - - # The other module loads - sys.modules['decimal'] = loader - d = pickle.loads(s) - self.assertIsInstance(d, loader.Context) - - self.assertEqual(d.prec, prec) - self.assertEqual(d.Emin, emin) - self.assertEqual(d.Emax, emax) - self.assertEqual(d.rounding, RoundingModes[ri]) - self.assertEqual(d.capitals, caps) - self.assertEqual(d.clamp, clamp) - assert_signals(self, d, 'flags', OrderedSignals[loader][:fi]) - assert_signals(self, d, 'traps', OrderedSignals[loader][:ti]) - - sys.modules['decimal'] = savedecimal - - def test_equality_with_other_types(self): - Decimal = self.decimal.Decimal - - self.assertIn(Decimal(10), ['a', 1.0, Decimal(10), (1,2), {}]) - self.assertNotIn(Decimal(10), ['a', 1.0, (1,2), {}]) - - def test_copy(self): - # All copies should be deep - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.copy() - self.assertNotEqual(id(c), id(d)) - self.assertNotEqual(id(c.flags), id(d.flags)) - self.assertNotEqual(id(c.traps), id(d.traps)) - k1 = set(c.flags.keys()) - k2 = set(d.flags.keys()) - self.assertEqual(k1, k2) - self.assertEqual(c.flags, d.flags) - - def test__clamp(self): - # In Python 3.2, the private attribute `_clamp` was made - # public (issue 8540), with the old `_clamp` becoming a - # property wrapping `clamp`. For the duration of Python 3.2 - # only, the attribute should be gettable/settable via both - # `clamp` and `_clamp`; in Python 3.3, `_clamp` should be - # removed. - Context = self.decimal.Context - c = Context() - self.assertRaises(AttributeError, getattr, c, '_clamp') - - def test_abs(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.abs(Decimal(-1)) - self.assertEqual(c.abs(-1), d) - self.assertRaises(TypeError, c.abs, '-1') - - def test_add(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.add(Decimal(1), Decimal(1)) - self.assertEqual(c.add(1, 1), d) - self.assertEqual(c.add(Decimal(1), 1), d) - self.assertEqual(c.add(1, Decimal(1)), d) - self.assertRaises(TypeError, c.add, '1', 1) - self.assertRaises(TypeError, c.add, 1, '1') - - def test_compare(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.compare(Decimal(1), Decimal(1)) - self.assertEqual(c.compare(1, 1), d) - self.assertEqual(c.compare(Decimal(1), 1), d) - self.assertEqual(c.compare(1, Decimal(1)), d) - self.assertRaises(TypeError, c.compare, '1', 1) - self.assertRaises(TypeError, c.compare, 1, '1') - - def test_compare_signal(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.compare_signal(Decimal(1), Decimal(1)) - self.assertEqual(c.compare_signal(1, 1), d) - self.assertEqual(c.compare_signal(Decimal(1), 1), d) - self.assertEqual(c.compare_signal(1, Decimal(1)), d) - self.assertRaises(TypeError, c.compare_signal, '1', 1) - self.assertRaises(TypeError, c.compare_signal, 1, '1') - - def test_compare_total(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.compare_total(Decimal(1), Decimal(1)) - self.assertEqual(c.compare_total(1, 1), d) - self.assertEqual(c.compare_total(Decimal(1), 1), d) - self.assertEqual(c.compare_total(1, Decimal(1)), d) - self.assertRaises(TypeError, c.compare_total, '1', 1) - self.assertRaises(TypeError, c.compare_total, 1, '1') - - def test_compare_total_mag(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.compare_total_mag(Decimal(1), Decimal(1)) - self.assertEqual(c.compare_total_mag(1, 1), d) - self.assertEqual(c.compare_total_mag(Decimal(1), 1), d) - self.assertEqual(c.compare_total_mag(1, Decimal(1)), d) - self.assertRaises(TypeError, c.compare_total_mag, '1', 1) - self.assertRaises(TypeError, c.compare_total_mag, 1, '1') - - def test_copy_abs(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.copy_abs(Decimal(-1)) - self.assertEqual(c.copy_abs(-1), d) - self.assertRaises(TypeError, c.copy_abs, '-1') - - def test_copy_decimal(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.copy_decimal(Decimal(-1)) - self.assertEqual(c.copy_decimal(-1), d) - self.assertRaises(TypeError, c.copy_decimal, '-1') - - def test_copy_negate(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.copy_negate(Decimal(-1)) - self.assertEqual(c.copy_negate(-1), d) - self.assertRaises(TypeError, c.copy_negate, '-1') - - def test_copy_sign(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.copy_sign(Decimal(1), Decimal(-2)) - self.assertEqual(c.copy_sign(1, -2), d) - self.assertEqual(c.copy_sign(Decimal(1), -2), d) - self.assertEqual(c.copy_sign(1, Decimal(-2)), d) - self.assertRaises(TypeError, c.copy_sign, '1', -2) - self.assertRaises(TypeError, c.copy_sign, 1, '-2') - - def test_divide(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.divide(Decimal(1), Decimal(2)) - self.assertEqual(c.divide(1, 2), d) - self.assertEqual(c.divide(Decimal(1), 2), d) - self.assertEqual(c.divide(1, Decimal(2)), d) - self.assertRaises(TypeError, c.divide, '1', 2) - self.assertRaises(TypeError, c.divide, 1, '2') - - def test_divide_int(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.divide_int(Decimal(1), Decimal(2)) - self.assertEqual(c.divide_int(1, 2), d) - self.assertEqual(c.divide_int(Decimal(1), 2), d) - self.assertEqual(c.divide_int(1, Decimal(2)), d) - self.assertRaises(TypeError, c.divide_int, '1', 2) - self.assertRaises(TypeError, c.divide_int, 1, '2') - - def test_divmod(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.divmod(Decimal(1), Decimal(2)) - self.assertEqual(c.divmod(1, 2), d) - self.assertEqual(c.divmod(Decimal(1), 2), d) - self.assertEqual(c.divmod(1, Decimal(2)), d) - self.assertRaises(TypeError, c.divmod, '1', 2) - self.assertRaises(TypeError, c.divmod, 1, '2') - - def test_exp(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.exp(Decimal(10)) - self.assertEqual(c.exp(10), d) - self.assertRaises(TypeError, c.exp, '10') - - def test_fma(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.fma(Decimal(2), Decimal(3), Decimal(4)) - self.assertEqual(c.fma(2, 3, 4), d) - self.assertEqual(c.fma(Decimal(2), 3, 4), d) - self.assertEqual(c.fma(2, Decimal(3), 4), d) - self.assertEqual(c.fma(2, 3, Decimal(4)), d) - self.assertEqual(c.fma(Decimal(2), Decimal(3), 4), d) - self.assertRaises(TypeError, c.fma, '2', 3, 4) - self.assertRaises(TypeError, c.fma, 2, '3', 4) - self.assertRaises(TypeError, c.fma, 2, 3, '4') - - # Issue 12079 for Context.fma ... - self.assertRaises(TypeError, c.fma, - Decimal('Infinity'), Decimal(0), "not a decimal") - self.assertRaises(TypeError, c.fma, - Decimal(1), Decimal('snan'), 1.222) - # ... and for Decimal.fma. - self.assertRaises(TypeError, Decimal('Infinity').fma, - Decimal(0), "not a decimal") - self.assertRaises(TypeError, Decimal(1).fma, - Decimal('snan'), 1.222) - - def test_is_finite(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_finite(Decimal(10)) - self.assertEqual(c.is_finite(10), d) - self.assertRaises(TypeError, c.is_finite, '10') - - def test_is_infinite(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_infinite(Decimal(10)) - self.assertEqual(c.is_infinite(10), d) - self.assertRaises(TypeError, c.is_infinite, '10') - - def test_is_nan(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_nan(Decimal(10)) - self.assertEqual(c.is_nan(10), d) - self.assertRaises(TypeError, c.is_nan, '10') - - def test_is_normal(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_normal(Decimal(10)) - self.assertEqual(c.is_normal(10), d) - self.assertRaises(TypeError, c.is_normal, '10') - - def test_is_qnan(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_qnan(Decimal(10)) - self.assertEqual(c.is_qnan(10), d) - self.assertRaises(TypeError, c.is_qnan, '10') - - def test_is_signed(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_signed(Decimal(10)) - self.assertEqual(c.is_signed(10), d) - self.assertRaises(TypeError, c.is_signed, '10') - - def test_is_snan(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_snan(Decimal(10)) - self.assertEqual(c.is_snan(10), d) - self.assertRaises(TypeError, c.is_snan, '10') - - def test_is_subnormal(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_subnormal(Decimal(10)) - self.assertEqual(c.is_subnormal(10), d) - self.assertRaises(TypeError, c.is_subnormal, '10') - - def test_is_zero(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.is_zero(Decimal(10)) - self.assertEqual(c.is_zero(10), d) - self.assertRaises(TypeError, c.is_zero, '10') - - def test_ln(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.ln(Decimal(10)) - self.assertEqual(c.ln(10), d) - self.assertRaises(TypeError, c.ln, '10') - - def test_log10(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.log10(Decimal(10)) - self.assertEqual(c.log10(10), d) - self.assertRaises(TypeError, c.log10, '10') - - def test_logb(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.logb(Decimal(10)) - self.assertEqual(c.logb(10), d) - self.assertRaises(TypeError, c.logb, '10') - - def test_logical_and(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.logical_and(Decimal(1), Decimal(1)) - self.assertEqual(c.logical_and(1, 1), d) - self.assertEqual(c.logical_and(Decimal(1), 1), d) - self.assertEqual(c.logical_and(1, Decimal(1)), d) - self.assertRaises(TypeError, c.logical_and, '1', 1) - self.assertRaises(TypeError, c.logical_and, 1, '1') - - def test_logical_invert(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.logical_invert(Decimal(1000)) - self.assertEqual(c.logical_invert(1000), d) - self.assertRaises(TypeError, c.logical_invert, '1000') - - def test_logical_or(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.logical_or(Decimal(1), Decimal(1)) - self.assertEqual(c.logical_or(1, 1), d) - self.assertEqual(c.logical_or(Decimal(1), 1), d) - self.assertEqual(c.logical_or(1, Decimal(1)), d) - self.assertRaises(TypeError, c.logical_or, '1', 1) - self.assertRaises(TypeError, c.logical_or, 1, '1') - - def test_logical_xor(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.logical_xor(Decimal(1), Decimal(1)) - self.assertEqual(c.logical_xor(1, 1), d) - self.assertEqual(c.logical_xor(Decimal(1), 1), d) - self.assertEqual(c.logical_xor(1, Decimal(1)), d) - self.assertRaises(TypeError, c.logical_xor, '1', 1) - self.assertRaises(TypeError, c.logical_xor, 1, '1') - - def test_max(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.max(Decimal(1), Decimal(2)) - self.assertEqual(c.max(1, 2), d) - self.assertEqual(c.max(Decimal(1), 2), d) - self.assertEqual(c.max(1, Decimal(2)), d) - self.assertRaises(TypeError, c.max, '1', 2) - self.assertRaises(TypeError, c.max, 1, '2') - - def test_max_mag(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.max_mag(Decimal(1), Decimal(2)) - self.assertEqual(c.max_mag(1, 2), d) - self.assertEqual(c.max_mag(Decimal(1), 2), d) - self.assertEqual(c.max_mag(1, Decimal(2)), d) - self.assertRaises(TypeError, c.max_mag, '1', 2) - self.assertRaises(TypeError, c.max_mag, 1, '2') - - def test_min(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.min(Decimal(1), Decimal(2)) - self.assertEqual(c.min(1, 2), d) - self.assertEqual(c.min(Decimal(1), 2), d) - self.assertEqual(c.min(1, Decimal(2)), d) - self.assertRaises(TypeError, c.min, '1', 2) - self.assertRaises(TypeError, c.min, 1, '2') - - def test_min_mag(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.min_mag(Decimal(1), Decimal(2)) - self.assertEqual(c.min_mag(1, 2), d) - self.assertEqual(c.min_mag(Decimal(1), 2), d) - self.assertEqual(c.min_mag(1, Decimal(2)), d) - self.assertRaises(TypeError, c.min_mag, '1', 2) - self.assertRaises(TypeError, c.min_mag, 1, '2') - - def test_minus(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.minus(Decimal(10)) - self.assertEqual(c.minus(10), d) - self.assertRaises(TypeError, c.minus, '10') - - def test_multiply(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.multiply(Decimal(1), Decimal(2)) - self.assertEqual(c.multiply(1, 2), d) - self.assertEqual(c.multiply(Decimal(1), 2), d) - self.assertEqual(c.multiply(1, Decimal(2)), d) - self.assertRaises(TypeError, c.multiply, '1', 2) - self.assertRaises(TypeError, c.multiply, 1, '2') - - def test_next_minus(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.next_minus(Decimal(10)) - self.assertEqual(c.next_minus(10), d) - self.assertRaises(TypeError, c.next_minus, '10') - - def test_next_plus(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.next_plus(Decimal(10)) - self.assertEqual(c.next_plus(10), d) - self.assertRaises(TypeError, c.next_plus, '10') - - def test_next_toward(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.next_toward(Decimal(1), Decimal(2)) - self.assertEqual(c.next_toward(1, 2), d) - self.assertEqual(c.next_toward(Decimal(1), 2), d) - self.assertEqual(c.next_toward(1, Decimal(2)), d) - self.assertRaises(TypeError, c.next_toward, '1', 2) - self.assertRaises(TypeError, c.next_toward, 1, '2') - - def test_normalize(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.normalize(Decimal(10)) - self.assertEqual(c.normalize(10), d) - self.assertRaises(TypeError, c.normalize, '10') - - def test_number_class(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - self.assertEqual(c.number_class(123), c.number_class(Decimal(123))) - self.assertEqual(c.number_class(0), c.number_class(Decimal(0))) - self.assertEqual(c.number_class(-45), c.number_class(Decimal(-45))) - - def test_plus(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.plus(Decimal(10)) - self.assertEqual(c.plus(10), d) - self.assertRaises(TypeError, c.plus, '10') - - def test_power(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.power(Decimal(1), Decimal(4)) - self.assertEqual(c.power(1, 4), d) - self.assertEqual(c.power(Decimal(1), 4), d) - self.assertEqual(c.power(1, Decimal(4)), d) - self.assertEqual(c.power(Decimal(1), Decimal(4)), d) - self.assertRaises(TypeError, c.power, '1', 4) - self.assertRaises(TypeError, c.power, 1, '4') - self.assertEqual(c.power(modulo=5, b=8, a=2), 1) - - def test_quantize(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.quantize(Decimal(1), Decimal(2)) - self.assertEqual(c.quantize(1, 2), d) - self.assertEqual(c.quantize(Decimal(1), 2), d) - self.assertEqual(c.quantize(1, Decimal(2)), d) - self.assertRaises(TypeError, c.quantize, '1', 2) - self.assertRaises(TypeError, c.quantize, 1, '2') - - def test_remainder(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.remainder(Decimal(1), Decimal(2)) - self.assertEqual(c.remainder(1, 2), d) - self.assertEqual(c.remainder(Decimal(1), 2), d) - self.assertEqual(c.remainder(1, Decimal(2)), d) - self.assertRaises(TypeError, c.remainder, '1', 2) - self.assertRaises(TypeError, c.remainder, 1, '2') - - def test_remainder_near(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.remainder_near(Decimal(1), Decimal(2)) - self.assertEqual(c.remainder_near(1, 2), d) - self.assertEqual(c.remainder_near(Decimal(1), 2), d) - self.assertEqual(c.remainder_near(1, Decimal(2)), d) - self.assertRaises(TypeError, c.remainder_near, '1', 2) - self.assertRaises(TypeError, c.remainder_near, 1, '2') - - def test_rotate(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.rotate(Decimal(1), Decimal(2)) - self.assertEqual(c.rotate(1, 2), d) - self.assertEqual(c.rotate(Decimal(1), 2), d) - self.assertEqual(c.rotate(1, Decimal(2)), d) - self.assertRaises(TypeError, c.rotate, '1', 2) - self.assertRaises(TypeError, c.rotate, 1, '2') - - def test_sqrt(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.sqrt(Decimal(10)) - self.assertEqual(c.sqrt(10), d) - self.assertRaises(TypeError, c.sqrt, '10') - - def test_same_quantum(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.same_quantum(Decimal(1), Decimal(2)) - self.assertEqual(c.same_quantum(1, 2), d) - self.assertEqual(c.same_quantum(Decimal(1), 2), d) - self.assertEqual(c.same_quantum(1, Decimal(2)), d) - self.assertRaises(TypeError, c.same_quantum, '1', 2) - self.assertRaises(TypeError, c.same_quantum, 1, '2') - - def test_scaleb(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.scaleb(Decimal(1), Decimal(2)) - self.assertEqual(c.scaleb(1, 2), d) - self.assertEqual(c.scaleb(Decimal(1), 2), d) - self.assertEqual(c.scaleb(1, Decimal(2)), d) - self.assertRaises(TypeError, c.scaleb, '1', 2) - self.assertRaises(TypeError, c.scaleb, 1, '2') - - def test_shift(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.shift(Decimal(1), Decimal(2)) - self.assertEqual(c.shift(1, 2), d) - self.assertEqual(c.shift(Decimal(1), 2), d) - self.assertEqual(c.shift(1, Decimal(2)), d) - self.assertRaises(TypeError, c.shift, '1', 2) - self.assertRaises(TypeError, c.shift, 1, '2') - - def test_subtract(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.subtract(Decimal(1), Decimal(2)) - self.assertEqual(c.subtract(1, 2), d) - self.assertEqual(c.subtract(Decimal(1), 2), d) - self.assertEqual(c.subtract(1, Decimal(2)), d) - self.assertRaises(TypeError, c.subtract, '1', 2) - self.assertRaises(TypeError, c.subtract, 1, '2') - - def test_to_eng_string(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.to_eng_string(Decimal(10)) - self.assertEqual(c.to_eng_string(10), d) - self.assertRaises(TypeError, c.to_eng_string, '10') - - def test_to_sci_string(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.to_sci_string(Decimal(10)) - self.assertEqual(c.to_sci_string(10), d) - self.assertRaises(TypeError, c.to_sci_string, '10') - - def test_to_integral_exact(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.to_integral_exact(Decimal(10)) - self.assertEqual(c.to_integral_exact(10), d) - self.assertRaises(TypeError, c.to_integral_exact, '10') - - def test_to_integral_value(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - d = c.to_integral_value(Decimal(10)) - self.assertEqual(c.to_integral_value(10), d) - self.assertRaises(TypeError, c.to_integral_value, '10') - self.assertRaises(TypeError, c.to_integral_value, 10, 'x') - -@requires_cdecimal -class CContextAPItests(ContextAPItests, unittest.TestCase): - decimal = C -class PyContextAPItests(ContextAPItests, unittest.TestCase): - decimal = P - -class ContextWithStatement: - # Can't do these as docstrings until Python 2.6 - # as doctest can't handle __future__ statements - - def test_localcontext(self): - # Use a copy of the current context in the block - getcontext = self.decimal.getcontext - localcontext = self.decimal.localcontext - - orig_ctx = getcontext() - with localcontext() as enter_ctx: - set_ctx = getcontext() - final_ctx = getcontext() - self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') - self.assertIsNot(orig_ctx, set_ctx, 'did not copy the context') - self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context') - - def test_localcontextarg(self): - # Use a copy of the supplied context in the block - Context = self.decimal.Context - getcontext = self.decimal.getcontext - localcontext = self.decimal.localcontext - - localcontext = self.decimal.localcontext - orig_ctx = getcontext() - new_ctx = Context(prec=42) - with localcontext(new_ctx) as enter_ctx: - set_ctx = getcontext() - final_ctx = getcontext() - self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') - self.assertEqual(set_ctx.prec, new_ctx.prec, 'did not set correct context') - self.assertIsNot(new_ctx, set_ctx, 'did not copy the context') - self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context') - - def test_localcontext_kwargs(self): - with self.decimal.localcontext( - prec=10, rounding=ROUND_HALF_DOWN, - Emin=-20, Emax=20, capitals=0, - clamp=1 - ) as ctx: - self.assertEqual(ctx.prec, 10) - self.assertEqual(ctx.rounding, self.decimal.ROUND_HALF_DOWN) - self.assertEqual(ctx.Emin, -20) - self.assertEqual(ctx.Emax, 20) - self.assertEqual(ctx.capitals, 0) - self.assertEqual(ctx.clamp, 1) - - self.assertRaises(TypeError, self.decimal.localcontext, precision=10) - - self.assertRaises(ValueError, self.decimal.localcontext, Emin=1) - self.assertRaises(ValueError, self.decimal.localcontext, Emax=-1) - self.assertRaises(ValueError, self.decimal.localcontext, capitals=2) - self.assertRaises(ValueError, self.decimal.localcontext, clamp=2) - - self.assertRaises(TypeError, self.decimal.localcontext, rounding="") - self.assertRaises(TypeError, self.decimal.localcontext, rounding=1) - - self.assertRaises(TypeError, self.decimal.localcontext, flags="") - self.assertRaises(TypeError, self.decimal.localcontext, traps="") - self.assertRaises(TypeError, self.decimal.localcontext, Emin="") - self.assertRaises(TypeError, self.decimal.localcontext, Emax="") - - def test_local_context_kwargs_does_not_overwrite_existing_argument(self): - ctx = self.decimal.getcontext() - orig_prec = ctx.prec - with self.decimal.localcontext(prec=10) as ctx2: - self.assertEqual(ctx2.prec, 10) - self.assertEqual(ctx.prec, orig_prec) - with self.decimal.localcontext(prec=20) as ctx2: - self.assertEqual(ctx2.prec, 20) - self.assertEqual(ctx.prec, orig_prec) - - def test_nested_with_statements(self): - # Use a copy of the supplied context in the block - Decimal = self.decimal.Decimal - Context = self.decimal.Context - getcontext = self.decimal.getcontext - localcontext = self.decimal.localcontext - Clamped = self.decimal.Clamped - Overflow = self.decimal.Overflow - - orig_ctx = getcontext() - orig_ctx.clear_flags() - new_ctx = Context(Emax=384) - with localcontext() as c1: - self.assertEqual(c1.flags, orig_ctx.flags) - self.assertEqual(c1.traps, orig_ctx.traps) - c1.traps[Clamped] = True - c1.Emin = -383 - self.assertNotEqual(orig_ctx.Emin, -383) - self.assertRaises(Clamped, c1.create_decimal, '0e-999') - self.assertTrue(c1.flags[Clamped]) - with localcontext(new_ctx) as c2: - self.assertEqual(c2.flags, new_ctx.flags) - self.assertEqual(c2.traps, new_ctx.traps) - self.assertRaises(Overflow, c2.power, Decimal('3.4e200'), 2) - self.assertFalse(c2.flags[Clamped]) - self.assertTrue(c2.flags[Overflow]) - del c2 - self.assertFalse(c1.flags[Overflow]) - del c1 - self.assertNotEqual(orig_ctx.Emin, -383) - self.assertFalse(orig_ctx.flags[Clamped]) - self.assertFalse(orig_ctx.flags[Overflow]) - self.assertFalse(new_ctx.flags[Clamped]) - self.assertFalse(new_ctx.flags[Overflow]) - - def test_with_statements_gc1(self): - localcontext = self.decimal.localcontext - - with localcontext() as c1: - del c1 - with localcontext() as c2: - del c2 - with localcontext() as c3: - del c3 - with localcontext() as c4: - del c4 - - def test_with_statements_gc2(self): - localcontext = self.decimal.localcontext - - with localcontext() as c1: - with localcontext(c1) as c2: - del c1 - with localcontext(c2) as c3: - del c2 - with localcontext(c3) as c4: - del c3 - del c4 - - def test_with_statements_gc3(self): - Context = self.decimal.Context - localcontext = self.decimal.localcontext - getcontext = self.decimal.getcontext - setcontext = self.decimal.setcontext - - with localcontext() as c1: - del c1 - n1 = Context(prec=1) - setcontext(n1) - with localcontext(n1) as c2: - del n1 - self.assertEqual(c2.prec, 1) - del c2 - n2 = Context(prec=2) - setcontext(n2) - del n2 - self.assertEqual(getcontext().prec, 2) - n3 = Context(prec=3) - setcontext(n3) - self.assertEqual(getcontext().prec, 3) - with localcontext(n3) as c3: - del n3 - self.assertEqual(c3.prec, 3) - del c3 - n4 = Context(prec=4) - setcontext(n4) - del n4 - self.assertEqual(getcontext().prec, 4) - with localcontext() as c4: - self.assertEqual(c4.prec, 4) - del c4 - -@requires_cdecimal -class CContextWithStatement(ContextWithStatement, unittest.TestCase): - decimal = C -class PyContextWithStatement(ContextWithStatement, unittest.TestCase): - decimal = P - -class ContextFlags: - - def test_flags_irrelevant(self): - # check that the result (numeric result + flags raised) of an - # arithmetic operation doesn't depend on the current flags - Decimal = self.decimal.Decimal - Context = self.decimal.Context - Inexact = self.decimal.Inexact - Rounded = self.decimal.Rounded - Underflow = self.decimal.Underflow - Clamped = self.decimal.Clamped - Subnormal = self.decimal.Subnormal - - def raise_error(context, flag): - if self.decimal == C: - context.flags[flag] = True - if context.traps[flag]: - raise flag - else: - context._raise_error(flag) - - context = Context(prec=9, Emin = -425000000, Emax = 425000000, - rounding=ROUND_HALF_EVEN, traps=[], flags=[]) - - # operations that raise various flags, in the form (function, arglist) - operations = [ - (context._apply, [Decimal("100E-425000010")]), - (context.sqrt, [Decimal(2)]), - (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]), - (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]), - (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]), - ] - - # try various flags individually, then a whole lot at once - flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal], - [Inexact, Rounded, Underflow, Clamped, Subnormal]] - - for fn, args in operations: - # find answer and flags raised using a clean context - context.clear_flags() - ans = fn(*args) - flags = [k for k, v in context.flags.items() if v] - - for extra_flags in flagsets: - # set flags, before calling operation - context.clear_flags() - for flag in extra_flags: - raise_error(context, flag) - new_ans = fn(*args) - - # flags that we expect to be set after the operation - expected_flags = list(flags) - for flag in extra_flags: - if flag not in expected_flags: - expected_flags.append(flag) - expected_flags.sort(key=id) - - # flags we actually got - new_flags = [k for k,v in context.flags.items() if v] - new_flags.sort(key=id) - - self.assertEqual(ans, new_ans, - "operation produces different answers depending on flags set: " + - "expected %s, got %s." % (ans, new_ans)) - self.assertEqual(new_flags, expected_flags, - "operation raises different flags depending on flags set: " + - "expected %s, got %s" % (expected_flags, new_flags)) - - def test_flag_comparisons(self): - Context = self.decimal.Context - Inexact = self.decimal.Inexact - Rounded = self.decimal.Rounded - - c = Context() - - # Valid SignalDict - self.assertNotEqual(c.flags, c.traps) - self.assertNotEqual(c.traps, c.flags) - - c.flags = c.traps - self.assertEqual(c.flags, c.traps) - self.assertEqual(c.traps, c.flags) - - c.flags[Rounded] = True - c.traps = c.flags - self.assertEqual(c.flags, c.traps) - self.assertEqual(c.traps, c.flags) - - d = {} - d.update(c.flags) - self.assertEqual(d, c.flags) - self.assertEqual(c.flags, d) - - d[Inexact] = True - self.assertNotEqual(d, c.flags) - self.assertNotEqual(c.flags, d) - - # Invalid SignalDict - d = {Inexact:False} - self.assertNotEqual(d, c.flags) - self.assertNotEqual(c.flags, d) - - d = ["xyz"] - self.assertNotEqual(d, c.flags) - self.assertNotEqual(c.flags, d) - - @requires_IEEE_754 - def test_float_operation(self): - Decimal = self.decimal.Decimal - FloatOperation = self.decimal.FloatOperation - localcontext = self.decimal.localcontext - - with localcontext() as c: - ##### trap is off by default - self.assertFalse(c.traps[FloatOperation]) - - # implicit conversion sets the flag - c.clear_flags() - self.assertEqual(Decimal(7.5), 7.5) - self.assertTrue(c.flags[FloatOperation]) - - c.clear_flags() - self.assertEqual(c.create_decimal(7.5), 7.5) - self.assertTrue(c.flags[FloatOperation]) - - # explicit conversion does not set the flag - c.clear_flags() - x = Decimal.from_float(7.5) - self.assertFalse(c.flags[FloatOperation]) - # comparison sets the flag - self.assertEqual(x, 7.5) - self.assertTrue(c.flags[FloatOperation]) - - c.clear_flags() - x = c.create_decimal_from_float(7.5) - self.assertFalse(c.flags[FloatOperation]) - self.assertEqual(x, 7.5) - self.assertTrue(c.flags[FloatOperation]) - - ##### set the trap - c.traps[FloatOperation] = True - - # implicit conversion raises - c.clear_flags() - self.assertRaises(FloatOperation, Decimal, 7.5) - self.assertTrue(c.flags[FloatOperation]) - - c.clear_flags() - self.assertRaises(FloatOperation, c.create_decimal, 7.5) - self.assertTrue(c.flags[FloatOperation]) - - # explicit conversion is silent - c.clear_flags() - x = Decimal.from_float(7.5) - self.assertFalse(c.flags[FloatOperation]) - - c.clear_flags() - x = c.create_decimal_from_float(7.5) - self.assertFalse(c.flags[FloatOperation]) - - def test_float_comparison(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - FloatOperation = self.decimal.FloatOperation - localcontext = self.decimal.localcontext - - def assert_attr(a, b, attr, context, signal=None): - context.clear_flags() - f = getattr(a, attr) - if signal == FloatOperation: - self.assertRaises(signal, f, b) - else: - self.assertIs(f(b), True) - self.assertTrue(context.flags[FloatOperation]) - - small_d = Decimal('0.25') - big_d = Decimal('3.0') - small_f = 0.25 - big_f = 3.0 - - zero_d = Decimal('0.0') - neg_zero_d = Decimal('-0.0') - zero_f = 0.0 - neg_zero_f = -0.0 - - inf_d = Decimal('Infinity') - neg_inf_d = Decimal('-Infinity') - inf_f = float('inf') - neg_inf_f = float('-inf') - - def doit(c, signal=None): - # Order - for attr in '__lt__', '__le__': - assert_attr(small_d, big_f, attr, c, signal) - - for attr in '__gt__', '__ge__': - assert_attr(big_d, small_f, attr, c, signal) - - # Equality - assert_attr(small_d, small_f, '__eq__', c, None) - - assert_attr(neg_zero_d, neg_zero_f, '__eq__', c, None) - assert_attr(neg_zero_d, zero_f, '__eq__', c, None) - - assert_attr(zero_d, neg_zero_f, '__eq__', c, None) - assert_attr(zero_d, zero_f, '__eq__', c, None) - - assert_attr(neg_inf_d, neg_inf_f, '__eq__', c, None) - assert_attr(inf_d, inf_f, '__eq__', c, None) - - # Inequality - assert_attr(small_d, big_f, '__ne__', c, None) - - assert_attr(Decimal('0.1'), 0.1, '__ne__', c, None) - - assert_attr(neg_inf_d, inf_f, '__ne__', c, None) - assert_attr(inf_d, neg_inf_f, '__ne__', c, None) - - assert_attr(Decimal('NaN'), float('nan'), '__ne__', c, None) - - def test_containers(c, signal=None): - c.clear_flags() - s = set([100.0, Decimal('100.0')]) - self.assertEqual(len(s), 1) - self.assertTrue(c.flags[FloatOperation]) - - c.clear_flags() - if signal: - self.assertRaises(signal, sorted, [1.0, Decimal('10.0')]) - else: - s = sorted([10.0, Decimal('10.0')]) - self.assertTrue(c.flags[FloatOperation]) - - c.clear_flags() - b = 10.0 in [Decimal('10.0'), 1.0] - self.assertTrue(c.flags[FloatOperation]) - - c.clear_flags() - b = 10.0 in {Decimal('10.0'):'a', 1.0:'b'} - self.assertTrue(c.flags[FloatOperation]) - - nc = Context() - with localcontext(nc) as c: - self.assertFalse(c.traps[FloatOperation]) - doit(c, signal=None) - test_containers(c, signal=None) - - c.traps[FloatOperation] = True - doit(c, signal=FloatOperation) - test_containers(c, signal=FloatOperation) - - def test_float_operation_default(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - Inexact = self.decimal.Inexact - FloatOperation= self.decimal.FloatOperation - - context = Context() - self.assertFalse(context.flags[FloatOperation]) - self.assertFalse(context.traps[FloatOperation]) - - context.clear_traps() - context.traps[Inexact] = True - context.traps[FloatOperation] = True - self.assertTrue(context.traps[FloatOperation]) - self.assertTrue(context.traps[Inexact]) - -@requires_cdecimal -class CContextFlags(ContextFlags, unittest.TestCase): - decimal = C -class PyContextFlags(ContextFlags, unittest.TestCase): - decimal = P - -class SpecialContexts: - """Test the context templates.""" - - def test_context_templates(self): - BasicContext = self.decimal.BasicContext - ExtendedContext = self.decimal.ExtendedContext - getcontext = self.decimal.getcontext - setcontext = self.decimal.setcontext - InvalidOperation = self.decimal.InvalidOperation - DivisionByZero = self.decimal.DivisionByZero - Overflow = self.decimal.Overflow - Underflow = self.decimal.Underflow - Clamped = self.decimal.Clamped - - assert_signals(self, BasicContext, 'traps', - [InvalidOperation, DivisionByZero, Overflow, Underflow, Clamped] - ) - - savecontext = getcontext().copy() - basic_context_prec = BasicContext.prec - extended_context_prec = ExtendedContext.prec - - ex = None - try: - BasicContext.prec = ExtendedContext.prec = 441 - for template in BasicContext, ExtendedContext: - setcontext(template) - c = getcontext() - self.assertIsNot(c, template) - self.assertEqual(c.prec, 441) - except Exception as e: - ex = e.__class__ - finally: - BasicContext.prec = basic_context_prec - ExtendedContext.prec = extended_context_prec - setcontext(savecontext) - if ex: - raise ex - - def test_default_context(self): - DefaultContext = self.decimal.DefaultContext - BasicContext = self.decimal.BasicContext - ExtendedContext = self.decimal.ExtendedContext - getcontext = self.decimal.getcontext - setcontext = self.decimal.setcontext - InvalidOperation = self.decimal.InvalidOperation - DivisionByZero = self.decimal.DivisionByZero - Overflow = self.decimal.Overflow - - self.assertEqual(BasicContext.prec, 9) - self.assertEqual(ExtendedContext.prec, 9) - - assert_signals(self, DefaultContext, 'traps', - [InvalidOperation, DivisionByZero, Overflow] - ) - - savecontext = getcontext().copy() - default_context_prec = DefaultContext.prec - - ex = None - try: - c = getcontext() - saveprec = c.prec - - DefaultContext.prec = 961 - c = getcontext() - self.assertEqual(c.prec, saveprec) - - setcontext(DefaultContext) - c = getcontext() - self.assertIsNot(c, DefaultContext) - self.assertEqual(c.prec, 961) - except Exception as e: - ex = e.__class__ - finally: - DefaultContext.prec = default_context_prec - setcontext(savecontext) - if ex: - raise ex - -@requires_cdecimal -class CSpecialContexts(SpecialContexts, unittest.TestCase): - decimal = C -class PySpecialContexts(SpecialContexts, unittest.TestCase): - decimal = P - -class ContextInputValidation: - - def test_invalid_context(self): - Context = self.decimal.Context - DefaultContext = self.decimal.DefaultContext - - c = DefaultContext.copy() - - # prec, Emax - for attr in ['prec', 'Emax']: - setattr(c, attr, 999999) - self.assertEqual(getattr(c, attr), 999999) - self.assertRaises(ValueError, setattr, c, attr, -1) - self.assertRaises(TypeError, setattr, c, attr, 'xyz') - - # Emin - setattr(c, 'Emin', -999999) - self.assertEqual(getattr(c, 'Emin'), -999999) - self.assertRaises(ValueError, setattr, c, 'Emin', 1) - self.assertRaises(TypeError, setattr, c, 'Emin', (1,2,3)) - - self.assertRaises(TypeError, setattr, c, 'rounding', -1) - self.assertRaises(TypeError, setattr, c, 'rounding', 9) - self.assertRaises(TypeError, setattr, c, 'rounding', 1.0) - self.assertRaises(TypeError, setattr, c, 'rounding', 'xyz') - - # capitals, clamp - for attr in ['capitals', 'clamp']: - self.assertRaises(ValueError, setattr, c, attr, -1) - self.assertRaises(ValueError, setattr, c, attr, 2) - self.assertRaises(TypeError, setattr, c, attr, [1,2,3]) - - # Invalid attribute - self.assertRaises(AttributeError, setattr, c, 'emax', 100) - - # Invalid signal dict - self.assertRaises(TypeError, setattr, c, 'flags', []) - self.assertRaises(KeyError, setattr, c, 'flags', {}) - self.assertRaises(KeyError, setattr, c, 'traps', - {'InvalidOperation':0}) - - # Attributes cannot be deleted - for attr in ['prec', 'Emax', 'Emin', 'rounding', 'capitals', 'clamp', - 'flags', 'traps']: - self.assertRaises(AttributeError, c.__delattr__, attr) - - # Invalid attributes - self.assertRaises(TypeError, getattr, c, 9) - self.assertRaises(TypeError, setattr, c, 9) - - # Invalid values in constructor - self.assertRaises(TypeError, Context, rounding=999999) - self.assertRaises(TypeError, Context, rounding='xyz') - self.assertRaises(ValueError, Context, clamp=2) - self.assertRaises(ValueError, Context, capitals=-1) - self.assertRaises(KeyError, Context, flags=["P"]) - self.assertRaises(KeyError, Context, traps=["Q"]) - - # Type error in conversion - self.assertRaises(TypeError, Context, flags=(0,1)) - self.assertRaises(TypeError, Context, traps=(1,0)) - -@requires_cdecimal -class CContextInputValidation(ContextInputValidation, unittest.TestCase): - decimal = C -class PyContextInputValidation(ContextInputValidation, unittest.TestCase): - decimal = P - -class ContextSubclassing: - - def test_context_subclassing(self): - decimal = self.decimal - Decimal = decimal.Decimal - Context = decimal.Context - Clamped = decimal.Clamped - DivisionByZero = decimal.DivisionByZero - Inexact = decimal.Inexact - Overflow = decimal.Overflow - Rounded = decimal.Rounded - Subnormal = decimal.Subnormal - Underflow = decimal.Underflow - InvalidOperation = decimal.InvalidOperation - - class MyContext(Context): - def __init__(self, prec=None, rounding=None, Emin=None, Emax=None, - capitals=None, clamp=None, flags=None, - traps=None): - Context.__init__(self) - if prec is not None: - self.prec = prec - if rounding is not None: - self.rounding = rounding - if Emin is not None: - self.Emin = Emin - if Emax is not None: - self.Emax = Emax - if capitals is not None: - self.capitals = capitals - if clamp is not None: - self.clamp = clamp - if flags is not None: - if isinstance(flags, list): - flags = {v:(v in flags) for v in OrderedSignals[decimal] + flags} - self.flags = flags - if traps is not None: - if isinstance(traps, list): - traps = {v:(v in traps) for v in OrderedSignals[decimal] + traps} - self.traps = traps - - c = Context() - d = MyContext() - for attr in ('prec', 'rounding', 'Emin', 'Emax', 'capitals', 'clamp', - 'flags', 'traps'): - self.assertEqual(getattr(c, attr), getattr(d, attr)) - - # prec - self.assertRaises(ValueError, MyContext, **{'prec':-1}) - c = MyContext(prec=1) - self.assertEqual(c.prec, 1) - self.assertRaises(InvalidOperation, c.quantize, Decimal('9e2'), 0) - - # rounding - self.assertRaises(TypeError, MyContext, **{'rounding':'XYZ'}) - c = MyContext(rounding=ROUND_DOWN, prec=1) - self.assertEqual(c.rounding, ROUND_DOWN) - self.assertEqual(c.plus(Decimal('9.9')), 9) - - # Emin - self.assertRaises(ValueError, MyContext, **{'Emin':5}) - c = MyContext(Emin=-1, prec=1) - self.assertEqual(c.Emin, -1) - x = c.add(Decimal('1e-99'), Decimal('2.234e-2000')) - self.assertEqual(x, Decimal('0.0')) - for signal in (Inexact, Underflow, Subnormal, Rounded, Clamped): - self.assertTrue(c.flags[signal]) - - # Emax - self.assertRaises(ValueError, MyContext, **{'Emax':-1}) - c = MyContext(Emax=1, prec=1) - self.assertEqual(c.Emax, 1) - self.assertRaises(Overflow, c.add, Decimal('1e99'), Decimal('2.234e2000')) - if self.decimal == C: - for signal in (Inexact, Overflow, Rounded): - self.assertTrue(c.flags[signal]) - - # capitals - self.assertRaises(ValueError, MyContext, **{'capitals':-1}) - c = MyContext(capitals=0) - self.assertEqual(c.capitals, 0) - x = c.create_decimal('1E222') - self.assertEqual(c.to_sci_string(x), '1e+222') - - # clamp - self.assertRaises(ValueError, MyContext, **{'clamp':2}) - c = MyContext(clamp=1, Emax=99) - self.assertEqual(c.clamp, 1) - x = c.plus(Decimal('1e99')) - self.assertEqual(str(x), '1.000000000000000000000000000E+99') - - # flags - self.assertRaises(TypeError, MyContext, **{'flags':'XYZ'}) - c = MyContext(flags=[Rounded, DivisionByZero]) - for signal in (Rounded, DivisionByZero): - self.assertTrue(c.flags[signal]) - c.clear_flags() - for signal in OrderedSignals[decimal]: - self.assertFalse(c.flags[signal]) - - # traps - self.assertRaises(TypeError, MyContext, **{'traps':'XYZ'}) - c = MyContext(traps=[Rounded, DivisionByZero]) - for signal in (Rounded, DivisionByZero): - self.assertTrue(c.traps[signal]) - c.clear_traps() - for signal in OrderedSignals[decimal]: - self.assertFalse(c.traps[signal]) - -@requires_cdecimal -class CContextSubclassing(ContextSubclassing, unittest.TestCase): - decimal = C -class PyContextSubclassing(ContextSubclassing, unittest.TestCase): - decimal = P - -class IEEEContexts: - - def test_ieee_context(self): - # issue 8786: Add support for IEEE 754 contexts to decimal module. - IEEEContext = self.decimal.IEEEContext - - def assert_rest(self, context): - self.assertEqual(context.clamp, 1) - assert_signals(self, context, 'traps', []) - assert_signals(self, context, 'flags', []) - - c = IEEEContext(32) - self.assertEqual(c.prec, 7) - self.assertEqual(c.Emax, 96) - self.assertEqual(c.Emin, -95) - assert_rest(self, c) - - c = IEEEContext(64) - self.assertEqual(c.prec, 16) - self.assertEqual(c.Emax, 384) - self.assertEqual(c.Emin, -383) - assert_rest(self, c) - - c = IEEEContext(128) - self.assertEqual(c.prec, 34) - self.assertEqual(c.Emax, 6144) - self.assertEqual(c.Emin, -6143) - assert_rest(self, c) - - # Invalid values - self.assertRaises(ValueError, IEEEContext, -1) - self.assertRaises(ValueError, IEEEContext, 123) - self.assertRaises(ValueError, IEEEContext, 1024) - - def test_constants(self): - # IEEEContext - IEEE_CONTEXT_MAX_BITS = self.decimal.IEEE_CONTEXT_MAX_BITS - self.assertIn(IEEE_CONTEXT_MAX_BITS, {256, 512}) - -@requires_cdecimal -class CIEEEContexts(IEEEContexts, unittest.TestCase): - decimal = C -class PyIEEEContexts(IEEEContexts, unittest.TestCase): - decimal = P - -@skip_if_extra_functionality -@requires_cdecimal -class CheckAttributes(unittest.TestCase): - - def test_module_attributes(self): - - # Architecture dependent context limits - self.assertEqual(C.MAX_PREC, P.MAX_PREC) - self.assertEqual(C.MAX_EMAX, P.MAX_EMAX) - self.assertEqual(C.MIN_EMIN, P.MIN_EMIN) - self.assertEqual(C.MIN_ETINY, P.MIN_ETINY) - self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, P.IEEE_CONTEXT_MAX_BITS) - - self.assertTrue(C.HAVE_THREADS is True or C.HAVE_THREADS is False) - self.assertTrue(P.HAVE_THREADS is True or P.HAVE_THREADS is False) - - self.assertEqual(C.SPEC_VERSION, P.SPEC_VERSION) - - self.assertLessEqual(set(dir(C)), set(dir(P))) - self.assertEqual([n for n in dir(C) if n[:2] != '__'], sorted(P.__all__)) - - def test_context_attributes(self): - - x = [s for s in dir(C.Context()) if '__' in s or not s.startswith('_')] - y = [s for s in dir(P.Context()) if '__' in s or not s.startswith('_')] - self.assertEqual(set(x) - set(y), set()) - - def test_decimal_attributes(self): - - x = [s for s in dir(C.Decimal(9)) if '__' in s or not s.startswith('_')] - y = [s for s in dir(C.Decimal(9)) if '__' in s or not s.startswith('_')] - self.assertEqual(set(x) - set(y), set()) - -class Coverage: - - def test_adjusted(self): - Decimal = self.decimal.Decimal - - self.assertEqual(Decimal('1234e9999').adjusted(), 10002) - # XXX raise? - self.assertEqual(Decimal('nan').adjusted(), 0) - self.assertEqual(Decimal('inf').adjusted(), 0) - - def test_canonical(self): - Decimal = self.decimal.Decimal - getcontext = self.decimal.getcontext - - x = Decimal(9).canonical() - self.assertEqual(x, 9) - - c = getcontext() - x = c.canonical(Decimal(9)) - self.assertEqual(x, 9) - - def test_context_repr(self): - c = self.decimal.DefaultContext.copy() - - c.prec = 425000000 - c.Emax = 425000000 - c.Emin = -425000000 - c.rounding = ROUND_HALF_DOWN - c.capitals = 0 - c.clamp = 1 - for sig in OrderedSignals[self.decimal]: - c.flags[sig] = False - c.traps[sig] = False - - s = c.__repr__() - t = "Context(prec=425000000, rounding=ROUND_HALF_DOWN, " \ - "Emin=-425000000, Emax=425000000, capitals=0, clamp=1, " \ - "flags=[], traps=[])" - self.assertEqual(s, t) - - def test_implicit_context(self): - Decimal = self.decimal.Decimal - localcontext = self.decimal.localcontext - - with localcontext() as c: - c.prec = 1 - c.Emax = 1 - c.Emin = -1 - - # abs - self.assertEqual(abs(Decimal("-10")), 10) - # add - self.assertEqual(Decimal("7") + 1, 8) - # divide - self.assertEqual(Decimal("10") / 5, 2) - # divide_int - self.assertEqual(Decimal("10") // 7, 1) - # fma - self.assertEqual(Decimal("1.2").fma(Decimal("0.01"), 1), 1) - self.assertIs(Decimal("NaN").fma(7, 1).is_nan(), True) - # three arg power - self.assertEqual(pow(Decimal(10), 2, 7), 2) - self.assertEqual(pow(10, Decimal(2), 7), 2) - if self.decimal == C: - self.assertEqual(pow(10, 2, Decimal(7)), 2) - else: - # XXX: There is no special method to dispatch on the - # third arg of three-arg power. - self.assertRaises(TypeError, pow, 10, 2, Decimal(7)) - # exp - self.assertEqual(Decimal("1.01").exp(), 3) - # is_normal - self.assertIs(Decimal("0.01").is_normal(), False) - # is_subnormal - self.assertIs(Decimal("0.01").is_subnormal(), True) - # ln - self.assertEqual(Decimal("20").ln(), 3) - # log10 - self.assertEqual(Decimal("20").log10(), 1) - # logb - self.assertEqual(Decimal("580").logb(), 2) - # logical_invert - self.assertEqual(Decimal("10").logical_invert(), 1) - # minus - self.assertEqual(-Decimal("-10"), 10) - # multiply - self.assertEqual(Decimal("2") * 4, 8) - # next_minus - self.assertEqual(Decimal("10").next_minus(), 9) - # next_plus - self.assertEqual(Decimal("10").next_plus(), Decimal('2E+1')) - # normalize - self.assertEqual(Decimal("-10").normalize(), Decimal('-1E+1')) - # number_class - self.assertEqual(Decimal("10").number_class(), '+Normal') - # plus - self.assertEqual(+Decimal("-1"), -1) - # remainder - self.assertEqual(Decimal("10") % 7, 3) - # subtract - self.assertEqual(Decimal("10") - 7, 3) - # to_integral_exact - self.assertEqual(Decimal("1.12345").to_integral_exact(), 1) - - # Boolean functions - self.assertTrue(Decimal("1").is_canonical()) - self.assertTrue(Decimal("1").is_finite()) - self.assertTrue(Decimal("1").is_finite()) - self.assertTrue(Decimal("snan").is_snan()) - self.assertTrue(Decimal("-1").is_signed()) - self.assertTrue(Decimal("0").is_zero()) - self.assertTrue(Decimal("0").is_zero()) - - # Copy - with localcontext() as c: - c.prec = 10000 - x = 1228 ** 1523 - y = -Decimal(x) - - z = y.copy_abs() - self.assertEqual(z, x) - - z = y.copy_negate() - self.assertEqual(z, x) - - z = y.copy_sign(Decimal(1)) - self.assertEqual(z, x) - - def test_divmod(self): - Decimal = self.decimal.Decimal - localcontext = self.decimal.localcontext - InvalidOperation = self.decimal.InvalidOperation - DivisionByZero = self.decimal.DivisionByZero - - with localcontext() as c: - q, r = divmod(Decimal("10912837129"), 1001) - self.assertEqual(q, Decimal('10901935')) - self.assertEqual(r, Decimal('194')) - - q, r = divmod(Decimal("NaN"), 7) - self.assertTrue(q.is_nan() and r.is_nan()) - - c.traps[InvalidOperation] = False - q, r = divmod(Decimal("NaN"), 7) - self.assertTrue(q.is_nan() and r.is_nan()) - - c.traps[InvalidOperation] = False - c.clear_flags() - q, r = divmod(Decimal("inf"), Decimal("inf")) - self.assertTrue(q.is_nan() and r.is_nan()) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - q, r = divmod(Decimal("inf"), 101) - self.assertTrue(q.is_infinite() and r.is_nan()) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - q, r = divmod(Decimal(0), 0) - self.assertTrue(q.is_nan() and r.is_nan()) - self.assertTrue(c.flags[InvalidOperation]) - - c.traps[DivisionByZero] = False - c.clear_flags() - q, r = divmod(Decimal(11), 0) - self.assertTrue(q.is_infinite() and r.is_nan()) - self.assertTrue(c.flags[InvalidOperation] and - c.flags[DivisionByZero]) - - def test_power(self): - Decimal = self.decimal.Decimal - localcontext = self.decimal.localcontext - Overflow = self.decimal.Overflow - Rounded = self.decimal.Rounded - - with localcontext() as c: - c.prec = 3 - c.clear_flags() - self.assertEqual(Decimal("1.0") ** 100, Decimal('1.00')) - self.assertTrue(c.flags[Rounded]) - - c.prec = 1 - c.Emax = 1 - c.Emin = -1 - c.clear_flags() - c.traps[Overflow] = False - self.assertEqual(Decimal(10000) ** Decimal("0.5"), Decimal('inf')) - self.assertTrue(c.flags[Overflow]) - - def test_quantize(self): - Decimal = self.decimal.Decimal - localcontext = self.decimal.localcontext - InvalidOperation = self.decimal.InvalidOperation - - with localcontext() as c: - c.prec = 1 - c.Emax = 1 - c.Emin = -1 - c.traps[InvalidOperation] = False - x = Decimal(99).quantize(Decimal("1e1")) - self.assertTrue(x.is_nan()) - - def test_radix(self): - Decimal = self.decimal.Decimal - getcontext = self.decimal.getcontext - - c = getcontext() - self.assertEqual(Decimal("1").radix(), 10) - self.assertEqual(c.radix(), 10) - - def test_rop(self): - Decimal = self.decimal.Decimal - - for attr in ('__radd__', '__rsub__', '__rmul__', '__rtruediv__', - '__rdivmod__', '__rmod__', '__rfloordiv__', '__rpow__'): - self.assertIs(getattr(Decimal("1"), attr)("xyz"), NotImplemented) - - def test_round(self): - # Python3 behavior: round() returns Decimal - Decimal = self.decimal.Decimal - localcontext = self.decimal.localcontext - - with localcontext() as c: - c.prec = 28 - - self.assertEqual(str(Decimal("9.99").__round__()), "10") - self.assertEqual(str(Decimal("9.99e-5").__round__()), "0") - self.assertEqual(str(Decimal("1.23456789").__round__(5)), "1.23457") - self.assertEqual(str(Decimal("1.2345").__round__(10)), "1.2345000000") - self.assertEqual(str(Decimal("1.2345").__round__(-10)), "0E+10") - - self.assertRaises(TypeError, Decimal("1.23").__round__, "5") - self.assertRaises(TypeError, Decimal("1.23").__round__, 5, 8) - - def test_create_decimal(self): - c = self.decimal.Context() - self.assertRaises(ValueError, c.create_decimal, ["%"]) - - def test_int(self): - Decimal = self.decimal.Decimal - localcontext = self.decimal.localcontext - - with localcontext() as c: - c.prec = 9999 - x = Decimal(1221**1271) / 10**3923 - self.assertEqual(int(x), 1) - self.assertEqual(x.to_integral(), 2) - - def test_copy(self): - Context = self.decimal.Context - - c = Context() - c.prec = 10000 - x = -(1172 ** 1712) - - y = c.copy_abs(x) - self.assertEqual(y, -x) - - y = c.copy_negate(x) - self.assertEqual(y, -x) - - y = c.copy_sign(x, 1) - self.assertEqual(y, -x) - -@requires_cdecimal -class CCoverage(Coverage, unittest.TestCase): - decimal = C -class PyCoverage(Coverage, unittest.TestCase): - decimal = P - - def setUp(self): - super().setUp() - self._previous_int_limit = sys.get_int_max_str_digits() - sys.set_int_max_str_digits(7000) - - def tearDown(self): - sys.set_int_max_str_digits(self._previous_int_limit) - super().tearDown() - -class PyFunctionality(unittest.TestCase): - """Extra functionality in decimal.py""" - - def test_py_alternate_formatting(self): - # triples giving a format, a Decimal, and the expected result - Decimal = P.Decimal - localcontext = P.localcontext - - test_values = [ - # Issue 7094: Alternate formatting (specified by #) - ('.0e', '1.0', '1e+0'), - ('#.0e', '1.0', '1.e+0'), - ('.0f', '1.0', '1'), - ('#.0f', '1.0', '1.'), - ('g', '1.1', '1.1'), - ('#g', '1.1', '1.1'), - ('.0g', '1', '1'), - ('#.0g', '1', '1.'), - ('.0%', '1.0', '100%'), - ('#.0%', '1.0', '100.%'), - ] - for fmt, d, result in test_values: - self.assertEqual(format(Decimal(d), fmt), result) - -class PyWhitebox(unittest.TestCase): - """White box testing for decimal.py""" - - def test_py_exact_power(self): - # Rarely exercised lines in _power_exact. - Decimal = P.Decimal - localcontext = P.localcontext - - with localcontext() as c: - c.prec = 8 - x = Decimal(2**16) ** Decimal("-0.5") - self.assertEqual(x, Decimal('0.00390625')) - - x = Decimal(2**16) ** Decimal("-0.6") - self.assertEqual(x, Decimal('0.0012885819')) - - x = Decimal("256e7") ** Decimal("-0.5") - - x = Decimal(152587890625) ** Decimal('-0.0625') - self.assertEqual(x, Decimal("0.2")) - - x = Decimal("152587890625e7") ** Decimal('-0.0625') - - x = Decimal(5**2659) ** Decimal('-0.0625') - - c.prec = 1 - x = Decimal("152587890625") ** Decimal('-0.5') - self.assertEqual(x, Decimal('3e-6')) - c.prec = 2 - x = Decimal("152587890625") ** Decimal('-0.5') - self.assertEqual(x, Decimal('2.6e-6')) - c.prec = 3 - x = Decimal("152587890625") ** Decimal('-0.5') - self.assertEqual(x, Decimal('2.56e-6')) - c.prec = 28 - x = Decimal("152587890625") ** Decimal('-0.5') - self.assertEqual(x, Decimal('2.56e-6')) - - c.prec = 201 - x = Decimal(2**578) ** Decimal("-0.5") - - # See https://github.com/python/cpython/issues/118027 - # Testing for an exact power could appear to hang, in the Python - # version, as it attempted to compute 10**(MAX_EMAX + 1). - # Fixed via https://github.com/python/cpython/pull/118503. - c.prec = P.MAX_PREC - c.Emax = P.MAX_EMAX - c.Emin = P.MIN_EMIN - c.traps[P.Inexact] = 1 - D2 = Decimal(2) - # If the bug is still present, the next statement won't complete. - res = D2 ** 117 - self.assertEqual(res, 1 << 117) - - def test_py_immutability_operations(self): - # Do operations and check that it didn't change internal objects. - Decimal = P.Decimal - DefaultContext = P.DefaultContext - setcontext = P.setcontext - - c = DefaultContext.copy() - c.traps = dict((s, 0) for s in OrderedSignals[P]) - setcontext(c) - - d1 = Decimal('-25e55') - b1 = Decimal('-25e55') - d2 = Decimal('33e+33') - b2 = Decimal('33e+33') - - def checkSameDec(operation, useOther=False): - if useOther: - eval("d1." + operation + "(d2)") - self.assertEqual(d1._sign, b1._sign) - self.assertEqual(d1._int, b1._int) - self.assertEqual(d1._exp, b1._exp) - self.assertEqual(d2._sign, b2._sign) - self.assertEqual(d2._int, b2._int) - self.assertEqual(d2._exp, b2._exp) - else: - eval("d1." + operation + "()") - self.assertEqual(d1._sign, b1._sign) - self.assertEqual(d1._int, b1._int) - self.assertEqual(d1._exp, b1._exp) - - Decimal(d1) - self.assertEqual(d1._sign, b1._sign) - self.assertEqual(d1._int, b1._int) - self.assertEqual(d1._exp, b1._exp) - - checkSameDec("__abs__") - checkSameDec("__add__", True) - checkSameDec("__divmod__", True) - checkSameDec("__eq__", True) - checkSameDec("__ne__", True) - checkSameDec("__le__", True) - checkSameDec("__lt__", True) - checkSameDec("__ge__", True) - checkSameDec("__gt__", True) - checkSameDec("__float__") - checkSameDec("__floordiv__", True) - checkSameDec("__hash__") - checkSameDec("__int__") - checkSameDec("__trunc__") - checkSameDec("__mod__", True) - checkSameDec("__mul__", True) - checkSameDec("__neg__") - checkSameDec("__bool__") - checkSameDec("__pos__") - checkSameDec("__pow__", True) - checkSameDec("__radd__", True) - checkSameDec("__rdivmod__", True) - checkSameDec("__repr__") - checkSameDec("__rfloordiv__", True) - checkSameDec("__rmod__", True) - checkSameDec("__rmul__", True) - checkSameDec("__rpow__", True) - checkSameDec("__rsub__", True) - checkSameDec("__str__") - checkSameDec("__sub__", True) - checkSameDec("__truediv__", True) - checkSameDec("adjusted") - checkSameDec("as_tuple") - checkSameDec("compare", True) - checkSameDec("max", True) - checkSameDec("min", True) - checkSameDec("normalize") - checkSameDec("quantize", True) - checkSameDec("remainder_near", True) - checkSameDec("same_quantum", True) - checkSameDec("sqrt") - checkSameDec("to_eng_string") - checkSameDec("to_integral") - - def test_py_decimal_id(self): - Decimal = P.Decimal - - d = Decimal(45) - e = Decimal(d) - self.assertEqual(str(e), '45') - self.assertNotEqual(id(d), id(e)) - - def test_py_rescale(self): - # Coverage - Decimal = P.Decimal - localcontext = P.localcontext - - with localcontext() as c: - x = Decimal("NaN")._rescale(3, ROUND_UP) - self.assertTrue(x.is_nan()) - - def test_py__round(self): - # Coverage - Decimal = P.Decimal - - self.assertRaises(ValueError, Decimal("3.1234")._round, 0, ROUND_UP) - -class CFunctionality(unittest.TestCase): - """Extra functionality in _decimal""" - - @requires_extra_functionality - def test_c_context(self): - Context = C.Context - - c = Context(flags=C.DecClamped, traps=C.DecRounded) - self.assertEqual(c._flags, C.DecClamped) - self.assertEqual(c._traps, C.DecRounded) - - @requires_extra_functionality - def test_constants(self): - # Condition flags - cond = ( - C.DecClamped, C.DecConversionSyntax, C.DecDivisionByZero, - C.DecDivisionImpossible, C.DecDivisionUndefined, - C.DecFpuError, C.DecInexact, C.DecInvalidContext, - C.DecInvalidOperation, C.DecMallocError, - C.DecFloatOperation, C.DecOverflow, C.DecRounded, - C.DecSubnormal, C.DecUnderflow - ) - - # Conditions - for i, v in enumerate(cond): - self.assertEqual(v, 1< 425000000) - - c = Context() - - # SignalDict: input validation - self.assertRaises(KeyError, c.flags.__setitem__, 801, 0) - self.assertRaises(KeyError, c.traps.__setitem__, 801, 0) - self.assertRaises(ValueError, c.flags.__delitem__, Overflow) - self.assertRaises(ValueError, c.traps.__delitem__, InvalidOperation) - self.assertRaises(TypeError, setattr, c, 'flags', ['x']) - self.assertRaises(TypeError, setattr, c,'traps', ['y']) - self.assertRaises(KeyError, setattr, c, 'flags', {0:1}) - self.assertRaises(KeyError, setattr, c, 'traps', {0:1}) - - # Test assignment from a signal dict with the correct length but - # one invalid key. - d = c.flags.copy() - del d[FloatOperation] - d["XYZ"] = 91283719 - self.assertRaises(KeyError, setattr, c, 'flags', d) - self.assertRaises(KeyError, setattr, c, 'traps', d) - - # Input corner cases - int_max = 2**63-1 if HAVE_CONFIG_64 else 2**31-1 - gt_max_emax = 10**18 if HAVE_CONFIG_64 else 10**9 - - # prec, Emax, Emin - for attr in ['prec', 'Emax']: - self.assertRaises(ValueError, setattr, c, attr, gt_max_emax) - self.assertRaises(ValueError, setattr, c, 'Emin', -gt_max_emax) - - # prec, Emax, Emin in context constructor - self.assertRaises(ValueError, Context, prec=gt_max_emax) - self.assertRaises(ValueError, Context, Emax=gt_max_emax) - self.assertRaises(ValueError, Context, Emin=-gt_max_emax) - - # Overflow in conversion - self.assertRaises(OverflowError, Context, prec=int_max+1) - self.assertRaises(OverflowError, Context, Emax=int_max+1) - self.assertRaises(OverflowError, Context, Emin=-int_max-2) - self.assertRaises(OverflowError, Context, clamp=int_max+1) - self.assertRaises(OverflowError, Context, capitals=int_max+1) - - # OverflowError, general ValueError - for attr in ('prec', 'Emin', 'Emax', 'capitals', 'clamp'): - self.assertRaises(OverflowError, setattr, c, attr, int_max+1) - self.assertRaises(OverflowError, setattr, c, attr, -int_max-2) - if sys.platform != 'win32': - self.assertRaises(ValueError, setattr, c, attr, int_max) - self.assertRaises(ValueError, setattr, c, attr, -int_max-1) - - # OverflowError: _unsafe_setprec, _unsafe_setemin, _unsafe_setemax - if C.MAX_PREC == 425000000: - self.assertRaises(OverflowError, getattr(c, '_unsafe_setprec'), - int_max+1) - self.assertRaises(OverflowError, getattr(c, '_unsafe_setemax'), - int_max+1) - self.assertRaises(OverflowError, getattr(c, '_unsafe_setemin'), - -int_max-2) - - # ValueError: _unsafe_setprec, _unsafe_setemin, _unsafe_setemax - if C.MAX_PREC == 425000000: - self.assertRaises(ValueError, getattr(c, '_unsafe_setprec'), 0) - self.assertRaises(ValueError, getattr(c, '_unsafe_setprec'), - 1070000001) - self.assertRaises(ValueError, getattr(c, '_unsafe_setemax'), -1) - self.assertRaises(ValueError, getattr(c, '_unsafe_setemax'), - 1070000001) - self.assertRaises(ValueError, getattr(c, '_unsafe_setemin'), - -1070000001) - self.assertRaises(ValueError, getattr(c, '_unsafe_setemin'), 1) - - # capitals, clamp - for attr in ['capitals', 'clamp']: - self.assertRaises(ValueError, setattr, c, attr, -1) - self.assertRaises(ValueError, setattr, c, attr, 2) - self.assertRaises(TypeError, setattr, c, attr, [1,2,3]) - if HAVE_CONFIG_64: - self.assertRaises(ValueError, setattr, c, attr, 2**32) - self.assertRaises(ValueError, setattr, c, attr, 2**32+1) - - # Invalid local context - self.assertRaises(TypeError, exec, 'with localcontext("xyz"): pass', - locals()) - self.assertRaises(TypeError, exec, - 'with localcontext(context=getcontext()): pass', - locals()) - - # setcontext - saved_context = getcontext() - self.assertRaises(TypeError, setcontext, "xyz") - setcontext(saved_context) - - def test_rounding_strings_interned(self): - - self.assertIs(C.ROUND_UP, P.ROUND_UP) - self.assertIs(C.ROUND_DOWN, P.ROUND_DOWN) - self.assertIs(C.ROUND_CEILING, P.ROUND_CEILING) - self.assertIs(C.ROUND_FLOOR, P.ROUND_FLOOR) - self.assertIs(C.ROUND_HALF_UP, P.ROUND_HALF_UP) - self.assertIs(C.ROUND_HALF_DOWN, P.ROUND_HALF_DOWN) - self.assertIs(C.ROUND_HALF_EVEN, P.ROUND_HALF_EVEN) - self.assertIs(C.ROUND_05UP, P.ROUND_05UP) - - @requires_extra_functionality - def test_c_context_errors_extra(self): - Context = C.Context - InvalidOperation = C.InvalidOperation - Overflow = C.Overflow - localcontext = C.localcontext - getcontext = C.getcontext - setcontext = C.setcontext - HAVE_CONFIG_64 = (C.MAX_PREC > 425000000) - - c = Context() - - # Input corner cases - int_max = 2**63-1 if HAVE_CONFIG_64 else 2**31-1 - - # OverflowError, general ValueError - self.assertRaises(OverflowError, setattr, c, '_allcr', int_max+1) - self.assertRaises(OverflowError, setattr, c, '_allcr', -int_max-2) - if sys.platform != 'win32': - self.assertRaises(ValueError, setattr, c, '_allcr', int_max) - self.assertRaises(ValueError, setattr, c, '_allcr', -int_max-1) - - # OverflowError, general TypeError - for attr in ('_flags', '_traps'): - self.assertRaises(OverflowError, setattr, c, attr, int_max+1) - self.assertRaises(OverflowError, setattr, c, attr, -int_max-2) - if sys.platform != 'win32': - self.assertRaises(TypeError, setattr, c, attr, int_max) - self.assertRaises(TypeError, setattr, c, attr, -int_max-1) - - # _allcr - self.assertRaises(ValueError, setattr, c, '_allcr', -1) - self.assertRaises(ValueError, setattr, c, '_allcr', 2) - self.assertRaises(TypeError, setattr, c, '_allcr', [1,2,3]) - if HAVE_CONFIG_64: - self.assertRaises(ValueError, setattr, c, '_allcr', 2**32) - self.assertRaises(ValueError, setattr, c, '_allcr', 2**32+1) - - # _flags, _traps - for attr in ['_flags', '_traps']: - self.assertRaises(TypeError, setattr, c, attr, 999999) - self.assertRaises(TypeError, setattr, c, attr, 'x') - - def test_c_valid_context(self): - # These tests are for code coverage in _decimal. - DefaultContext = C.DefaultContext - Clamped = C.Clamped - Underflow = C.Underflow - Inexact = C.Inexact - Rounded = C.Rounded - Subnormal = C.Subnormal - - c = DefaultContext.copy() - - # Exercise all getters and setters - c.prec = 34 - c.rounding = ROUND_HALF_UP - c.Emax = 3000 - c.Emin = -3000 - c.capitals = 1 - c.clamp = 0 - - self.assertEqual(c.prec, 34) - self.assertEqual(c.rounding, ROUND_HALF_UP) - self.assertEqual(c.Emin, -3000) - self.assertEqual(c.Emax, 3000) - self.assertEqual(c.capitals, 1) - self.assertEqual(c.clamp, 0) - - self.assertEqual(c.Etiny(), -3033) - self.assertEqual(c.Etop(), 2967) - - # Exercise all unsafe setters - if C.MAX_PREC == 425000000: - c._unsafe_setprec(999999999) - c._unsafe_setemax(999999999) - c._unsafe_setemin(-999999999) - self.assertEqual(c.prec, 999999999) - self.assertEqual(c.Emax, 999999999) - self.assertEqual(c.Emin, -999999999) - - @requires_extra_functionality - def test_c_valid_context_extra(self): - DefaultContext = C.DefaultContext - - c = DefaultContext.copy() - self.assertEqual(c._allcr, 1) - c._allcr = 0 - self.assertEqual(c._allcr, 0) - - def test_c_round(self): - # Restricted input. - Decimal = C.Decimal - InvalidOperation = C.InvalidOperation - localcontext = C.localcontext - MAX_EMAX = C.MAX_EMAX - MIN_ETINY = C.MIN_ETINY - int_max = 2**63-1 if C.MAX_PREC > 425000000 else 2**31-1 - - with localcontext() as c: - c.traps[InvalidOperation] = True - self.assertRaises(InvalidOperation, Decimal("1.23").__round__, - -int_max-1) - self.assertRaises(InvalidOperation, Decimal("1.23").__round__, - int_max) - self.assertRaises(InvalidOperation, Decimal("1").__round__, - int(MAX_EMAX+1)) - self.assertRaises(C.InvalidOperation, Decimal("1").__round__, - -int(MIN_ETINY-1)) - self.assertRaises(OverflowError, Decimal("1.23").__round__, - -int_max-2) - self.assertRaises(OverflowError, Decimal("1.23").__round__, - int_max+1) - - def test_c_format(self): - # Restricted input - Decimal = C.Decimal - HAVE_CONFIG_64 = (C.MAX_PREC > 425000000) - - self.assertRaises(TypeError, Decimal(1).__format__, "=10.10", [], 9) - self.assertRaises(TypeError, Decimal(1).__format__, "=10.10", 9) - self.assertRaises(TypeError, Decimal(1).__format__, []) - - self.assertRaises(ValueError, Decimal(1).__format__, "<>=10.10") - maxsize = 2**63-1 if HAVE_CONFIG_64 else 2**31-1 - self.assertRaises(ValueError, Decimal("1.23456789").__format__, - "=%d.1" % maxsize) - - def test_c_integral(self): - Decimal = C.Decimal - Inexact = C.Inexact - localcontext = C.localcontext - - x = Decimal(10) - self.assertEqual(x.to_integral(), 10) - self.assertRaises(TypeError, x.to_integral, '10') - self.assertRaises(TypeError, x.to_integral, 10, 'x') - self.assertRaises(TypeError, x.to_integral, 10) - - self.assertEqual(x.to_integral_value(), 10) - self.assertRaises(TypeError, x.to_integral_value, '10') - self.assertRaises(TypeError, x.to_integral_value, 10, 'x') - self.assertRaises(TypeError, x.to_integral_value, 10) - - self.assertEqual(x.to_integral_exact(), 10) - self.assertRaises(TypeError, x.to_integral_exact, '10') - self.assertRaises(TypeError, x.to_integral_exact, 10, 'x') - self.assertRaises(TypeError, x.to_integral_exact, 10) - - with localcontext() as c: - x = Decimal("99999999999999999999999999.9").to_integral_value(ROUND_UP) - self.assertEqual(x, Decimal('100000000000000000000000000')) - - x = Decimal("99999999999999999999999999.9").to_integral_exact(ROUND_UP) - self.assertEqual(x, Decimal('100000000000000000000000000')) - - c.traps[Inexact] = True - self.assertRaises(Inexact, Decimal("999.9").to_integral_exact, ROUND_UP) - - def test_c_funcs(self): - # Invalid arguments - Decimal = C.Decimal - InvalidOperation = C.InvalidOperation - DivisionByZero = C.DivisionByZero - getcontext = C.getcontext - localcontext = C.localcontext - - self.assertEqual(Decimal('9.99e10').to_eng_string(), '99.9E+9') - - self.assertRaises(TypeError, pow, Decimal(1), 2, "3") - self.assertRaises(TypeError, Decimal(9).number_class, "x", "y") - self.assertRaises(TypeError, Decimal(9).same_quantum, 3, "x", "y") - - self.assertRaises( - TypeError, - Decimal("1.23456789").quantize, Decimal('1e-100000'), [] - ) - self.assertRaises( - TypeError, - Decimal("1.23456789").quantize, Decimal('1e-100000'), getcontext() - ) - self.assertRaises( - TypeError, - Decimal("1.23456789").quantize, Decimal('1e-100000'), 10 - ) - self.assertRaises( - TypeError, - Decimal("1.23456789").quantize, Decimal('1e-100000'), ROUND_UP, 1000 - ) - - with localcontext() as c: - c.clear_traps() - - # Invalid arguments - self.assertRaises(TypeError, c.copy_sign, Decimal(1), "x", "y") - self.assertRaises(TypeError, c.canonical, 200) - self.assertRaises(TypeError, c.is_canonical, 200) - self.assertRaises(TypeError, c.divmod, 9, 8, "x", "y") - self.assertRaises(TypeError, c.same_quantum, 9, 3, "x", "y") - - self.assertEqual(str(c.canonical(Decimal(200))), '200') - self.assertEqual(c.radix(), 10) - - c.traps[DivisionByZero] = True - self.assertRaises(DivisionByZero, Decimal(9).__divmod__, 0) - self.assertRaises(DivisionByZero, c.divmod, 9, 0) - self.assertTrue(c.flags[InvalidOperation]) - - c.clear_flags() - c.traps[InvalidOperation] = True - self.assertRaises(InvalidOperation, Decimal(9).__divmod__, 0) - self.assertRaises(InvalidOperation, c.divmod, 9, 0) - self.assertTrue(c.flags[DivisionByZero]) - - c.traps[InvalidOperation] = True - c.prec = 2 - self.assertRaises(InvalidOperation, pow, Decimal(1000), 1, 501) - - def test_va_args_exceptions(self): - Decimal = C.Decimal - Context = C.Context - - x = Decimal("10001111111") - - for attr in ['exp', 'is_normal', 'is_subnormal', 'ln', 'log10', - 'logb', 'logical_invert', 'next_minus', 'next_plus', - 'normalize', 'number_class', 'sqrt', 'to_eng_string']: - func = getattr(x, attr) - self.assertRaises(TypeError, func, context="x") - self.assertRaises(TypeError, func, "x", context=None) - - for attr in ['compare', 'compare_signal', 'logical_and', - 'logical_or', 'max', 'max_mag', 'min', 'min_mag', - 'remainder_near', 'rotate', 'scaleb', 'shift']: - func = getattr(x, attr) - self.assertRaises(TypeError, func, context="x") - self.assertRaises(TypeError, func, "x", context=None) - - self.assertRaises(TypeError, x.to_integral, rounding=None, context=[]) - self.assertRaises(TypeError, x.to_integral, rounding={}, context=[]) - self.assertRaises(TypeError, x.to_integral, [], []) - - self.assertRaises(TypeError, x.to_integral_value, rounding=None, context=[]) - self.assertRaises(TypeError, x.to_integral_value, rounding={}, context=[]) - self.assertRaises(TypeError, x.to_integral_value, [], []) - - self.assertRaises(TypeError, x.to_integral_exact, rounding=None, context=[]) - self.assertRaises(TypeError, x.to_integral_exact, rounding={}, context=[]) - self.assertRaises(TypeError, x.to_integral_exact, [], []) - - self.assertRaises(TypeError, x.fma, 1, 2, context="x") - self.assertRaises(TypeError, x.fma, 1, 2, "x", context=None) - - self.assertRaises(TypeError, x.quantize, 1, [], context=None) - self.assertRaises(TypeError, x.quantize, 1, [], rounding=None) - self.assertRaises(TypeError, x.quantize, 1, [], []) - - c = Context() - self.assertRaises(TypeError, c.power, 1, 2, mod="x") - self.assertRaises(TypeError, c.power, 1, "x", mod=None) - self.assertRaises(TypeError, c.power, "x", 2, mod=None) - - @requires_extra_functionality - def test_c_context_templates(self): - self.assertEqual( - C.BasicContext._traps, - C.DecIEEEInvalidOperation|C.DecDivisionByZero|C.DecOverflow| - C.DecUnderflow|C.DecClamped - ) - self.assertEqual( - C.DefaultContext._traps, - C.DecIEEEInvalidOperation|C.DecDivisionByZero|C.DecOverflow - ) - - @requires_extra_functionality - def test_c_signal_dict(self): - - # SignalDict coverage - Context = C.Context - DefaultContext = C.DefaultContext - - InvalidOperation = C.InvalidOperation - FloatOperation = C.FloatOperation - DivisionByZero = C.DivisionByZero - Overflow = C.Overflow - Subnormal = C.Subnormal - Underflow = C.Underflow - Rounded = C.Rounded - Inexact = C.Inexact - Clamped = C.Clamped - - DecClamped = C.DecClamped - DecInvalidOperation = C.DecInvalidOperation - DecIEEEInvalidOperation = C.DecIEEEInvalidOperation - - def assertIsExclusivelySet(signal, signal_dict): - for sig in signal_dict: - if sig == signal: - self.assertTrue(signal_dict[sig]) - else: - self.assertFalse(signal_dict[sig]) - - c = DefaultContext.copy() - - # Signal dict methods - self.assertTrue(Overflow in c.traps) - c.clear_traps() - for k in c.traps.keys(): - c.traps[k] = True - for v in c.traps.values(): - self.assertTrue(v) - c.clear_traps() - for k, v in c.traps.items(): - self.assertFalse(v) - - self.assertFalse(c.flags.get(Overflow)) - self.assertIs(c.flags.get("x"), None) - self.assertEqual(c.flags.get("x", "y"), "y") - self.assertRaises(TypeError, c.flags.get, "x", "y", "z") - - self.assertEqual(len(c.flags), len(c.traps)) - s = sys.getsizeof(c.flags) - s = sys.getsizeof(c.traps) - s = c.flags.__repr__() - - # Set flags/traps. - c.clear_flags() - c._flags = DecClamped - self.assertTrue(c.flags[Clamped]) - - c.clear_traps() - c._traps = DecInvalidOperation - self.assertTrue(c.traps[InvalidOperation]) - - # Set flags/traps from dictionary. - c.clear_flags() - d = c.flags.copy() - d[DivisionByZero] = True - c.flags = d - assertIsExclusivelySet(DivisionByZero, c.flags) - - c.clear_traps() - d = c.traps.copy() - d[Underflow] = True - c.traps = d - assertIsExclusivelySet(Underflow, c.traps) - - # Random constructors - IntSignals = { - Clamped: C.DecClamped, - Rounded: C.DecRounded, - Inexact: C.DecInexact, - Subnormal: C.DecSubnormal, - Underflow: C.DecUnderflow, - Overflow: C.DecOverflow, - DivisionByZero: C.DecDivisionByZero, - FloatOperation: C.DecFloatOperation, - InvalidOperation: C.DecIEEEInvalidOperation - } - IntCond = [ - C.DecDivisionImpossible, C.DecDivisionUndefined, C.DecFpuError, - C.DecInvalidContext, C.DecInvalidOperation, C.DecMallocError, - C.DecConversionSyntax, - ] - - lim = len(OrderedSignals[C]) - for r in range(lim): - for t in range(lim): - for round in RoundingModes: - flags = random.sample(OrderedSignals[C], r) - traps = random.sample(OrderedSignals[C], t) - prec = random.randrange(1, 10000) - emin = random.randrange(-10000, 0) - emax = random.randrange(0, 10000) - clamp = random.randrange(0, 2) - caps = random.randrange(0, 2) - cr = random.randrange(0, 2) - c = Context(prec=prec, rounding=round, Emin=emin, Emax=emax, - capitals=caps, clamp=clamp, flags=list(flags), - traps=list(traps)) - - self.assertEqual(c.prec, prec) - self.assertEqual(c.rounding, round) - self.assertEqual(c.Emin, emin) - self.assertEqual(c.Emax, emax) - self.assertEqual(c.capitals, caps) - self.assertEqual(c.clamp, clamp) - - f = 0 - for x in flags: - f |= IntSignals[x] - self.assertEqual(c._flags, f) - - f = 0 - for x in traps: - f |= IntSignals[x] - self.assertEqual(c._traps, f) - - for cond in IntCond: - c._flags = cond - self.assertTrue(c._flags&DecIEEEInvalidOperation) - assertIsExclusivelySet(InvalidOperation, c.flags) - - for cond in IntCond: - c._traps = cond - self.assertTrue(c._traps&DecIEEEInvalidOperation) - assertIsExclusivelySet(InvalidOperation, c.traps) - - def test_invalid_override(self): - Decimal = C.Decimal - - try: - from locale import CHAR_MAX - except ImportError: - self.skipTest('locale.CHAR_MAX not available') - - def make_grouping(lst): - return ''.join([chr(x) for x in lst]) - - def get_fmt(x, override=None, fmt='n'): - return Decimal(x).__format__(fmt, override) - - invalid_grouping = { - 'decimal_point' : ',', - 'grouping' : make_grouping([255, 255, 0]), - 'thousands_sep' : ',' - } - invalid_dot = { - 'decimal_point' : 'xxxxx', - 'grouping' : make_grouping([3, 3, 0]), - 'thousands_sep' : ',' - } - invalid_sep = { - 'decimal_point' : '.', - 'grouping' : make_grouping([3, 3, 0]), - 'thousands_sep' : 'yyyyy' - } - - if CHAR_MAX == 127: # negative grouping in override - self.assertRaises(ValueError, get_fmt, 12345, - invalid_grouping, 'g') - - self.assertRaises(ValueError, get_fmt, 12345, invalid_dot, 'g') - self.assertRaises(ValueError, get_fmt, 12345, invalid_sep, 'g') - - def test_exact_conversion(self): - Decimal = C.Decimal - localcontext = C.localcontext - InvalidOperation = C.InvalidOperation - - with localcontext() as c: - - c.traps[InvalidOperation] = True - - # Clamped - x = "0e%d" % sys.maxsize - self.assertRaises(InvalidOperation, Decimal, x) - - x = "0e%d" % (-sys.maxsize-1) - self.assertRaises(InvalidOperation, Decimal, x) - - # Overflow - x = "1e%d" % sys.maxsize - self.assertRaises(InvalidOperation, Decimal, x) - - # Underflow - x = "1e%d" % (-sys.maxsize-1) - self.assertRaises(InvalidOperation, Decimal, x) - - def test_from_tuple(self): - Decimal = C.Decimal - localcontext = C.localcontext - InvalidOperation = C.InvalidOperation - Overflow = C.Overflow - Underflow = C.Underflow - - with localcontext() as c: - - c.prec = 9 - c.traps[InvalidOperation] = True - c.traps[Overflow] = True - c.traps[Underflow] = True - - # SSIZE_MAX - x = (1, (), sys.maxsize) - self.assertEqual(str(c.create_decimal(x)), '-0E+999999') - self.assertRaises(InvalidOperation, Decimal, x) - - x = (1, (0, 1, 2), sys.maxsize) - self.assertRaises(Overflow, c.create_decimal, x) - self.assertRaises(InvalidOperation, Decimal, x) - - # SSIZE_MIN - x = (1, (), -sys.maxsize-1) - self.assertEqual(str(c.create_decimal(x)), '-0E-1000007') - self.assertRaises(InvalidOperation, Decimal, x) - - x = (1, (0, 1, 2), -sys.maxsize-1) - self.assertRaises(Underflow, c.create_decimal, x) - self.assertRaises(InvalidOperation, Decimal, x) - - # OverflowError - x = (1, (), sys.maxsize+1) - self.assertRaises(OverflowError, c.create_decimal, x) - self.assertRaises(OverflowError, Decimal, x) - - x = (1, (), -sys.maxsize-2) - self.assertRaises(OverflowError, c.create_decimal, x) - self.assertRaises(OverflowError, Decimal, x) - - # Specials - x = (1, (), "N") - self.assertEqual(str(Decimal(x)), '-sNaN') - x = (1, (0,), "N") - self.assertEqual(str(Decimal(x)), '-sNaN') - x = (1, (0, 1), "N") - self.assertEqual(str(Decimal(x)), '-sNaN1') - - def test_sizeof(self): - Decimal = C.Decimal - HAVE_CONFIG_64 = (C.MAX_PREC > 425000000) - - self.assertGreater(Decimal(0).__sizeof__(), 0) - if HAVE_CONFIG_64: - x = Decimal(10**(19*24)).__sizeof__() - y = Decimal(10**(19*25)).__sizeof__() - self.assertEqual(y, x+8) - else: - x = Decimal(10**(9*24)).__sizeof__() - y = Decimal(10**(9*25)).__sizeof__() - self.assertEqual(y, x+4) - - def test_internal_use_of_overridden_methods(self): - Decimal = C.Decimal - - # Unsound subtyping - class X(float): - def as_integer_ratio(self): - return 1 - def __abs__(self): - return self - - class Y(float): - def __abs__(self): - return [1]*200 - - class I(int): - def bit_length(self): - return [1]*200 - - class Z(float): - def as_integer_ratio(self): - return (I(1), I(1)) - def __abs__(self): - return self - - for cls in X, Y, Z: - self.assertEqual(Decimal.from_float(cls(101.1)), - Decimal.from_float(101.1)) - - def test_c_immutable_types(self): - SignalDict = type(C.Context().flags) - SignalDictMixin = SignalDict.__bases__[0] - ContextManager = type(C.localcontext()) - types = ( - SignalDictMixin, - ContextManager, - C.Decimal, - C.Context, - ) - for tp in types: - with self.subTest(tp=tp): - with self.assertRaisesRegex(TypeError, "immutable"): - tp.foo = 1 - - def test_c_disallow_instantiation(self): - ContextManager = type(C.localcontext()) - check_disallow_instantiation(self, ContextManager) - - def test_c_signaldict_segfault(self): - # See gh-106263 for details. - SignalDict = type(C.Context().flags) - sd = SignalDict() - err_msg = "invalid signal dict" - - with self.assertRaisesRegex(ValueError, err_msg): - len(sd) - - with self.assertRaisesRegex(ValueError, err_msg): - iter(sd) - - with self.assertRaisesRegex(ValueError, err_msg): - repr(sd) - - with self.assertRaisesRegex(ValueError, err_msg): - sd[C.InvalidOperation] = True - - with self.assertRaisesRegex(ValueError, err_msg): - sd[C.InvalidOperation] - - with self.assertRaisesRegex(ValueError, err_msg): - sd == C.Context().flags - - with self.assertRaisesRegex(ValueError, err_msg): - C.Context().flags == sd - - with self.assertRaisesRegex(ValueError, err_msg): - sd.copy() - - def test_format_fallback_capitals(self): - # Fallback to _pydecimal formatting (triggered by `#` format which - # is unsupported by mpdecimal) should honor the current context. - x = C.Decimal('6.09e+23') - self.assertEqual(format(x, '#'), '6.09E+23') - with C.localcontext(capitals=0): - self.assertEqual(format(x, '#'), '6.09e+23') - - def test_format_fallback_rounding(self): - y = C.Decimal('6.09') - self.assertEqual(format(y, '#.1f'), '6.1') - with C.localcontext(rounding=C.ROUND_DOWN): - self.assertEqual(format(y, '#.1f'), '6.0') - -@requires_docstrings -@requires_cdecimal -class SignatureTest(unittest.TestCase): - """Function signatures""" - - def test_inspect_module(self): - for attr in dir(P): - if attr.startswith('_'): - continue - p_func = getattr(P, attr) - c_func = getattr(C, attr) - if (attr == 'Decimal' or attr == 'Context' or - inspect.isfunction(p_func)): - p_sig = inspect.signature(p_func) - c_sig = inspect.signature(c_func) - - # parameter names: - c_names = list(c_sig.parameters.keys()) - p_names = [x for x in p_sig.parameters.keys() if not - x.startswith('_')] - - self.assertEqual(c_names, p_names, - msg="parameter name mismatch in %s" % p_func) - - c_kind = [x.kind for x in c_sig.parameters.values()] - p_kind = [x[1].kind for x in p_sig.parameters.items() if not - x[0].startswith('_')] - - # parameters: - if attr != 'setcontext': - self.assertEqual(c_kind, p_kind, - msg="parameter kind mismatch in %s" % p_func) - - def test_inspect_types(self): - - POS = inspect._ParameterKind.POSITIONAL_ONLY - POS_KWD = inspect._ParameterKind.POSITIONAL_OR_KEYWORD - - # Type heuristic (type annotations would help!): - pdict = {C: {'other': C.Decimal(1), - 'third': C.Decimal(1), - 'x': C.Decimal(1), - 'y': C.Decimal(1), - 'z': C.Decimal(1), - 'a': C.Decimal(1), - 'b': C.Decimal(1), - 'c': C.Decimal(1), - 'exp': C.Decimal(1), - 'modulo': C.Decimal(1), - 'num': "1", - 'f': 1.0, - 'rounding': C.ROUND_HALF_UP, - 'context': C.getcontext()}, - P: {'other': P.Decimal(1), - 'third': P.Decimal(1), - 'a': P.Decimal(1), - 'b': P.Decimal(1), - 'c': P.Decimal(1), - 'exp': P.Decimal(1), - 'modulo': P.Decimal(1), - 'num': "1", - 'f': 1.0, - 'rounding': P.ROUND_HALF_UP, - 'context': P.getcontext()}} - - def mkargs(module, sig): - args = [] - kwargs = {} - for name, param in sig.parameters.items(): - if name == 'self': continue - if param.kind == POS: - args.append(pdict[module][name]) - elif param.kind == POS_KWD: - kwargs[name] = pdict[module][name] - else: - raise TestFailed("unexpected parameter kind") - return args, kwargs - - def tr(s): - """The C Context docstrings use 'x' in order to prevent confusion - with the article 'a' in the descriptions.""" - if s == 'x': return 'a' - if s == 'y': return 'b' - if s == 'z': return 'c' - return s - - def doit(ty): - p_type = getattr(P, ty) - c_type = getattr(C, ty) - for attr in dir(p_type): - if attr.startswith('_'): - continue - p_func = getattr(p_type, attr) - c_func = getattr(c_type, attr) - if inspect.isfunction(p_func): - p_sig = inspect.signature(p_func) - c_sig = inspect.signature(c_func) - - # parameter names: - p_names = list(p_sig.parameters.keys()) - c_names = [tr(x) for x in c_sig.parameters.keys()] - - self.assertEqual(c_names, p_names, - msg="parameter name mismatch in %s" % p_func) - - p_kind = [x.kind for x in p_sig.parameters.values()] - c_kind = [x.kind for x in c_sig.parameters.values()] - - # 'self' parameter: - self.assertIs(p_kind[0], POS_KWD) - self.assertIs(c_kind[0], POS) - - # remaining parameters: - if ty == 'Decimal': - self.assertEqual(c_kind[1:], p_kind[1:], - msg="parameter kind mismatch in %s" % p_func) - else: # Context methods are positional only in the C version. - self.assertEqual(len(c_kind), len(p_kind), - msg="parameter kind mismatch in %s" % p_func) - - # Run the function: - args, kwds = mkargs(C, c_sig) - try: - getattr(c_type(9), attr)(*args, **kwds) - except Exception: - raise TestFailed("invalid signature for %s: %s %s" % (c_func, args, kwds)) - - args, kwds = mkargs(P, p_sig) - try: - getattr(p_type(9), attr)(*args, **kwds) - except Exception: - raise TestFailed("invalid signature for %s: %s %s" % (p_func, args, kwds)) - - doit('Decimal') - doit('Context') - - -class TestModule: - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(self.decimal, "__version__") - self.assertEqual(cm.filename, __file__) - - -@requires_cdecimal -class CTestModule(TestModule, unittest.TestCase): - decimal = C -class PyTestModule(TestModule, unittest.TestCase): - decimal = P - - -def load_tests(loader, tests, pattern): - if TODO_TESTS is not None: - # Run only Arithmetic tests - tests = loader.suiteClass() - # Dynamically build custom test definition for each file in the test - # directory and add the definitions to the DecimalTest class. This - # procedure insures that new files do not get skipped. - for filename in os.listdir(directory): - if '.decTest' not in filename or filename.startswith("."): - continue - head, tail = filename.split('.') - if TODO_TESTS is not None and head not in TODO_TESTS: - continue - tester = lambda self, f=filename: self.eval_file(directory + f) - setattr(IBMTestCases, 'test_' + head, tester) - del filename, head, tail, tester - for prefix, mod in ('C', C), ('Py', P): - if not mod: - continue - test_class = type(prefix + 'IBMTestCases', - (IBMTestCases, unittest.TestCase), - {'decimal': mod}) - tests.addTest(loader.loadTestsFromTestCase(test_class)) - - if TODO_TESTS is None: - from doctest import DocTestSuite, IGNORE_EXCEPTION_DETAIL - orig_context = orig_sys_decimal.getcontext().copy() - for mod in C, P: - if not mod: - continue - def setUp(slf, mod=mod): - sys.modules['decimal'] = mod - init(mod) - def tearDown(slf, mod=mod): - sys.modules['decimal'] = orig_sys_decimal - mod.setcontext(ORIGINAL_CONTEXT[mod].copy()) - orig_sys_decimal.setcontext(orig_context.copy()) - optionflags = IGNORE_EXCEPTION_DETAIL if mod is C else 0 - sys.modules['decimal'] = mod - tests.addTest(DocTestSuite(mod, setUp=setUp, tearDown=tearDown, - optionflags=optionflags)) - sys.modules['decimal'] = orig_sys_decimal - return tests - -def setUpModule(): - init(C) - init(P) - global TEST_ALL - TEST_ALL = ARITH if ARITH is not None else is_resource_enabled('decimal') - -def tearDownModule(): - if C: C.setcontext(ORIGINAL_CONTEXT[C].copy()) - P.setcontext(ORIGINAL_CONTEXT[P].copy()) - if not C: - logging.getLogger(__name__).warning( - 'C tests skipped: no module named _decimal.' - ) - if not orig_sys_decimal is sys.modules['decimal']: - raise TestFailed("Internal error: unbalanced number of changes to " - "sys.modules['decimal'].") - - -ARITH = None -TEST_ALL = True -TODO_TESTS = None -DEBUG = False - -def test(arith=None, verbose=None, todo_tests=None, debug=None): - """ Execute the tests. - - Runs all arithmetic tests if arith is True or if the "decimal" resource - is enabled in regrtest.py - """ - - global ARITH, TODO_TESTS, DEBUG - ARITH = arith - TODO_TESTS = todo_tests - DEBUG = debug - unittest.main(__name__, verbosity=2 if verbose else 1, exit=False, argv=[__name__]) - - -if __name__ == '__main__': - import optparse - p = optparse.OptionParser("test_decimal.py [--debug] [{--skip | test1 [test2 [...]]}]") - p.add_option('--debug', '-d', action='store_true', help='shows the test number and context before each test') - p.add_option('--skip', '-s', action='store_true', help='skip over 90% of the arithmetic tests') - (opt, args) = p.parse_args() - - if opt.skip: - test(arith=False, verbose=True) - elif args: - test(arith=True, verbose=True, todo_tests=args, debug=opt.debug) - else: - test(arith=True, verbose=True) diff --git a/Lib/test/test_decimal/__init__.py b/Lib/test/test_decimal/__init__.py new file mode 100644 index 00000000000000..a5fe7800baad09 --- /dev/null +++ b/Lib/test/test_decimal/__init__.py @@ -0,0 +1,147 @@ +# Copyright (c) 2004 Python Software Foundation. +# All rights reserved. + +# Written by Eric Price +# and Facundo Batista +# and Raymond Hettinger +# and Aahz (aahz at pobox.com) +# and Tim Peters + +""" +These are the test cases for the Decimal module. + +There are two groups of tests, Arithmetic and Behaviour. The former test +the Decimal arithmetic using the tests provided by Mike Cowlishaw. The latter +test the pythonic behaviour according to PEP 327. + +Cowlishaw's tests can be downloaded from: + + http://speleotrove.com/decimal/dectest.zip + +This test module can be called from command line with one parameter (Arithmetic +or Behaviour) to test each part, or without parameter to test both parts. If +you're working through IDLE, you can import this test module and call test() +with the corresponding argument. +""" + +import unittest +import logging +import os +import sys +from test.support import (is_resource_enabled, TestFailed, + darwin_malloc_err_warning, + STDLIB_DIR) +from test.support.import_helper import import_fresh_module + + +if sys.platform == 'darwin': + darwin_malloc_err_warning('test_decimal') + + +C = import_fresh_module('decimal', fresh=['_decimal']) +P = import_fresh_module('decimal', blocked=['_decimal']) +import decimal as orig_sys_decimal + +requires_cdecimal = unittest.skipUnless(C, "test requires C version") + +# Useful Test Constant +Signals = { + C: tuple(C.getcontext().flags.keys()) if C else None, + P: tuple(P.getcontext().flags.keys()) +} +# Signals ordered with respect to precedence: when an operation +# produces multiple signals, signals occurring later in the list +# should be handled before those occurring earlier in the list. +OrderedSignals = { + C: [C.Clamped, C.Rounded, C.Inexact, C.Subnormal, C.Underflow, + C.Overflow, C.DivisionByZero, C.InvalidOperation, + C.FloatOperation] if C else None, + P: [P.Clamped, P.Rounded, P.Inexact, P.Subnormal, P.Underflow, + P.Overflow, P.DivisionByZero, P.InvalidOperation, + P.FloatOperation] +} + +def assert_signals(cls, context, attr, expected): + d = getattr(context, attr) + cls.assertTrue(all(d[s] if s in expected else not d[s] for s in d)) + +RoundingModes = [ + P.ROUND_UP, P.ROUND_DOWN, P.ROUND_CEILING, P.ROUND_FLOOR, + P.ROUND_HALF_UP, P.ROUND_HALF_DOWN, P.ROUND_HALF_EVEN, + P.ROUND_05UP +] + +# Tests are built around these assumed context defaults. +# tearDownModule() restores the original context. +ORIGINAL_CONTEXT = { + C: C.getcontext().copy() if C else None, + P: P.getcontext().copy() +} + +# Test extra functionality in the C version (-DEXTRA_FUNCTIONALITY). +EXTRA_FUNCTIONALITY = True if hasattr(C, 'DecClamped') else False +requires_extra_functionality = unittest.skipUnless( + EXTRA_FUNCTIONALITY, "test requires build with -DEXTRA_FUNCTIONALITY") +skip_if_extra_functionality = unittest.skipIf( + EXTRA_FUNCTIONALITY, "test requires regular build") + + +TEST_ALL = is_resource_enabled('decimal') +TODO_TESTS = None +DEBUG = False + + +def load_arithmetic_module(): + module = __import__("test.test_decimal.test_arithmetic") + arithmetic = module.test_decimal.test_arithmetic + arithmetic.TEST_ALL = TEST_ALL + arithmetic.TODO_TESTS = TODO_TESTS + arithmetic.DEBUG = DEBUG + return arithmetic + +def load_tests(loader, tests, pattern): + arithmetic = load_arithmetic_module() + if TODO_TESTS is not None: + # Run only Arithmetic tests + return arithmetic.load_tests(loader, loader.suiteClass(), pattern) + + start_dir = os.path.dirname(__file__) + package_tests = loader.discover(start_dir=start_dir, + top_level_dir=STDLIB_DIR) + tests.addTests(package_tests) + return tests + +def load_tests_for_base_classes(loader, tests, base_classes): + for prefix, mod in ('C', C), ('Py', P): + if not mod: + continue + + for base_class in base_classes: + test_class = type(prefix + base_class.__name__, + (base_class, unittest.TestCase), + {'decimal': mod}) + tests.addTest(loader.loadTestsFromTestCase(test_class)) + + return tests + +def setUpModule(): + for mod in C, P: + if not mod: + continue + + mod.setcontext(mod.Context( + prec=9, rounding=P.ROUND_HALF_EVEN, + traps=dict.fromkeys(Signals[mod], 0), + )) + +def tearDownModule(): + if C: C.setcontext(ORIGINAL_CONTEXT[C].copy()) + P.setcontext(ORIGINAL_CONTEXT[P].copy()) + if not C: + logging.getLogger(__name__).warning( + 'C tests skipped: no module named _decimal.' + ) + if not orig_sys_decimal is sys.modules['decimal']: + raise TestFailed("Internal error: unbalanced number of changes to " + "sys.modules['decimal'].") + diff --git a/Lib/test/test_decimal/__main__.py b/Lib/test/test_decimal/__main__.py new file mode 100644 index 00000000000000..5c4e191252ad20 --- /dev/null +++ b/Lib/test/test_decimal/__main__.py @@ -0,0 +1,31 @@ +import unittest +from . import load_tests +import optparse + +def execute_tests(arith, verbose, todo_tests=None, debug=None): + """ Execute the tests. + + Runs all arithmetic tests if arith is True or if the "decimal" resource + is enabled in regrtest.py + """ + + module = __import__("test.test_decimal") + module = module.test_decimal + module.TEST_ALL = arith + module.TODO_TESTS = todo_tests + module.DEBUG = debug + verbosity = 2 if verbose else 1 + unittest.main(module, verbosity=verbosity, exit=False, argv=[__name__]) + + +p = optparse.OptionParser("test_decimal [--debug] [{--skip | test1 [test2 [...]]}]") +p.add_option('--debug', '-d', action='store_true', help='shows the test number and context before each test') +p.add_option('--skip', '-s', action='store_true', help='skip over 90% of the arithmetic tests') +(opt, args) = p.parse_args() + +if opt.skip: + execute_tests(arith=False, verbose=True) +elif args: + execute_tests(arith=True, verbose=True, todo_tests=args, debug=opt.debug) +else: + execute_tests(arith=True, verbose=True) diff --git a/Lib/test/decimaltestdata/abs.decTest b/Lib/test/test_decimal/data/abs.decTest similarity index 100% rename from Lib/test/decimaltestdata/abs.decTest rename to Lib/test/test_decimal/data/abs.decTest diff --git a/Lib/test/decimaltestdata/add.decTest b/Lib/test/test_decimal/data/add.decTest similarity index 100% rename from Lib/test/decimaltestdata/add.decTest rename to Lib/test/test_decimal/data/add.decTest diff --git a/Lib/test/decimaltestdata/and.decTest b/Lib/test/test_decimal/data/and.decTest similarity index 100% rename from Lib/test/decimaltestdata/and.decTest rename to Lib/test/test_decimal/data/and.decTest diff --git a/Lib/test/decimaltestdata/base.decTest b/Lib/test/test_decimal/data/base.decTest similarity index 100% rename from Lib/test/decimaltestdata/base.decTest rename to Lib/test/test_decimal/data/base.decTest diff --git a/Lib/test/decimaltestdata/clamp.decTest b/Lib/test/test_decimal/data/clamp.decTest similarity index 100% rename from Lib/test/decimaltestdata/clamp.decTest rename to Lib/test/test_decimal/data/clamp.decTest diff --git a/Lib/test/decimaltestdata/class.decTest b/Lib/test/test_decimal/data/class.decTest similarity index 100% rename from Lib/test/decimaltestdata/class.decTest rename to Lib/test/test_decimal/data/class.decTest diff --git a/Lib/test/decimaltestdata/compare.decTest b/Lib/test/test_decimal/data/compare.decTest similarity index 100% rename from Lib/test/decimaltestdata/compare.decTest rename to Lib/test/test_decimal/data/compare.decTest diff --git a/Lib/test/decimaltestdata/comparetotal.decTest b/Lib/test/test_decimal/data/comparetotal.decTest similarity index 100% rename from Lib/test/decimaltestdata/comparetotal.decTest rename to Lib/test/test_decimal/data/comparetotal.decTest diff --git a/Lib/test/decimaltestdata/comparetotmag.decTest b/Lib/test/test_decimal/data/comparetotmag.decTest similarity index 100% rename from Lib/test/decimaltestdata/comparetotmag.decTest rename to Lib/test/test_decimal/data/comparetotmag.decTest diff --git a/Lib/test/decimaltestdata/copy.decTest b/Lib/test/test_decimal/data/copy.decTest similarity index 100% rename from Lib/test/decimaltestdata/copy.decTest rename to Lib/test/test_decimal/data/copy.decTest diff --git a/Lib/test/decimaltestdata/copyabs.decTest b/Lib/test/test_decimal/data/copyabs.decTest similarity index 100% rename from Lib/test/decimaltestdata/copyabs.decTest rename to Lib/test/test_decimal/data/copyabs.decTest diff --git a/Lib/test/decimaltestdata/copynegate.decTest b/Lib/test/test_decimal/data/copynegate.decTest similarity index 100% rename from Lib/test/decimaltestdata/copynegate.decTest rename to Lib/test/test_decimal/data/copynegate.decTest diff --git a/Lib/test/decimaltestdata/copysign.decTest b/Lib/test/test_decimal/data/copysign.decTest similarity index 100% rename from Lib/test/decimaltestdata/copysign.decTest rename to Lib/test/test_decimal/data/copysign.decTest diff --git a/Lib/test/decimaltestdata/ddAbs.decTest b/Lib/test/test_decimal/data/ddAbs.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddAbs.decTest rename to Lib/test/test_decimal/data/ddAbs.decTest diff --git a/Lib/test/decimaltestdata/ddAdd.decTest b/Lib/test/test_decimal/data/ddAdd.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddAdd.decTest rename to Lib/test/test_decimal/data/ddAdd.decTest diff --git a/Lib/test/decimaltestdata/ddAnd.decTest b/Lib/test/test_decimal/data/ddAnd.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddAnd.decTest rename to Lib/test/test_decimal/data/ddAnd.decTest diff --git a/Lib/test/decimaltestdata/ddBase.decTest b/Lib/test/test_decimal/data/ddBase.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddBase.decTest rename to Lib/test/test_decimal/data/ddBase.decTest diff --git a/Lib/test/decimaltestdata/ddCanonical.decTest b/Lib/test/test_decimal/data/ddCanonical.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCanonical.decTest rename to Lib/test/test_decimal/data/ddCanonical.decTest diff --git a/Lib/test/decimaltestdata/ddClass.decTest b/Lib/test/test_decimal/data/ddClass.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddClass.decTest rename to Lib/test/test_decimal/data/ddClass.decTest diff --git a/Lib/test/decimaltestdata/ddCompare.decTest b/Lib/test/test_decimal/data/ddCompare.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCompare.decTest rename to Lib/test/test_decimal/data/ddCompare.decTest diff --git a/Lib/test/decimaltestdata/ddCompareSig.decTest b/Lib/test/test_decimal/data/ddCompareSig.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCompareSig.decTest rename to Lib/test/test_decimal/data/ddCompareSig.decTest diff --git a/Lib/test/decimaltestdata/ddCompareTotal.decTest b/Lib/test/test_decimal/data/ddCompareTotal.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCompareTotal.decTest rename to Lib/test/test_decimal/data/ddCompareTotal.decTest diff --git a/Lib/test/decimaltestdata/ddCompareTotalMag.decTest b/Lib/test/test_decimal/data/ddCompareTotalMag.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCompareTotalMag.decTest rename to Lib/test/test_decimal/data/ddCompareTotalMag.decTest diff --git a/Lib/test/decimaltestdata/ddCopy.decTest b/Lib/test/test_decimal/data/ddCopy.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCopy.decTest rename to Lib/test/test_decimal/data/ddCopy.decTest diff --git a/Lib/test/decimaltestdata/ddCopyAbs.decTest b/Lib/test/test_decimal/data/ddCopyAbs.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCopyAbs.decTest rename to Lib/test/test_decimal/data/ddCopyAbs.decTest diff --git a/Lib/test/decimaltestdata/ddCopyNegate.decTest b/Lib/test/test_decimal/data/ddCopyNegate.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCopyNegate.decTest rename to Lib/test/test_decimal/data/ddCopyNegate.decTest diff --git a/Lib/test/decimaltestdata/ddCopySign.decTest b/Lib/test/test_decimal/data/ddCopySign.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddCopySign.decTest rename to Lib/test/test_decimal/data/ddCopySign.decTest diff --git a/Lib/test/decimaltestdata/ddDivide.decTest b/Lib/test/test_decimal/data/ddDivide.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddDivide.decTest rename to Lib/test/test_decimal/data/ddDivide.decTest diff --git a/Lib/test/decimaltestdata/ddDivideInt.decTest b/Lib/test/test_decimal/data/ddDivideInt.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddDivideInt.decTest rename to Lib/test/test_decimal/data/ddDivideInt.decTest diff --git a/Lib/test/decimaltestdata/ddEncode.decTest b/Lib/test/test_decimal/data/ddEncode.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddEncode.decTest rename to Lib/test/test_decimal/data/ddEncode.decTest diff --git a/Lib/test/decimaltestdata/ddFMA.decTest b/Lib/test/test_decimal/data/ddFMA.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddFMA.decTest rename to Lib/test/test_decimal/data/ddFMA.decTest diff --git a/Lib/test/decimaltestdata/ddInvert.decTest b/Lib/test/test_decimal/data/ddInvert.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddInvert.decTest rename to Lib/test/test_decimal/data/ddInvert.decTest diff --git a/Lib/test/decimaltestdata/ddLogB.decTest b/Lib/test/test_decimal/data/ddLogB.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddLogB.decTest rename to Lib/test/test_decimal/data/ddLogB.decTest diff --git a/Lib/test/decimaltestdata/ddMax.decTest b/Lib/test/test_decimal/data/ddMax.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddMax.decTest rename to Lib/test/test_decimal/data/ddMax.decTest diff --git a/Lib/test/decimaltestdata/ddMaxMag.decTest b/Lib/test/test_decimal/data/ddMaxMag.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddMaxMag.decTest rename to Lib/test/test_decimal/data/ddMaxMag.decTest diff --git a/Lib/test/decimaltestdata/ddMin.decTest b/Lib/test/test_decimal/data/ddMin.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddMin.decTest rename to Lib/test/test_decimal/data/ddMin.decTest diff --git a/Lib/test/decimaltestdata/ddMinMag.decTest b/Lib/test/test_decimal/data/ddMinMag.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddMinMag.decTest rename to Lib/test/test_decimal/data/ddMinMag.decTest diff --git a/Lib/test/decimaltestdata/ddMinus.decTest b/Lib/test/test_decimal/data/ddMinus.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddMinus.decTest rename to Lib/test/test_decimal/data/ddMinus.decTest diff --git a/Lib/test/decimaltestdata/ddMultiply.decTest b/Lib/test/test_decimal/data/ddMultiply.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddMultiply.decTest rename to Lib/test/test_decimal/data/ddMultiply.decTest diff --git a/Lib/test/decimaltestdata/ddNextMinus.decTest b/Lib/test/test_decimal/data/ddNextMinus.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddNextMinus.decTest rename to Lib/test/test_decimal/data/ddNextMinus.decTest diff --git a/Lib/test/decimaltestdata/ddNextPlus.decTest b/Lib/test/test_decimal/data/ddNextPlus.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddNextPlus.decTest rename to Lib/test/test_decimal/data/ddNextPlus.decTest diff --git a/Lib/test/decimaltestdata/ddNextToward.decTest b/Lib/test/test_decimal/data/ddNextToward.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddNextToward.decTest rename to Lib/test/test_decimal/data/ddNextToward.decTest diff --git a/Lib/test/decimaltestdata/ddOr.decTest b/Lib/test/test_decimal/data/ddOr.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddOr.decTest rename to Lib/test/test_decimal/data/ddOr.decTest diff --git a/Lib/test/decimaltestdata/ddPlus.decTest b/Lib/test/test_decimal/data/ddPlus.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddPlus.decTest rename to Lib/test/test_decimal/data/ddPlus.decTest diff --git a/Lib/test/decimaltestdata/ddQuantize.decTest b/Lib/test/test_decimal/data/ddQuantize.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddQuantize.decTest rename to Lib/test/test_decimal/data/ddQuantize.decTest diff --git a/Lib/test/decimaltestdata/ddReduce.decTest b/Lib/test/test_decimal/data/ddReduce.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddReduce.decTest rename to Lib/test/test_decimal/data/ddReduce.decTest diff --git a/Lib/test/decimaltestdata/ddRemainder.decTest b/Lib/test/test_decimal/data/ddRemainder.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddRemainder.decTest rename to Lib/test/test_decimal/data/ddRemainder.decTest diff --git a/Lib/test/decimaltestdata/ddRemainderNear.decTest b/Lib/test/test_decimal/data/ddRemainderNear.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddRemainderNear.decTest rename to Lib/test/test_decimal/data/ddRemainderNear.decTest diff --git a/Lib/test/decimaltestdata/ddRotate.decTest b/Lib/test/test_decimal/data/ddRotate.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddRotate.decTest rename to Lib/test/test_decimal/data/ddRotate.decTest diff --git a/Lib/test/decimaltestdata/ddSameQuantum.decTest b/Lib/test/test_decimal/data/ddSameQuantum.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddSameQuantum.decTest rename to Lib/test/test_decimal/data/ddSameQuantum.decTest diff --git a/Lib/test/decimaltestdata/ddScaleB.decTest b/Lib/test/test_decimal/data/ddScaleB.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddScaleB.decTest rename to Lib/test/test_decimal/data/ddScaleB.decTest diff --git a/Lib/test/decimaltestdata/ddShift.decTest b/Lib/test/test_decimal/data/ddShift.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddShift.decTest rename to Lib/test/test_decimal/data/ddShift.decTest diff --git a/Lib/test/decimaltestdata/ddSubtract.decTest b/Lib/test/test_decimal/data/ddSubtract.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddSubtract.decTest rename to Lib/test/test_decimal/data/ddSubtract.decTest diff --git a/Lib/test/decimaltestdata/ddToIntegral.decTest b/Lib/test/test_decimal/data/ddToIntegral.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddToIntegral.decTest rename to Lib/test/test_decimal/data/ddToIntegral.decTest diff --git a/Lib/test/decimaltestdata/ddXor.decTest b/Lib/test/test_decimal/data/ddXor.decTest similarity index 100% rename from Lib/test/decimaltestdata/ddXor.decTest rename to Lib/test/test_decimal/data/ddXor.decTest diff --git a/Lib/test/decimaltestdata/decDouble.decTest b/Lib/test/test_decimal/data/decDouble.decTest similarity index 100% rename from Lib/test/decimaltestdata/decDouble.decTest rename to Lib/test/test_decimal/data/decDouble.decTest diff --git a/Lib/test/decimaltestdata/decQuad.decTest b/Lib/test/test_decimal/data/decQuad.decTest similarity index 100% rename from Lib/test/decimaltestdata/decQuad.decTest rename to Lib/test/test_decimal/data/decQuad.decTest diff --git a/Lib/test/decimaltestdata/decSingle.decTest b/Lib/test/test_decimal/data/decSingle.decTest similarity index 100% rename from Lib/test/decimaltestdata/decSingle.decTest rename to Lib/test/test_decimal/data/decSingle.decTest diff --git a/Lib/test/decimaltestdata/divide.decTest b/Lib/test/test_decimal/data/divide.decTest similarity index 100% rename from Lib/test/decimaltestdata/divide.decTest rename to Lib/test/test_decimal/data/divide.decTest diff --git a/Lib/test/decimaltestdata/divideint.decTest b/Lib/test/test_decimal/data/divideint.decTest similarity index 100% rename from Lib/test/decimaltestdata/divideint.decTest rename to Lib/test/test_decimal/data/divideint.decTest diff --git a/Lib/test/decimaltestdata/dqAbs.decTest b/Lib/test/test_decimal/data/dqAbs.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqAbs.decTest rename to Lib/test/test_decimal/data/dqAbs.decTest diff --git a/Lib/test/decimaltestdata/dqAdd.decTest b/Lib/test/test_decimal/data/dqAdd.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqAdd.decTest rename to Lib/test/test_decimal/data/dqAdd.decTest diff --git a/Lib/test/decimaltestdata/dqAnd.decTest b/Lib/test/test_decimal/data/dqAnd.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqAnd.decTest rename to Lib/test/test_decimal/data/dqAnd.decTest diff --git a/Lib/test/decimaltestdata/dqBase.decTest b/Lib/test/test_decimal/data/dqBase.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqBase.decTest rename to Lib/test/test_decimal/data/dqBase.decTest diff --git a/Lib/test/decimaltestdata/dqCanonical.decTest b/Lib/test/test_decimal/data/dqCanonical.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCanonical.decTest rename to Lib/test/test_decimal/data/dqCanonical.decTest diff --git a/Lib/test/decimaltestdata/dqClass.decTest b/Lib/test/test_decimal/data/dqClass.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqClass.decTest rename to Lib/test/test_decimal/data/dqClass.decTest diff --git a/Lib/test/decimaltestdata/dqCompare.decTest b/Lib/test/test_decimal/data/dqCompare.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCompare.decTest rename to Lib/test/test_decimal/data/dqCompare.decTest diff --git a/Lib/test/decimaltestdata/dqCompareSig.decTest b/Lib/test/test_decimal/data/dqCompareSig.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCompareSig.decTest rename to Lib/test/test_decimal/data/dqCompareSig.decTest diff --git a/Lib/test/decimaltestdata/dqCompareTotal.decTest b/Lib/test/test_decimal/data/dqCompareTotal.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCompareTotal.decTest rename to Lib/test/test_decimal/data/dqCompareTotal.decTest diff --git a/Lib/test/decimaltestdata/dqCompareTotalMag.decTest b/Lib/test/test_decimal/data/dqCompareTotalMag.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCompareTotalMag.decTest rename to Lib/test/test_decimal/data/dqCompareTotalMag.decTest diff --git a/Lib/test/decimaltestdata/dqCopy.decTest b/Lib/test/test_decimal/data/dqCopy.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCopy.decTest rename to Lib/test/test_decimal/data/dqCopy.decTest diff --git a/Lib/test/decimaltestdata/dqCopyAbs.decTest b/Lib/test/test_decimal/data/dqCopyAbs.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCopyAbs.decTest rename to Lib/test/test_decimal/data/dqCopyAbs.decTest diff --git a/Lib/test/decimaltestdata/dqCopyNegate.decTest b/Lib/test/test_decimal/data/dqCopyNegate.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCopyNegate.decTest rename to Lib/test/test_decimal/data/dqCopyNegate.decTest diff --git a/Lib/test/decimaltestdata/dqCopySign.decTest b/Lib/test/test_decimal/data/dqCopySign.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqCopySign.decTest rename to Lib/test/test_decimal/data/dqCopySign.decTest diff --git a/Lib/test/decimaltestdata/dqDivide.decTest b/Lib/test/test_decimal/data/dqDivide.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqDivide.decTest rename to Lib/test/test_decimal/data/dqDivide.decTest diff --git a/Lib/test/decimaltestdata/dqDivideInt.decTest b/Lib/test/test_decimal/data/dqDivideInt.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqDivideInt.decTest rename to Lib/test/test_decimal/data/dqDivideInt.decTest diff --git a/Lib/test/decimaltestdata/dqEncode.decTest b/Lib/test/test_decimal/data/dqEncode.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqEncode.decTest rename to Lib/test/test_decimal/data/dqEncode.decTest diff --git a/Lib/test/decimaltestdata/dqFMA.decTest b/Lib/test/test_decimal/data/dqFMA.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqFMA.decTest rename to Lib/test/test_decimal/data/dqFMA.decTest diff --git a/Lib/test/decimaltestdata/dqInvert.decTest b/Lib/test/test_decimal/data/dqInvert.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqInvert.decTest rename to Lib/test/test_decimal/data/dqInvert.decTest diff --git a/Lib/test/decimaltestdata/dqLogB.decTest b/Lib/test/test_decimal/data/dqLogB.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqLogB.decTest rename to Lib/test/test_decimal/data/dqLogB.decTest diff --git a/Lib/test/decimaltestdata/dqMax.decTest b/Lib/test/test_decimal/data/dqMax.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqMax.decTest rename to Lib/test/test_decimal/data/dqMax.decTest diff --git a/Lib/test/decimaltestdata/dqMaxMag.decTest b/Lib/test/test_decimal/data/dqMaxMag.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqMaxMag.decTest rename to Lib/test/test_decimal/data/dqMaxMag.decTest diff --git a/Lib/test/decimaltestdata/dqMin.decTest b/Lib/test/test_decimal/data/dqMin.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqMin.decTest rename to Lib/test/test_decimal/data/dqMin.decTest diff --git a/Lib/test/decimaltestdata/dqMinMag.decTest b/Lib/test/test_decimal/data/dqMinMag.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqMinMag.decTest rename to Lib/test/test_decimal/data/dqMinMag.decTest diff --git a/Lib/test/decimaltestdata/dqMinus.decTest b/Lib/test/test_decimal/data/dqMinus.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqMinus.decTest rename to Lib/test/test_decimal/data/dqMinus.decTest diff --git a/Lib/test/decimaltestdata/dqMultiply.decTest b/Lib/test/test_decimal/data/dqMultiply.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqMultiply.decTest rename to Lib/test/test_decimal/data/dqMultiply.decTest diff --git a/Lib/test/decimaltestdata/dqNextMinus.decTest b/Lib/test/test_decimal/data/dqNextMinus.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqNextMinus.decTest rename to Lib/test/test_decimal/data/dqNextMinus.decTest diff --git a/Lib/test/decimaltestdata/dqNextPlus.decTest b/Lib/test/test_decimal/data/dqNextPlus.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqNextPlus.decTest rename to Lib/test/test_decimal/data/dqNextPlus.decTest diff --git a/Lib/test/decimaltestdata/dqNextToward.decTest b/Lib/test/test_decimal/data/dqNextToward.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqNextToward.decTest rename to Lib/test/test_decimal/data/dqNextToward.decTest diff --git a/Lib/test/decimaltestdata/dqOr.decTest b/Lib/test/test_decimal/data/dqOr.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqOr.decTest rename to Lib/test/test_decimal/data/dqOr.decTest diff --git a/Lib/test/decimaltestdata/dqPlus.decTest b/Lib/test/test_decimal/data/dqPlus.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqPlus.decTest rename to Lib/test/test_decimal/data/dqPlus.decTest diff --git a/Lib/test/decimaltestdata/dqQuantize.decTest b/Lib/test/test_decimal/data/dqQuantize.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqQuantize.decTest rename to Lib/test/test_decimal/data/dqQuantize.decTest diff --git a/Lib/test/decimaltestdata/dqReduce.decTest b/Lib/test/test_decimal/data/dqReduce.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqReduce.decTest rename to Lib/test/test_decimal/data/dqReduce.decTest diff --git a/Lib/test/decimaltestdata/dqRemainder.decTest b/Lib/test/test_decimal/data/dqRemainder.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqRemainder.decTest rename to Lib/test/test_decimal/data/dqRemainder.decTest diff --git a/Lib/test/decimaltestdata/dqRemainderNear.decTest b/Lib/test/test_decimal/data/dqRemainderNear.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqRemainderNear.decTest rename to Lib/test/test_decimal/data/dqRemainderNear.decTest diff --git a/Lib/test/decimaltestdata/dqRotate.decTest b/Lib/test/test_decimal/data/dqRotate.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqRotate.decTest rename to Lib/test/test_decimal/data/dqRotate.decTest diff --git a/Lib/test/decimaltestdata/dqSameQuantum.decTest b/Lib/test/test_decimal/data/dqSameQuantum.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqSameQuantum.decTest rename to Lib/test/test_decimal/data/dqSameQuantum.decTest diff --git a/Lib/test/decimaltestdata/dqScaleB.decTest b/Lib/test/test_decimal/data/dqScaleB.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqScaleB.decTest rename to Lib/test/test_decimal/data/dqScaleB.decTest diff --git a/Lib/test/decimaltestdata/dqShift.decTest b/Lib/test/test_decimal/data/dqShift.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqShift.decTest rename to Lib/test/test_decimal/data/dqShift.decTest diff --git a/Lib/test/decimaltestdata/dqSubtract.decTest b/Lib/test/test_decimal/data/dqSubtract.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqSubtract.decTest rename to Lib/test/test_decimal/data/dqSubtract.decTest diff --git a/Lib/test/decimaltestdata/dqToIntegral.decTest b/Lib/test/test_decimal/data/dqToIntegral.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqToIntegral.decTest rename to Lib/test/test_decimal/data/dqToIntegral.decTest diff --git a/Lib/test/decimaltestdata/dqXor.decTest b/Lib/test/test_decimal/data/dqXor.decTest similarity index 100% rename from Lib/test/decimaltestdata/dqXor.decTest rename to Lib/test/test_decimal/data/dqXor.decTest diff --git a/Lib/test/decimaltestdata/dsBase.decTest b/Lib/test/test_decimal/data/dsBase.decTest similarity index 100% rename from Lib/test/decimaltestdata/dsBase.decTest rename to Lib/test/test_decimal/data/dsBase.decTest diff --git a/Lib/test/decimaltestdata/dsEncode.decTest b/Lib/test/test_decimal/data/dsEncode.decTest similarity index 100% rename from Lib/test/decimaltestdata/dsEncode.decTest rename to Lib/test/test_decimal/data/dsEncode.decTest diff --git a/Lib/test/decimaltestdata/exp.decTest b/Lib/test/test_decimal/data/exp.decTest similarity index 100% rename from Lib/test/decimaltestdata/exp.decTest rename to Lib/test/test_decimal/data/exp.decTest diff --git a/Lib/test/decimaltestdata/extra.decTest b/Lib/test/test_decimal/data/extra.decTest similarity index 100% rename from Lib/test/decimaltestdata/extra.decTest rename to Lib/test/test_decimal/data/extra.decTest diff --git a/Lib/test/decimaltestdata/fma.decTest b/Lib/test/test_decimal/data/fma.decTest similarity index 100% rename from Lib/test/decimaltestdata/fma.decTest rename to Lib/test/test_decimal/data/fma.decTest diff --git a/Lib/test/decimaltestdata/inexact.decTest b/Lib/test/test_decimal/data/inexact.decTest similarity index 100% rename from Lib/test/decimaltestdata/inexact.decTest rename to Lib/test/test_decimal/data/inexact.decTest diff --git a/Lib/test/decimaltestdata/invert.decTest b/Lib/test/test_decimal/data/invert.decTest similarity index 100% rename from Lib/test/decimaltestdata/invert.decTest rename to Lib/test/test_decimal/data/invert.decTest diff --git a/Lib/test/decimaltestdata/ln.decTest b/Lib/test/test_decimal/data/ln.decTest similarity index 100% rename from Lib/test/decimaltestdata/ln.decTest rename to Lib/test/test_decimal/data/ln.decTest diff --git a/Lib/test/decimaltestdata/log10.decTest b/Lib/test/test_decimal/data/log10.decTest similarity index 100% rename from Lib/test/decimaltestdata/log10.decTest rename to Lib/test/test_decimal/data/log10.decTest diff --git a/Lib/test/decimaltestdata/logb.decTest b/Lib/test/test_decimal/data/logb.decTest similarity index 100% rename from Lib/test/decimaltestdata/logb.decTest rename to Lib/test/test_decimal/data/logb.decTest diff --git a/Lib/test/decimaltestdata/max.decTest b/Lib/test/test_decimal/data/max.decTest similarity index 100% rename from Lib/test/decimaltestdata/max.decTest rename to Lib/test/test_decimal/data/max.decTest diff --git a/Lib/test/decimaltestdata/maxmag.decTest b/Lib/test/test_decimal/data/maxmag.decTest similarity index 100% rename from Lib/test/decimaltestdata/maxmag.decTest rename to Lib/test/test_decimal/data/maxmag.decTest diff --git a/Lib/test/decimaltestdata/min.decTest b/Lib/test/test_decimal/data/min.decTest similarity index 100% rename from Lib/test/decimaltestdata/min.decTest rename to Lib/test/test_decimal/data/min.decTest diff --git a/Lib/test/decimaltestdata/minmag.decTest b/Lib/test/test_decimal/data/minmag.decTest similarity index 100% rename from Lib/test/decimaltestdata/minmag.decTest rename to Lib/test/test_decimal/data/minmag.decTest diff --git a/Lib/test/decimaltestdata/minus.decTest b/Lib/test/test_decimal/data/minus.decTest similarity index 100% rename from Lib/test/decimaltestdata/minus.decTest rename to Lib/test/test_decimal/data/minus.decTest diff --git a/Lib/test/decimaltestdata/multiply.decTest b/Lib/test/test_decimal/data/multiply.decTest similarity index 100% rename from Lib/test/decimaltestdata/multiply.decTest rename to Lib/test/test_decimal/data/multiply.decTest diff --git a/Lib/test/decimaltestdata/nextminus.decTest b/Lib/test/test_decimal/data/nextminus.decTest similarity index 100% rename from Lib/test/decimaltestdata/nextminus.decTest rename to Lib/test/test_decimal/data/nextminus.decTest diff --git a/Lib/test/decimaltestdata/nextplus.decTest b/Lib/test/test_decimal/data/nextplus.decTest similarity index 100% rename from Lib/test/decimaltestdata/nextplus.decTest rename to Lib/test/test_decimal/data/nextplus.decTest diff --git a/Lib/test/decimaltestdata/nexttoward.decTest b/Lib/test/test_decimal/data/nexttoward.decTest similarity index 100% rename from Lib/test/decimaltestdata/nexttoward.decTest rename to Lib/test/test_decimal/data/nexttoward.decTest diff --git a/Lib/test/decimaltestdata/or.decTest b/Lib/test/test_decimal/data/or.decTest similarity index 100% rename from Lib/test/decimaltestdata/or.decTest rename to Lib/test/test_decimal/data/or.decTest diff --git a/Lib/test/decimaltestdata/plus.decTest b/Lib/test/test_decimal/data/plus.decTest similarity index 100% rename from Lib/test/decimaltestdata/plus.decTest rename to Lib/test/test_decimal/data/plus.decTest diff --git a/Lib/test/decimaltestdata/power.decTest b/Lib/test/test_decimal/data/power.decTest similarity index 100% rename from Lib/test/decimaltestdata/power.decTest rename to Lib/test/test_decimal/data/power.decTest diff --git a/Lib/test/decimaltestdata/powersqrt.decTest b/Lib/test/test_decimal/data/powersqrt.decTest similarity index 100% rename from Lib/test/decimaltestdata/powersqrt.decTest rename to Lib/test/test_decimal/data/powersqrt.decTest diff --git a/Lib/test/decimaltestdata/quantize.decTest b/Lib/test/test_decimal/data/quantize.decTest similarity index 100% rename from Lib/test/decimaltestdata/quantize.decTest rename to Lib/test/test_decimal/data/quantize.decTest diff --git a/Lib/test/decimaltestdata/randomBound32.decTest b/Lib/test/test_decimal/data/randomBound32.decTest similarity index 100% rename from Lib/test/decimaltestdata/randomBound32.decTest rename to Lib/test/test_decimal/data/randomBound32.decTest diff --git a/Lib/test/decimaltestdata/randoms.decTest b/Lib/test/test_decimal/data/randoms.decTest similarity index 100% rename from Lib/test/decimaltestdata/randoms.decTest rename to Lib/test/test_decimal/data/randoms.decTest diff --git a/Lib/test/decimaltestdata/reduce.decTest b/Lib/test/test_decimal/data/reduce.decTest similarity index 100% rename from Lib/test/decimaltestdata/reduce.decTest rename to Lib/test/test_decimal/data/reduce.decTest diff --git a/Lib/test/decimaltestdata/remainder.decTest b/Lib/test/test_decimal/data/remainder.decTest similarity index 100% rename from Lib/test/decimaltestdata/remainder.decTest rename to Lib/test/test_decimal/data/remainder.decTest diff --git a/Lib/test/decimaltestdata/remainderNear.decTest b/Lib/test/test_decimal/data/remainderNear.decTest similarity index 100% rename from Lib/test/decimaltestdata/remainderNear.decTest rename to Lib/test/test_decimal/data/remainderNear.decTest diff --git a/Lib/test/decimaltestdata/rescale.decTest b/Lib/test/test_decimal/data/rescale.decTest similarity index 100% rename from Lib/test/decimaltestdata/rescale.decTest rename to Lib/test/test_decimal/data/rescale.decTest diff --git a/Lib/test/decimaltestdata/rotate.decTest b/Lib/test/test_decimal/data/rotate.decTest similarity index 100% rename from Lib/test/decimaltestdata/rotate.decTest rename to Lib/test/test_decimal/data/rotate.decTest diff --git a/Lib/test/decimaltestdata/rounding.decTest b/Lib/test/test_decimal/data/rounding.decTest similarity index 100% rename from Lib/test/decimaltestdata/rounding.decTest rename to Lib/test/test_decimal/data/rounding.decTest diff --git a/Lib/test/decimaltestdata/samequantum.decTest b/Lib/test/test_decimal/data/samequantum.decTest similarity index 100% rename from Lib/test/decimaltestdata/samequantum.decTest rename to Lib/test/test_decimal/data/samequantum.decTest diff --git a/Lib/test/decimaltestdata/scaleb.decTest b/Lib/test/test_decimal/data/scaleb.decTest similarity index 100% rename from Lib/test/decimaltestdata/scaleb.decTest rename to Lib/test/test_decimal/data/scaleb.decTest diff --git a/Lib/test/decimaltestdata/shift.decTest b/Lib/test/test_decimal/data/shift.decTest similarity index 100% rename from Lib/test/decimaltestdata/shift.decTest rename to Lib/test/test_decimal/data/shift.decTest diff --git a/Lib/test/decimaltestdata/squareroot.decTest b/Lib/test/test_decimal/data/squareroot.decTest similarity index 100% rename from Lib/test/decimaltestdata/squareroot.decTest rename to Lib/test/test_decimal/data/squareroot.decTest diff --git a/Lib/test/decimaltestdata/subtract.decTest b/Lib/test/test_decimal/data/subtract.decTest similarity index 100% rename from Lib/test/decimaltestdata/subtract.decTest rename to Lib/test/test_decimal/data/subtract.decTest diff --git a/Lib/test/decimaltestdata/testall.decTest b/Lib/test/test_decimal/data/testall.decTest similarity index 100% rename from Lib/test/decimaltestdata/testall.decTest rename to Lib/test/test_decimal/data/testall.decTest diff --git a/Lib/test/decimaltestdata/tointegral.decTest b/Lib/test/test_decimal/data/tointegral.decTest similarity index 100% rename from Lib/test/decimaltestdata/tointegral.decTest rename to Lib/test/test_decimal/data/tointegral.decTest diff --git a/Lib/test/decimaltestdata/tointegralx.decTest b/Lib/test/test_decimal/data/tointegralx.decTest similarity index 100% rename from Lib/test/decimaltestdata/tointegralx.decTest rename to Lib/test/test_decimal/data/tointegralx.decTest diff --git a/Lib/test/decimaltestdata/xor.decTest b/Lib/test/test_decimal/data/xor.decTest similarity index 100% rename from Lib/test/decimaltestdata/xor.decTest rename to Lib/test/test_decimal/data/xor.decTest diff --git a/Lib/test/test_decimal/test_api.py b/Lib/test/test_decimal/test_api.py new file mode 100644 index 00000000000000..f50d7d02faa838 --- /dev/null +++ b/Lib/test/test_decimal/test_api.py @@ -0,0 +1,1099 @@ +import unittest +import math +import sys +import pickle +import numbers +from test.support import cpython_only +import random +from . import (C, P, load_tests_for_base_classes, assert_signals, + RoundingModes, OrderedSignals, + setUpModule, tearDownModule) + + +class PythonAPItests: + def test_abc(self): + Decimal = self.decimal.Decimal + + self.assertIsSubclass(Decimal, numbers.Number) + self.assertNotIsSubclass(Decimal, numbers.Real) + self.assertIsInstance(Decimal(0), numbers.Number) + self.assertNotIsInstance(Decimal(0), numbers.Real) + + def test_pickle(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + Decimal = self.decimal.Decimal + + savedecimal = sys.modules['decimal'] + + # Round trip + sys.modules['decimal'] = self.decimal + d = Decimal('-3.141590000') + p = pickle.dumps(d, proto) + e = pickle.loads(p) + self.assertEqual(d, e) + + if C: + # Test interchangeability + x = C.Decimal('-3.123e81723') + y = P.Decimal('-3.123e81723') + + sys.modules['decimal'] = C + sx = pickle.dumps(x, proto) + sys.modules['decimal'] = P + r = pickle.loads(sx) + self.assertIsInstance(r, P.Decimal) + self.assertEqual(r, y) + + sys.modules['decimal'] = P + sy = pickle.dumps(y, proto) + sys.modules['decimal'] = C + r = pickle.loads(sy) + self.assertIsInstance(r, C.Decimal) + self.assertEqual(r, x) + + x = C.Decimal('-3.123e81723').as_tuple() + y = P.Decimal('-3.123e81723').as_tuple() + + sys.modules['decimal'] = C + sx = pickle.dumps(x, proto) + sys.modules['decimal'] = P + r = pickle.loads(sx) + self.assertIsInstance(r, P.DecimalTuple) + self.assertEqual(r, y) + + sys.modules['decimal'] = P + sy = pickle.dumps(y, proto) + sys.modules['decimal'] = C + r = pickle.loads(sy) + self.assertIsInstance(r, C.DecimalTuple) + self.assertEqual(r, x) + + sys.modules['decimal'] = savedecimal + + def test_int(self): + Decimal = self.decimal.Decimal + + for x in range(-250, 250): + s = '%0.2f' % (x / 100.0) + # should work the same as for floats + self.assertEqual(int(Decimal(s)), int(float(s))) + # should work the same as to_integral in the P.ROUND_DOWN mode + d = Decimal(s) + r = d.to_integral(P.ROUND_DOWN) + self.assertEqual(Decimal(int(d)), r) + + self.assertRaises(ValueError, int, Decimal('-nan')) + self.assertRaises(ValueError, int, Decimal('snan')) + self.assertRaises(OverflowError, int, Decimal('inf')) + self.assertRaises(OverflowError, int, Decimal('-inf')) + + @cpython_only + def test_small_ints(self): + Decimal = self.decimal.Decimal + # bpo-46361 + for x in range(-5, 257): + self.assertIs(int(Decimal(x)), x) + + def test_trunc(self): + Decimal = self.decimal.Decimal + + for x in range(-250, 250): + s = '%0.2f' % (x / 100.0) + # should work the same as for floats + self.assertEqual(int(Decimal(s)), int(float(s))) + # should work the same as to_integral in the P.ROUND_DOWN mode + d = Decimal(s) + r = d.to_integral(P.ROUND_DOWN) + self.assertEqual(Decimal(math.trunc(d)), r) + + def test_from_float(self): + + Decimal = self.decimal.Decimal + + class MyDecimal(Decimal): + def __init__(self, _): + self.x = 'y' + + self.assertIsSubclass(MyDecimal, Decimal) + + r = MyDecimal.from_float(0.1) + self.assertEqual(type(r), MyDecimal) + self.assertEqual(str(r), + '0.1000000000000000055511151231257827021181583404541015625') + self.assertEqual(r.x, 'y') + + bigint = 12345678901234567890123456789 + self.assertEqual(MyDecimal.from_float(bigint), MyDecimal(bigint)) + self.assertTrue(MyDecimal.from_float(float('nan')).is_qnan()) + self.assertTrue(MyDecimal.from_float(float('inf')).is_infinite()) + self.assertTrue(MyDecimal.from_float(float('-inf')).is_infinite()) + self.assertEqual(str(MyDecimal.from_float(float('nan'))), + str(Decimal('NaN'))) + self.assertEqual(str(MyDecimal.from_float(float('inf'))), + str(Decimal('Infinity'))) + self.assertEqual(str(MyDecimal.from_float(float('-inf'))), + str(Decimal('-Infinity'))) + self.assertRaises(TypeError, MyDecimal.from_float, 'abc') + for i in range(200): + x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) + self.assertEqual(x, float(MyDecimal.from_float(x))) # roundtrip + + def test_create_decimal_from_float(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + Inexact = self.decimal.Inexact + + context = Context(prec=5, rounding=P.ROUND_DOWN) + self.assertEqual( + context.create_decimal_from_float(math.pi), + Decimal('3.1415') + ) + context = Context(prec=5, rounding=P.ROUND_UP) + self.assertEqual( + context.create_decimal_from_float(math.pi), + Decimal('3.1416') + ) + context = Context(prec=5, traps=[Inexact]) + self.assertRaises( + Inexact, + context.create_decimal_from_float, + math.pi + ) + self.assertEqual(repr(context.create_decimal_from_float(-0.0)), + "Decimal('-0')") + self.assertEqual(repr(context.create_decimal_from_float(1.0)), + "Decimal('1')") + self.assertEqual(repr(context.create_decimal_from_float(10)), + "Decimal('10')") + + def test_quantize(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + InvalidOperation = self.decimal.InvalidOperation + + c = Context(Emax=99999, Emin=-99999) + self.assertEqual( + Decimal('7.335').quantize(Decimal('.01')), + Decimal('7.34') + ) + self.assertEqual( + Decimal('7.335').quantize(Decimal('.01'), rounding=P.ROUND_DOWN), + Decimal('7.33') + ) + self.assertRaises( + InvalidOperation, + Decimal("10e99999").quantize, Decimal('1e100000'), context=c + ) + + c = Context() + d = Decimal("0.871831e800") + x = d.quantize(context=c, exp=Decimal("1e797"), rounding=P.ROUND_DOWN) + self.assertEqual(x, Decimal('8.71E+799')) + + def test_complex(self): + Decimal = self.decimal.Decimal + + x = Decimal("9.8182731e181273") + self.assertEqual(x.real, x) + self.assertEqual(x.imag, 0) + self.assertEqual(x.conjugate(), x) + + x = Decimal("1") + self.assertEqual(complex(x), complex(float(1))) + + self.assertRaises(AttributeError, setattr, x, 'real', 100) + self.assertRaises(AttributeError, setattr, x, 'imag', 100) + self.assertRaises(AttributeError, setattr, x, 'conjugate', 100) + self.assertRaises(AttributeError, setattr, x, '__complex__', 100) + + def test_named_parameters(self): + D = self.decimal.Decimal + Context = self.decimal.Context + localcontext = self.decimal.localcontext + InvalidOperation = self.decimal.InvalidOperation + Overflow = self.decimal.Overflow + + xc = Context() + xc.prec = 1 + xc.Emax = 1 + xc.Emin = -1 + + with localcontext() as c: + c.clear_flags() + + self.assertEqual(D(9, xc), 9) + self.assertEqual(D(9, context=xc), 9) + self.assertEqual(D(context=xc, value=9), 9) + self.assertEqual(D(context=xc), 0) + xc.clear_flags() + self.assertRaises(InvalidOperation, D, "xyz", context=xc) + self.assertTrue(xc.flags[InvalidOperation]) + self.assertFalse(c.flags[InvalidOperation]) + + xc.clear_flags() + self.assertEqual(D(2).exp(context=xc), 7) + self.assertRaises(Overflow, D(8).exp, context=xc) + self.assertTrue(xc.flags[Overflow]) + self.assertFalse(c.flags[Overflow]) + + xc.clear_flags() + self.assertEqual(D(2).ln(context=xc), D('0.7')) + self.assertRaises(InvalidOperation, D(-1).ln, context=xc) + self.assertTrue(xc.flags[InvalidOperation]) + self.assertFalse(c.flags[InvalidOperation]) + + self.assertEqual(D(0).log10(context=xc), D('-inf')) + self.assertEqual(D(-1).next_minus(context=xc), -2) + self.assertEqual(D(-1).next_plus(context=xc), D('-0.9')) + self.assertEqual(D("9.73").normalize(context=xc), D('1E+1')) + self.assertEqual(D("9999").to_integral(context=xc), 9999) + self.assertEqual(D("-2000").to_integral_exact(context=xc), -2000) + self.assertEqual(D("123").to_integral_value(context=xc), 123) + self.assertEqual(D("0.0625").sqrt(context=xc), D('0.2')) + + self.assertEqual(D("0.0625").compare(context=xc, other=3), -1) + xc.clear_flags() + self.assertRaises(InvalidOperation, + D("0").compare_signal, D('nan'), context=xc) + self.assertTrue(xc.flags[InvalidOperation]) + self.assertFalse(c.flags[InvalidOperation]) + self.assertEqual(D("0.01").max(D('0.0101'), context=xc), D('0.0')) + self.assertEqual(D("0.01").max(D('0.0101'), context=xc), D('0.0')) + self.assertEqual(D("0.2").max_mag(D('-0.3'), context=xc), + D('-0.3')) + self.assertEqual(D("0.02").min(D('-0.03'), context=xc), D('-0.0')) + self.assertEqual(D("0.02").min_mag(D('-0.03'), context=xc), + D('0.0')) + self.assertEqual(D("0.2").next_toward(D('-1'), context=xc), D('0.1')) + xc.clear_flags() + self.assertRaises(InvalidOperation, + D("0.2").quantize, D('1e10'), context=xc) + self.assertTrue(xc.flags[InvalidOperation]) + self.assertFalse(c.flags[InvalidOperation]) + self.assertEqual(D("9.99").remainder_near(D('1.5'), context=xc), + D('-0.5')) + + self.assertEqual(D("9.9").fma(third=D('0.9'), context=xc, other=7), + D('7E+1')) + + self.assertRaises(TypeError, D(1).is_canonical, context=xc) + self.assertRaises(TypeError, D(1).is_finite, context=xc) + self.assertRaises(TypeError, D(1).is_infinite, context=xc) + self.assertRaises(TypeError, D(1).is_nan, context=xc) + self.assertRaises(TypeError, D(1).is_qnan, context=xc) + self.assertRaises(TypeError, D(1).is_snan, context=xc) + self.assertRaises(TypeError, D(1).is_signed, context=xc) + self.assertRaises(TypeError, D(1).is_zero, context=xc) + + self.assertFalse(D("0.01").is_normal(context=xc)) + self.assertTrue(D("0.01").is_subnormal(context=xc)) + + self.assertRaises(TypeError, D(1).adjusted, context=xc) + self.assertRaises(TypeError, D(1).conjugate, context=xc) + self.assertRaises(TypeError, D(1).radix, context=xc) + + self.assertEqual(D(-111).logb(context=xc), 2) + self.assertEqual(D(0).logical_invert(context=xc), 1) + self.assertEqual(D('0.01').number_class(context=xc), '+Subnormal') + self.assertEqual(D('0.21').to_eng_string(context=xc), '0.21') + + self.assertEqual(D('11').logical_and(D('10'), context=xc), 0) + self.assertEqual(D('11').logical_or(D('10'), context=xc), 1) + self.assertEqual(D('01').logical_xor(D('10'), context=xc), 1) + self.assertEqual(D('23').rotate(1, context=xc), 3) + self.assertEqual(D('23').rotate(1, context=xc), 3) + xc.clear_flags() + self.assertRaises(Overflow, + D('23').scaleb, 1, context=xc) + self.assertTrue(xc.flags[Overflow]) + self.assertFalse(c.flags[Overflow]) + self.assertEqual(D('23').shift(-1, context=xc), 0) + + self.assertRaises(TypeError, D.from_float, 1.1, context=xc) + self.assertRaises(TypeError, D(0).as_tuple, context=xc) + + self.assertEqual(D(1).canonical(), 1) + self.assertRaises(TypeError, D("-1").copy_abs, context=xc) + self.assertRaises(TypeError, D("-1").copy_negate, context=xc) + self.assertRaises(TypeError, D(1).canonical, context="x") + self.assertRaises(TypeError, D(1).canonical, xyz="x") + + def test_exception_hierarchy(self): + DecimalException = self.decimal.DecimalException + InvalidOperation = self.decimal.InvalidOperation + FloatOperation = self.decimal.FloatOperation + DivisionByZero = self.decimal.DivisionByZero + Overflow = self.decimal.Overflow + Underflow = self.decimal.Underflow + Subnormal = self.decimal.Subnormal + Inexact = self.decimal.Inexact + Rounded = self.decimal.Rounded + Clamped = self.decimal.Clamped + ConversionSyntax = self.decimal.ConversionSyntax + DivisionImpossible = self.decimal.DivisionImpossible + DivisionUndefined = self.decimal.DivisionUndefined + InvalidContext = self.decimal.InvalidContext + + self.assertIsSubclass(DecimalException, ArithmeticError) + + self.assertIsSubclass(InvalidOperation, DecimalException) + self.assertIsSubclass(FloatOperation, DecimalException) + self.assertIsSubclass(FloatOperation, TypeError) + self.assertIsSubclass(DivisionByZero, DecimalException) + self.assertIsSubclass(DivisionByZero, ZeroDivisionError) + self.assertIsSubclass(Overflow, Rounded) + self.assertIsSubclass(Overflow, Inexact) + self.assertIsSubclass(Overflow, DecimalException) + self.assertIsSubclass(Underflow, Inexact) + self.assertIsSubclass(Underflow, Rounded) + self.assertIsSubclass(Underflow, Subnormal) + self.assertIsSubclass(Underflow, DecimalException) + + self.assertIsSubclass(Subnormal, DecimalException) + self.assertIsSubclass(Inexact, DecimalException) + self.assertIsSubclass(Rounded, DecimalException) + self.assertIsSubclass(Clamped, DecimalException) + + self.assertIsSubclass(ConversionSyntax, InvalidOperation) + self.assertIsSubclass(DivisionImpossible, InvalidOperation) + self.assertIsSubclass(DivisionUndefined, InvalidOperation) + self.assertIsSubclass(DivisionUndefined, ZeroDivisionError) + self.assertIsSubclass(InvalidContext, InvalidOperation) + + +class ContextAPItests: + def test_none_args(self): + Context = self.decimal.Context + InvalidOperation = self.decimal.InvalidOperation + DivisionByZero = self.decimal.DivisionByZero + Overflow = self.decimal.Overflow + + c1 = Context() + c2 = Context(prec=None, rounding=None, Emax=None, Emin=None, + capitals=None, clamp=None, flags=None, traps=None) + for c in [c1, c2]: + self.assertEqual(c.prec, 28) + self.assertEqual(c.rounding, P.ROUND_HALF_EVEN) + self.assertEqual(c.Emax, 999999) + self.assertEqual(c.Emin, -999999) + self.assertEqual(c.capitals, 1) + self.assertEqual(c.clamp, 0) + assert_signals(self, c, 'flags', []) + assert_signals(self, c, 'traps', [InvalidOperation, DivisionByZero, + Overflow]) + + def test_pickle(self): + + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + Context = self.decimal.Context + + savedecimal = sys.modules['decimal'] + + # Round trip + sys.modules['decimal'] = self.decimal + c = Context() + e = pickle.loads(pickle.dumps(c, proto)) + + self.assertEqual(c.prec, e.prec) + self.assertEqual(c.Emin, e.Emin) + self.assertEqual(c.Emax, e.Emax) + self.assertEqual(c.rounding, e.rounding) + self.assertEqual(c.capitals, e.capitals) + self.assertEqual(c.clamp, e.clamp) + self.assertEqual(c.flags, e.flags) + self.assertEqual(c.traps, e.traps) + + # Test interchangeability + combinations = [(C, P), (P, C)] if C else [(P, P)] + for dumper, loader in combinations: + for ri, _ in enumerate(RoundingModes): + for fi, _ in enumerate(OrderedSignals[dumper]): + for ti, _ in enumerate(OrderedSignals[dumper]): + + prec = random.randrange(1, 100) + emin = random.randrange(-100, 0) + emax = random.randrange(1, 100) + caps = random.randrange(2) + clamp = random.randrange(2) + + # One module dumps + sys.modules['decimal'] = dumper + c = dumper.Context( + prec=prec, Emin=emin, Emax=emax, + rounding=RoundingModes[ri], + capitals=caps, clamp=clamp, + flags=OrderedSignals[dumper][:fi], + traps=OrderedSignals[dumper][:ti] + ) + s = pickle.dumps(c, proto) + + # The other module loads + sys.modules['decimal'] = loader + d = pickle.loads(s) + self.assertIsInstance(d, loader.Context) + + self.assertEqual(d.prec, prec) + self.assertEqual(d.Emin, emin) + self.assertEqual(d.Emax, emax) + self.assertEqual(d.rounding, RoundingModes[ri]) + self.assertEqual(d.capitals, caps) + self.assertEqual(d.clamp, clamp) + assert_signals(self, d, 'flags', OrderedSignals[loader][:fi]) + assert_signals(self, d, 'traps', OrderedSignals[loader][:ti]) + + sys.modules['decimal'] = savedecimal + + def test_equality_with_other_types(self): + Decimal = self.decimal.Decimal + + self.assertIn(Decimal(10), ['a', 1.0, Decimal(10), (1,2), {}]) + self.assertNotIn(Decimal(10), ['a', 1.0, (1,2), {}]) + + def test_copy(self): + # All copies should be deep + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.copy() + self.assertNotEqual(id(c), id(d)) + self.assertNotEqual(id(c.flags), id(d.flags)) + self.assertNotEqual(id(c.traps), id(d.traps)) + k1 = set(c.flags.keys()) + k2 = set(d.flags.keys()) + self.assertEqual(k1, k2) + self.assertEqual(c.flags, d.flags) + + def test__clamp(self): + # In Python 3.2, the private attribute `_clamp` was made + # public (issue 8540), with the old `_clamp` becoming a + # property wrapping `clamp`. For the duration of Python 3.2 + # only, the attribute should be gettable/settable via both + # `clamp` and `_clamp`; in Python 3.3, `_clamp` should be + # removed. + Context = self.decimal.Context + c = Context() + self.assertRaises(AttributeError, getattr, c, '_clamp') + + def test_abs(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.abs(Decimal(-1)) + self.assertEqual(c.abs(-1), d) + self.assertRaises(TypeError, c.abs, '-1') + + def test_add(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.add(Decimal(1), Decimal(1)) + self.assertEqual(c.add(1, 1), d) + self.assertEqual(c.add(Decimal(1), 1), d) + self.assertEqual(c.add(1, Decimal(1)), d) + self.assertRaises(TypeError, c.add, '1', 1) + self.assertRaises(TypeError, c.add, 1, '1') + + def test_compare(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.compare(Decimal(1), Decimal(1)) + self.assertEqual(c.compare(1, 1), d) + self.assertEqual(c.compare(Decimal(1), 1), d) + self.assertEqual(c.compare(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare, '1', 1) + self.assertRaises(TypeError, c.compare, 1, '1') + + def test_compare_signal(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.compare_signal(Decimal(1), Decimal(1)) + self.assertEqual(c.compare_signal(1, 1), d) + self.assertEqual(c.compare_signal(Decimal(1), 1), d) + self.assertEqual(c.compare_signal(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_signal, '1', 1) + self.assertRaises(TypeError, c.compare_signal, 1, '1') + + def test_compare_total(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.compare_total(Decimal(1), Decimal(1)) + self.assertEqual(c.compare_total(1, 1), d) + self.assertEqual(c.compare_total(Decimal(1), 1), d) + self.assertEqual(c.compare_total(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_total, '1', 1) + self.assertRaises(TypeError, c.compare_total, 1, '1') + + def test_compare_total_mag(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.compare_total_mag(Decimal(1), Decimal(1)) + self.assertEqual(c.compare_total_mag(1, 1), d) + self.assertEqual(c.compare_total_mag(Decimal(1), 1), d) + self.assertEqual(c.compare_total_mag(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_total_mag, '1', 1) + self.assertRaises(TypeError, c.compare_total_mag, 1, '1') + + def test_copy_abs(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.copy_abs(Decimal(-1)) + self.assertEqual(c.copy_abs(-1), d) + self.assertRaises(TypeError, c.copy_abs, '-1') + + def test_copy_decimal(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.copy_decimal(Decimal(-1)) + self.assertEqual(c.copy_decimal(-1), d) + self.assertRaises(TypeError, c.copy_decimal, '-1') + + def test_copy_negate(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.copy_negate(Decimal(-1)) + self.assertEqual(c.copy_negate(-1), d) + self.assertRaises(TypeError, c.copy_negate, '-1') + + def test_copy_sign(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.copy_sign(Decimal(1), Decimal(-2)) + self.assertEqual(c.copy_sign(1, -2), d) + self.assertEqual(c.copy_sign(Decimal(1), -2), d) + self.assertEqual(c.copy_sign(1, Decimal(-2)), d) + self.assertRaises(TypeError, c.copy_sign, '1', -2) + self.assertRaises(TypeError, c.copy_sign, 1, '-2') + + def test_divide(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.divide(Decimal(1), Decimal(2)) + self.assertEqual(c.divide(1, 2), d) + self.assertEqual(c.divide(Decimal(1), 2), d) + self.assertEqual(c.divide(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divide, '1', 2) + self.assertRaises(TypeError, c.divide, 1, '2') + + def test_divide_int(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.divide_int(Decimal(1), Decimal(2)) + self.assertEqual(c.divide_int(1, 2), d) + self.assertEqual(c.divide_int(Decimal(1), 2), d) + self.assertEqual(c.divide_int(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divide_int, '1', 2) + self.assertRaises(TypeError, c.divide_int, 1, '2') + + def test_divmod(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.divmod(Decimal(1), Decimal(2)) + self.assertEqual(c.divmod(1, 2), d) + self.assertEqual(c.divmod(Decimal(1), 2), d) + self.assertEqual(c.divmod(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divmod, '1', 2) + self.assertRaises(TypeError, c.divmod, 1, '2') + + def test_exp(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.exp(Decimal(10)) + self.assertEqual(c.exp(10), d) + self.assertRaises(TypeError, c.exp, '10') + + def test_fma(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.fma(Decimal(2), Decimal(3), Decimal(4)) + self.assertEqual(c.fma(2, 3, 4), d) + self.assertEqual(c.fma(Decimal(2), 3, 4), d) + self.assertEqual(c.fma(2, Decimal(3), 4), d) + self.assertEqual(c.fma(2, 3, Decimal(4)), d) + self.assertEqual(c.fma(Decimal(2), Decimal(3), 4), d) + self.assertRaises(TypeError, c.fma, '2', 3, 4) + self.assertRaises(TypeError, c.fma, 2, '3', 4) + self.assertRaises(TypeError, c.fma, 2, 3, '4') + + # Issue 12079 for Context.fma ... + self.assertRaises(TypeError, c.fma, + Decimal('Infinity'), Decimal(0), "not a decimal") + self.assertRaises(TypeError, c.fma, + Decimal(1), Decimal('snan'), 1.222) + # ... and for Decimal.fma. + self.assertRaises(TypeError, Decimal('Infinity').fma, + Decimal(0), "not a decimal") + self.assertRaises(TypeError, Decimal(1).fma, + Decimal('snan'), 1.222) + + def test_is_finite(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_finite(Decimal(10)) + self.assertEqual(c.is_finite(10), d) + self.assertRaises(TypeError, c.is_finite, '10') + + def test_is_infinite(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_infinite(Decimal(10)) + self.assertEqual(c.is_infinite(10), d) + self.assertRaises(TypeError, c.is_infinite, '10') + + def test_is_nan(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_nan(Decimal(10)) + self.assertEqual(c.is_nan(10), d) + self.assertRaises(TypeError, c.is_nan, '10') + + def test_is_normal(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_normal(Decimal(10)) + self.assertEqual(c.is_normal(10), d) + self.assertRaises(TypeError, c.is_normal, '10') + + def test_is_qnan(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_qnan(Decimal(10)) + self.assertEqual(c.is_qnan(10), d) + self.assertRaises(TypeError, c.is_qnan, '10') + + def test_is_signed(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_signed(Decimal(10)) + self.assertEqual(c.is_signed(10), d) + self.assertRaises(TypeError, c.is_signed, '10') + + def test_is_snan(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_snan(Decimal(10)) + self.assertEqual(c.is_snan(10), d) + self.assertRaises(TypeError, c.is_snan, '10') + + def test_is_subnormal(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_subnormal(Decimal(10)) + self.assertEqual(c.is_subnormal(10), d) + self.assertRaises(TypeError, c.is_subnormal, '10') + + def test_is_zero(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_zero(Decimal(10)) + self.assertEqual(c.is_zero(10), d) + self.assertRaises(TypeError, c.is_zero, '10') + + def test_ln(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.ln(Decimal(10)) + self.assertEqual(c.ln(10), d) + self.assertRaises(TypeError, c.ln, '10') + + def test_log10(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.log10(Decimal(10)) + self.assertEqual(c.log10(10), d) + self.assertRaises(TypeError, c.log10, '10') + + def test_logb(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.logb(Decimal(10)) + self.assertEqual(c.logb(10), d) + self.assertRaises(TypeError, c.logb, '10') + + def test_logical_and(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.logical_and(Decimal(1), Decimal(1)) + self.assertEqual(c.logical_and(1, 1), d) + self.assertEqual(c.logical_and(Decimal(1), 1), d) + self.assertEqual(c.logical_and(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_and, '1', 1) + self.assertRaises(TypeError, c.logical_and, 1, '1') + + def test_logical_invert(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.logical_invert(Decimal(1000)) + self.assertEqual(c.logical_invert(1000), d) + self.assertRaises(TypeError, c.logical_invert, '1000') + + def test_logical_or(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.logical_or(Decimal(1), Decimal(1)) + self.assertEqual(c.logical_or(1, 1), d) + self.assertEqual(c.logical_or(Decimal(1), 1), d) + self.assertEqual(c.logical_or(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_or, '1', 1) + self.assertRaises(TypeError, c.logical_or, 1, '1') + + def test_logical_xor(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.logical_xor(Decimal(1), Decimal(1)) + self.assertEqual(c.logical_xor(1, 1), d) + self.assertEqual(c.logical_xor(Decimal(1), 1), d) + self.assertEqual(c.logical_xor(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_xor, '1', 1) + self.assertRaises(TypeError, c.logical_xor, 1, '1') + + def test_max(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.max(Decimal(1), Decimal(2)) + self.assertEqual(c.max(1, 2), d) + self.assertEqual(c.max(Decimal(1), 2), d) + self.assertEqual(c.max(1, Decimal(2)), d) + self.assertRaises(TypeError, c.max, '1', 2) + self.assertRaises(TypeError, c.max, 1, '2') + + def test_max_mag(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.max_mag(Decimal(1), Decimal(2)) + self.assertEqual(c.max_mag(1, 2), d) + self.assertEqual(c.max_mag(Decimal(1), 2), d) + self.assertEqual(c.max_mag(1, Decimal(2)), d) + self.assertRaises(TypeError, c.max_mag, '1', 2) + self.assertRaises(TypeError, c.max_mag, 1, '2') + + def test_min(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.min(Decimal(1), Decimal(2)) + self.assertEqual(c.min(1, 2), d) + self.assertEqual(c.min(Decimal(1), 2), d) + self.assertEqual(c.min(1, Decimal(2)), d) + self.assertRaises(TypeError, c.min, '1', 2) + self.assertRaises(TypeError, c.min, 1, '2') + + def test_min_mag(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.min_mag(Decimal(1), Decimal(2)) + self.assertEqual(c.min_mag(1, 2), d) + self.assertEqual(c.min_mag(Decimal(1), 2), d) + self.assertEqual(c.min_mag(1, Decimal(2)), d) + self.assertRaises(TypeError, c.min_mag, '1', 2) + self.assertRaises(TypeError, c.min_mag, 1, '2') + + def test_minus(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.minus(Decimal(10)) + self.assertEqual(c.minus(10), d) + self.assertRaises(TypeError, c.minus, '10') + + def test_multiply(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.multiply(Decimal(1), Decimal(2)) + self.assertEqual(c.multiply(1, 2), d) + self.assertEqual(c.multiply(Decimal(1), 2), d) + self.assertEqual(c.multiply(1, Decimal(2)), d) + self.assertRaises(TypeError, c.multiply, '1', 2) + self.assertRaises(TypeError, c.multiply, 1, '2') + + def test_next_minus(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.next_minus(Decimal(10)) + self.assertEqual(c.next_minus(10), d) + self.assertRaises(TypeError, c.next_minus, '10') + + def test_next_plus(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.next_plus(Decimal(10)) + self.assertEqual(c.next_plus(10), d) + self.assertRaises(TypeError, c.next_plus, '10') + + def test_next_toward(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.next_toward(Decimal(1), Decimal(2)) + self.assertEqual(c.next_toward(1, 2), d) + self.assertEqual(c.next_toward(Decimal(1), 2), d) + self.assertEqual(c.next_toward(1, Decimal(2)), d) + self.assertRaises(TypeError, c.next_toward, '1', 2) + self.assertRaises(TypeError, c.next_toward, 1, '2') + + def test_normalize(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.normalize(Decimal(10)) + self.assertEqual(c.normalize(10), d) + self.assertRaises(TypeError, c.normalize, '10') + + def test_number_class(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + self.assertEqual(c.number_class(123), c.number_class(Decimal(123))) + self.assertEqual(c.number_class(0), c.number_class(Decimal(0))) + self.assertEqual(c.number_class(-45), c.number_class(Decimal(-45))) + + def test_plus(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.plus(Decimal(10)) + self.assertEqual(c.plus(10), d) + self.assertRaises(TypeError, c.plus, '10') + + def test_power(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.power(Decimal(1), Decimal(4)) + self.assertEqual(c.power(1, 4), d) + self.assertEqual(c.power(Decimal(1), 4), d) + self.assertEqual(c.power(1, Decimal(4)), d) + self.assertEqual(c.power(Decimal(1), Decimal(4)), d) + self.assertRaises(TypeError, c.power, '1', 4) + self.assertRaises(TypeError, c.power, 1, '4') + self.assertEqual(c.power(modulo=5, b=8, a=2), 1) + + def test_quantize(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.quantize(Decimal(1), Decimal(2)) + self.assertEqual(c.quantize(1, 2), d) + self.assertEqual(c.quantize(Decimal(1), 2), d) + self.assertEqual(c.quantize(1, Decimal(2)), d) + self.assertRaises(TypeError, c.quantize, '1', 2) + self.assertRaises(TypeError, c.quantize, 1, '2') + + def test_remainder(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.remainder(Decimal(1), Decimal(2)) + self.assertEqual(c.remainder(1, 2), d) + self.assertEqual(c.remainder(Decimal(1), 2), d) + self.assertEqual(c.remainder(1, Decimal(2)), d) + self.assertRaises(TypeError, c.remainder, '1', 2) + self.assertRaises(TypeError, c.remainder, 1, '2') + + def test_remainder_near(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.remainder_near(Decimal(1), Decimal(2)) + self.assertEqual(c.remainder_near(1, 2), d) + self.assertEqual(c.remainder_near(Decimal(1), 2), d) + self.assertEqual(c.remainder_near(1, Decimal(2)), d) + self.assertRaises(TypeError, c.remainder_near, '1', 2) + self.assertRaises(TypeError, c.remainder_near, 1, '2') + + def test_rotate(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.rotate(Decimal(1), Decimal(2)) + self.assertEqual(c.rotate(1, 2), d) + self.assertEqual(c.rotate(Decimal(1), 2), d) + self.assertEqual(c.rotate(1, Decimal(2)), d) + self.assertRaises(TypeError, c.rotate, '1', 2) + self.assertRaises(TypeError, c.rotate, 1, '2') + + def test_sqrt(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.sqrt(Decimal(10)) + self.assertEqual(c.sqrt(10), d) + self.assertRaises(TypeError, c.sqrt, '10') + + def test_same_quantum(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.same_quantum(Decimal(1), Decimal(2)) + self.assertEqual(c.same_quantum(1, 2), d) + self.assertEqual(c.same_quantum(Decimal(1), 2), d) + self.assertEqual(c.same_quantum(1, Decimal(2)), d) + self.assertRaises(TypeError, c.same_quantum, '1', 2) + self.assertRaises(TypeError, c.same_quantum, 1, '2') + + def test_scaleb(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.scaleb(Decimal(1), Decimal(2)) + self.assertEqual(c.scaleb(1, 2), d) + self.assertEqual(c.scaleb(Decimal(1), 2), d) + self.assertEqual(c.scaleb(1, Decimal(2)), d) + self.assertRaises(TypeError, c.scaleb, '1', 2) + self.assertRaises(TypeError, c.scaleb, 1, '2') + + def test_shift(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.shift(Decimal(1), Decimal(2)) + self.assertEqual(c.shift(1, 2), d) + self.assertEqual(c.shift(Decimal(1), 2), d) + self.assertEqual(c.shift(1, Decimal(2)), d) + self.assertRaises(TypeError, c.shift, '1', 2) + self.assertRaises(TypeError, c.shift, 1, '2') + + def test_subtract(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.subtract(Decimal(1), Decimal(2)) + self.assertEqual(c.subtract(1, 2), d) + self.assertEqual(c.subtract(Decimal(1), 2), d) + self.assertEqual(c.subtract(1, Decimal(2)), d) + self.assertRaises(TypeError, c.subtract, '1', 2) + self.assertRaises(TypeError, c.subtract, 1, '2') + + def test_to_eng_string(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.to_eng_string(Decimal(10)) + self.assertEqual(c.to_eng_string(10), d) + self.assertRaises(TypeError, c.to_eng_string, '10') + + def test_to_sci_string(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.to_sci_string(Decimal(10)) + self.assertEqual(c.to_sci_string(10), d) + self.assertRaises(TypeError, c.to_sci_string, '10') + + def test_to_integral_exact(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.to_integral_exact(Decimal(10)) + self.assertEqual(c.to_integral_exact(10), d) + self.assertRaises(TypeError, c.to_integral_exact, '10') + + def test_to_integral_value(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.to_integral_value(Decimal(10)) + self.assertEqual(c.to_integral_value(10), d) + self.assertRaises(TypeError, c.to_integral_value, '10') + self.assertRaises(TypeError, c.to_integral_value, 10, 'x') + + +def load_tests(loader, tests, pattern): + base_classes = [PythonAPItests, ContextAPItests] + return load_tests_for_base_classes(loader, tests, base_classes) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_arithmetic.py b/Lib/test/test_decimal/test_arithmetic.py new file mode 100644 index 00000000000000..83004c5302a8aa --- /dev/null +++ b/Lib/test/test_decimal/test_arithmetic.py @@ -0,0 +1,405 @@ +import unittest +import os +import sys +import random +from . import (C, P, load_tests_for_base_classes, + Signals, OrderedSignals, + setUpModule, tearDownModule) + +TESTDATADIR = 'data' +if __name__ == '__main__': + file = sys.argv[0] +else: + file = __file__ +testdir = os.path.dirname(file) or os.curdir +directory = testdir + os.sep + TESTDATADIR + os.sep + +skip_expected = not os.path.isdir(directory) + +# Make sure it actually raises errors when not expected and caught in flags +# Slower, since it runs some things several times. +EXTENDEDERRORTEST = False + + +TEST_ALL = True +TODO_TESTS = None +DEBUG = False + + +class IBMTestCases: + """Class which tests the Decimal class against the IBM test cases.""" + + def setUp(self): + self.context = self.decimal.Context() + self.readcontext = self.decimal.Context() + self.ignore_list = ['#'] + + # List of individual .decTest test ids that correspond to tests that + # we're skipping for one reason or another. + self.skipped_test_ids = set([ + # Skip implementation-specific scaleb tests. + 'scbx164', + 'scbx165', + + # For some operations (currently exp, ln, log10, power), the decNumber + # reference implementation imposes additional restrictions on the context + # and operands. These restrictions are not part of the specification; + # however, the effect of these restrictions does show up in some of the + # testcases. We skip testcases that violate these restrictions, since + # Decimal behaves differently from decNumber for these testcases so these + # testcases would otherwise fail. + 'expx901', + 'expx902', + 'expx903', + 'expx905', + 'lnx901', + 'lnx902', + 'lnx903', + 'lnx905', + 'logx901', + 'logx902', + 'logx903', + 'logx905', + 'powx1183', + 'powx1184', + 'powx4001', + 'powx4002', + 'powx4003', + 'powx4005', + 'powx4008', + 'powx4010', + 'powx4012', + 'powx4014', + ]) + + if self.decimal == C: + # status has additional Subnormal, Underflow + self.skipped_test_ids.add('pwsx803') + self.skipped_test_ids.add('pwsx805') + # Correct rounding (skipped for decNumber, too) + self.skipped_test_ids.add('powx4302') + self.skipped_test_ids.add('powx4303') + self.skipped_test_ids.add('powx4342') + self.skipped_test_ids.add('powx4343') + # http://bugs.python.org/issue7049 + self.skipped_test_ids.add('pwmx325') + self.skipped_test_ids.add('pwmx326') + + # Map test directives to setter functions. + self.ChangeDict = {'precision' : self.change_precision, + 'rounding' : self.change_rounding_method, + 'maxexponent' : self.change_max_exponent, + 'minexponent' : self.change_min_exponent, + 'clamp' : self.change_clamp} + + # Name adapter to be able to change the Decimal and Context + # interface without changing the test files from Cowlishaw. + self.NameAdapter = {'and':'logical_and', + 'apply':'_apply', + 'class':'number_class', + 'comparesig':'compare_signal', + 'comparetotal':'compare_total', + 'comparetotmag':'compare_total_mag', + 'copy':'copy_decimal', + 'copyabs':'copy_abs', + 'copynegate':'copy_negate', + 'copysign':'copy_sign', + 'divideint':'divide_int', + 'invert':'logical_invert', + 'iscanonical':'is_canonical', + 'isfinite':'is_finite', + 'isinfinite':'is_infinite', + 'isnan':'is_nan', + 'isnormal':'is_normal', + 'isqnan':'is_qnan', + 'issigned':'is_signed', + 'issnan':'is_snan', + 'issubnormal':'is_subnormal', + 'iszero':'is_zero', + 'maxmag':'max_mag', + 'minmag':'min_mag', + 'nextminus':'next_minus', + 'nextplus':'next_plus', + 'nexttoward':'next_toward', + 'or':'logical_or', + 'reduce':'normalize', + 'remaindernear':'remainder_near', + 'samequantum':'same_quantum', + 'squareroot':'sqrt', + 'toeng':'to_eng_string', + 'tointegral':'to_integral_value', + 'tointegralx':'to_integral_exact', + 'tosci':'to_sci_string', + 'xor':'logical_xor'} + + # Map test-case names to roundings. + self.RoundingDict = {'ceiling' :P.ROUND_CEILING, + 'down' :P.ROUND_DOWN, + 'floor' :P.ROUND_FLOOR, + 'half_down' :P.ROUND_HALF_DOWN, + 'half_even' :P.ROUND_HALF_EVEN, + 'half_up' :P.ROUND_HALF_UP, + 'up' :P.ROUND_UP, + '05up' :P.ROUND_05UP} + + # Map the test cases' error names to the actual errors. + self.ErrorNames = {'clamped' : self.decimal.Clamped, + 'conversion_syntax' : self.decimal.InvalidOperation, + 'division_by_zero' : self.decimal.DivisionByZero, + 'division_impossible' : self.decimal.InvalidOperation, + 'division_undefined' : self.decimal.InvalidOperation, + 'inexact' : self.decimal.Inexact, + 'invalid_context' : self.decimal.InvalidOperation, + 'invalid_operation' : self.decimal.InvalidOperation, + 'overflow' : self.decimal.Overflow, + 'rounded' : self.decimal.Rounded, + 'subnormal' : self.decimal.Subnormal, + 'underflow' : self.decimal.Underflow} + + # The following functions return True/False rather than a + # Decimal instance. + self.LogicalFunctions = ('is_canonical', + 'is_finite', + 'is_infinite', + 'is_nan', + 'is_normal', + 'is_qnan', + 'is_signed', + 'is_snan', + 'is_subnormal', + 'is_zero', + 'same_quantum') + + def read_unlimited(self, v, context): + """Work around the limitations of the 32-bit _decimal version. The + guaranteed maximum values for prec, Emax etc. are 425000000, + but higher values usually work, except for rare corner cases. + In particular, all of the IBM tests pass with maximum values + of 1070000000.""" + if self.decimal == C and self.decimal.MAX_EMAX == 425000000: + self.readcontext._unsafe_setprec(1070000000) + self.readcontext._unsafe_setemax(1070000000) + self.readcontext._unsafe_setemin(-1070000000) + return self.readcontext.create_decimal(v) + else: + return self.decimal.Decimal(v, context) + + def eval_file(self, file): + global skip_expected + if skip_expected: + raise unittest.SkipTest + with open(file, encoding="utf-8") as f: + for line in f: + line = line.replace('\r\n', '').replace('\n', '') + #print line + try: + t = self.eval_line(line) + except self.decimal.DecimalException as exception: + #Exception raised where there shouldn't have been one. + self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line) + + + def eval_line(self, s): + if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'): + s = (s.split('->')[0] + '->' + + s.split('->')[1].split('--')[0]).strip() + else: + s = s.split('--')[0].strip() + + for ignore in self.ignore_list: + if s.find(ignore) >= 0: + #print s.split()[0], 'NotImplemented--', ignore + return + if not s: + return + elif ':' in s: + return self.eval_directive(s) + else: + return self.eval_equation(s) + + def eval_directive(self, s): + funct, value = (x.strip().lower() for x in s.split(':')) + if funct == 'rounding': + value = self.RoundingDict[value] + else: + try: + value = int(value) + except ValueError: + pass + + funct = self.ChangeDict.get(funct, (lambda *args: None)) + funct(value) + + def eval_equation(self, s): + if not TEST_ALL and random.random() < 0.90: + return + + self.context.clear_flags() + + try: + Sides = s.split('->') + L = Sides[0].strip().split() + id = L[0] + if DEBUG: + print("Test ", id, end=" ") + funct = L[1].lower() + valstemp = L[2:] + L = Sides[1].strip().split() + ans = L[0] + exceptions = L[1:] + except (TypeError, AttributeError, IndexError): + raise self.decimal.InvalidOperation + def FixQuotes(val): + val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote') + val = val.replace("'", '').replace('"', '') + val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"') + return val + + if id in self.skipped_test_ids: + return + + fname = self.NameAdapter.get(funct, funct) + if fname == 'rescale': + return + funct = getattr(self.context, fname) + vals = [] + conglomerate = '' + quote = 0 + theirexceptions = [self.ErrorNames[x.lower()] for x in exceptions] + + for exception in Signals[self.decimal]: + self.context.traps[exception] = 1 #Catch these bugs... + for exception in theirexceptions: + self.context.traps[exception] = 0 + for i, val in enumerate(valstemp): + if val.count("'") % 2 == 1: + quote = 1 - quote + if quote: + conglomerate = conglomerate + ' ' + val + continue + else: + val = conglomerate + val + conglomerate = '' + v = FixQuotes(val) + if fname in ('to_sci_string', 'to_eng_string'): + if EXTENDEDERRORTEST: + for error in theirexceptions: + self.context.traps[error] = 1 + try: + funct(self.context.create_decimal(v)) + except error: + pass + except Signals[self.decimal] as e: + self.fail("Raised %s in %s when %s disabled" % \ + (e, s, error)) + else: + self.fail("Did not raise %s in %s" % (error, s)) + self.context.traps[error] = 0 + v = self.context.create_decimal(v) + else: + v = self.read_unlimited(v, self.context) + vals.append(v) + + ans = FixQuotes(ans) + + if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'): + for error in theirexceptions: + self.context.traps[error] = 1 + try: + funct(*vals) + except error: + pass + except Signals[self.decimal] as e: + self.fail("Raised %s in %s when %s disabled" % \ + (e, s, error)) + else: + self.fail("Did not raise %s in %s" % (error, s)) + self.context.traps[error] = 0 + + # as above, but add traps cumulatively, to check precedence + ordered_errors = [e for e in OrderedSignals[self.decimal] if e in theirexceptions] + for error in ordered_errors: + self.context.traps[error] = 1 + try: + funct(*vals) + except error: + pass + except Signals[self.decimal] as e: + self.fail("Raised %s in %s; expected %s" % + (type(e), s, error)) + else: + self.fail("Did not raise %s in %s" % (error, s)) + # reset traps + for error in ordered_errors: + self.context.traps[error] = 0 + + + if DEBUG: + print("--", self.context) + try: + result = str(funct(*vals)) + if fname in self.LogicalFunctions: + result = str(int(eval(result))) # 'True', 'False' -> '1', '0' + except Signals[self.decimal] as error: + self.fail("Raised %s in %s" % (error, s)) + except: #Catch any error long enough to state the test case. + print("ERROR:", s) + raise + + myexceptions = self.getexceptions() + + myexceptions.sort(key=repr) + theirexceptions.sort(key=repr) + + self.assertEqual(result, ans, + 'Incorrect answer for ' + s + ' -- got ' + result) + + self.assertEqual(myexceptions, theirexceptions, + 'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions)) + + def getexceptions(self): + return [e for e in Signals[self.decimal] if self.context.flags[e]] + + def change_precision(self, prec): + if self.decimal == C and self.decimal.MAX_PREC == 425000000: + self.context._unsafe_setprec(prec) + else: + self.context.prec = prec + + def change_rounding_method(self, rounding): + self.context.rounding = rounding + + def change_min_exponent(self, exp): + if self.decimal == C and self.decimal.MAX_PREC == 425000000: + self.context._unsafe_setemin(exp) + else: + self.context.Emin = exp + + def change_max_exponent(self, exp): + if self.decimal == C and self.decimal.MAX_PREC == 425000000: + self.context._unsafe_setemax(exp) + else: + self.context.Emax = exp + + def change_clamp(self, clamp): + self.context.clamp = clamp + + +def load_tests(loader, tests, pattern): + # Dynamically build custom test definition for each file in the test + # directory and add the definitions to the DecimalTest class. This + # procedure insures that new files do not get skipped. + for filename in os.listdir(directory): + if '.decTest' not in filename or filename.startswith("."): + continue + head, tail = filename.split('.') + if TODO_TESTS is not None and head not in TODO_TESTS: + continue + tester = lambda self, f=filename: self.eval_file(directory + f) + setattr(IBMTestCases, 'test_' + head, tester) + + return load_tests_for_base_classes(loader, tests, [IBMTestCases]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_c_specific.py b/Lib/test/test_decimal/test_c_specific.py new file mode 100644 index 00000000000000..b313b72ec49d91 --- /dev/null +++ b/Lib/test/test_decimal/test_c_specific.py @@ -0,0 +1,853 @@ +import unittest +import sys +from test.support import check_disallow_instantiation +import random +from . import (C, P, requires_cdecimal, requires_extra_functionality, + RoundingModes, OrderedSignals, + setUpModule, tearDownModule) + + +@requires_cdecimal +@requires_extra_functionality +class CFunctionality(unittest.TestCase): + """Extra functionality in _decimal""" + + def test_c_context(self): + Context = C.Context + + c = Context(flags=C.DecClamped, traps=C.DecRounded) + self.assertEqual(c._flags, C.DecClamped) + self.assertEqual(c._traps, C.DecRounded) + + def test_constants(self): + # Condition flags + cond = ( + C.DecClamped, C.DecConversionSyntax, C.DecDivisionByZero, + C.DecDivisionImpossible, C.DecDivisionUndefined, + C.DecFpuError, C.DecInexact, C.DecInvalidContext, + C.DecInvalidOperation, C.DecMallocError, + C.DecFloatOperation, C.DecOverflow, C.DecRounded, + C.DecSubnormal, C.DecUnderflow + ) + + # Conditions + for i, v in enumerate(cond): + self.assertEqual(v, 1< 425000000) + + c = Context() + + # SignalDict: input validation + self.assertRaises(KeyError, c.flags.__setitem__, 801, 0) + self.assertRaises(KeyError, c.traps.__setitem__, 801, 0) + self.assertRaises(ValueError, c.flags.__delitem__, Overflow) + self.assertRaises(ValueError, c.traps.__delitem__, InvalidOperation) + self.assertRaises(TypeError, setattr, c, 'flags', ['x']) + self.assertRaises(TypeError, setattr, c,'traps', ['y']) + self.assertRaises(KeyError, setattr, c, 'flags', {0:1}) + self.assertRaises(KeyError, setattr, c, 'traps', {0:1}) + + # Test assignment from a signal dict with the correct length but + # one invalid key. + d = c.flags.copy() + del d[FloatOperation] + d["XYZ"] = 91283719 + self.assertRaises(KeyError, setattr, c, 'flags', d) + self.assertRaises(KeyError, setattr, c, 'traps', d) + + # Input corner cases + int_max = 2**63-1 if HAVE_CONFIG_64 else 2**31-1 + gt_max_emax = 10**18 if HAVE_CONFIG_64 else 10**9 + + # prec, Emax, Emin + for attr in ['prec', 'Emax']: + self.assertRaises(ValueError, setattr, c, attr, gt_max_emax) + self.assertRaises(ValueError, setattr, c, 'Emin', -gt_max_emax) + + # prec, Emax, Emin in context constructor + self.assertRaises(ValueError, Context, prec=gt_max_emax) + self.assertRaises(ValueError, Context, Emax=gt_max_emax) + self.assertRaises(ValueError, Context, Emin=-gt_max_emax) + + # Overflow in conversion + self.assertRaises(OverflowError, Context, prec=int_max+1) + self.assertRaises(OverflowError, Context, Emax=int_max+1) + self.assertRaises(OverflowError, Context, Emin=-int_max-2) + self.assertRaises(OverflowError, Context, clamp=int_max+1) + self.assertRaises(OverflowError, Context, capitals=int_max+1) + + # OverflowError, general ValueError + for attr in ('prec', 'Emin', 'Emax', 'capitals', 'clamp'): + self.assertRaises(OverflowError, setattr, c, attr, int_max+1) + self.assertRaises(OverflowError, setattr, c, attr, -int_max-2) + if sys.platform != 'win32': + self.assertRaises(ValueError, setattr, c, attr, int_max) + self.assertRaises(ValueError, setattr, c, attr, -int_max-1) + + # OverflowError: _unsafe_setprec, _unsafe_setemin, _unsafe_setemax + if C.MAX_PREC == 425000000: + self.assertRaises(OverflowError, getattr(c, '_unsafe_setprec'), + int_max+1) + self.assertRaises(OverflowError, getattr(c, '_unsafe_setemax'), + int_max+1) + self.assertRaises(OverflowError, getattr(c, '_unsafe_setemin'), + -int_max-2) + + # ValueError: _unsafe_setprec, _unsafe_setemin, _unsafe_setemax + if C.MAX_PREC == 425000000: + self.assertRaises(ValueError, getattr(c, '_unsafe_setprec'), 0) + self.assertRaises(ValueError, getattr(c, '_unsafe_setprec'), + 1070000001) + self.assertRaises(ValueError, getattr(c, '_unsafe_setemax'), -1) + self.assertRaises(ValueError, getattr(c, '_unsafe_setemax'), + 1070000001) + self.assertRaises(ValueError, getattr(c, '_unsafe_setemin'), + -1070000001) + self.assertRaises(ValueError, getattr(c, '_unsafe_setemin'), 1) + + # capitals, clamp + for attr in ['capitals', 'clamp']: + self.assertRaises(ValueError, setattr, c, attr, -1) + self.assertRaises(ValueError, setattr, c, attr, 2) + self.assertRaises(TypeError, setattr, c, attr, [1,2,3]) + if HAVE_CONFIG_64: + self.assertRaises(ValueError, setattr, c, attr, 2**32) + self.assertRaises(ValueError, setattr, c, attr, 2**32+1) + + # Invalid local context + self.assertRaises(TypeError, exec, 'with localcontext("xyz"): pass', + locals()) + self.assertRaises(TypeError, exec, + 'with localcontext(context=getcontext()): pass', + locals()) + + # setcontext + saved_context = getcontext() + self.assertRaises(TypeError, setcontext, "xyz") + setcontext(saved_context) + + def test_rounding_strings_interned(self): + self.assertIs(C.ROUND_UP, P.ROUND_UP) + self.assertIs(C.ROUND_DOWN, P.ROUND_DOWN) + self.assertIs(C.ROUND_CEILING, P.ROUND_CEILING) + self.assertIs(C.ROUND_FLOOR, P.ROUND_FLOOR) + self.assertIs(C.ROUND_HALF_UP, P.ROUND_HALF_UP) + self.assertIs(C.ROUND_HALF_DOWN, P.ROUND_HALF_DOWN) + self.assertIs(C.ROUND_HALF_EVEN, P.ROUND_HALF_EVEN) + self.assertIs(C.ROUND_05UP, P.ROUND_05UP) + + @requires_extra_functionality + def test_c_context_errors_extra(self): + Context = C.Context + InvalidOperation = C.InvalidOperation + Overflow = C.Overflow + localcontext = C.localcontext + getcontext = C.getcontext + setcontext = C.setcontext + HAVE_CONFIG_64 = (C.MAX_PREC > 425000000) + + c = Context() + + # Input corner cases + int_max = 2**63-1 if HAVE_CONFIG_64 else 2**31-1 + + # OverflowError, general ValueError + self.assertRaises(OverflowError, setattr, c, '_allcr', int_max+1) + self.assertRaises(OverflowError, setattr, c, '_allcr', -int_max-2) + if sys.platform != 'win32': + self.assertRaises(ValueError, setattr, c, '_allcr', int_max) + self.assertRaises(ValueError, setattr, c, '_allcr', -int_max-1) + + # OverflowError, general TypeError + for attr in ('_flags', '_traps'): + self.assertRaises(OverflowError, setattr, c, attr, int_max+1) + self.assertRaises(OverflowError, setattr, c, attr, -int_max-2) + if sys.platform != 'win32': + self.assertRaises(TypeError, setattr, c, attr, int_max) + self.assertRaises(TypeError, setattr, c, attr, -int_max-1) + + # _allcr + self.assertRaises(ValueError, setattr, c, '_allcr', -1) + self.assertRaises(ValueError, setattr, c, '_allcr', 2) + self.assertRaises(TypeError, setattr, c, '_allcr', [1,2,3]) + if HAVE_CONFIG_64: + self.assertRaises(ValueError, setattr, c, '_allcr', 2**32) + self.assertRaises(ValueError, setattr, c, '_allcr', 2**32+1) + + # _flags, _traps + for attr in ['_flags', '_traps']: + self.assertRaises(TypeError, setattr, c, attr, 999999) + self.assertRaises(TypeError, setattr, c, attr, 'x') + + def test_c_valid_context(self): + # These tests are for code coverage in _decimal. + DefaultContext = C.DefaultContext + Clamped = C.Clamped + Underflow = C.Underflow + Inexact = C.Inexact + Rounded = C.Rounded + Subnormal = C.Subnormal + + c = DefaultContext.copy() + + # Exercise all getters and setters + c.prec = 34 + c.rounding = P.ROUND_HALF_UP + c.Emax = 3000 + c.Emin = -3000 + c.capitals = 1 + c.clamp = 0 + + self.assertEqual(c.prec, 34) + self.assertEqual(c.rounding, P.ROUND_HALF_UP) + self.assertEqual(c.Emin, -3000) + self.assertEqual(c.Emax, 3000) + self.assertEqual(c.capitals, 1) + self.assertEqual(c.clamp, 0) + + self.assertEqual(c.Etiny(), -3033) + self.assertEqual(c.Etop(), 2967) + + # Exercise all unsafe setters + if C.MAX_PREC == 425000000: + c._unsafe_setprec(999999999) + c._unsafe_setemax(999999999) + c._unsafe_setemin(-999999999) + self.assertEqual(c.prec, 999999999) + self.assertEqual(c.Emax, 999999999) + self.assertEqual(c.Emin, -999999999) + + @requires_extra_functionality + def test_c_valid_context_extra(self): + DefaultContext = C.DefaultContext + + c = DefaultContext.copy() + self.assertEqual(c._allcr, 1) + c._allcr = 0 + self.assertEqual(c._allcr, 0) + + def test_c_round(self): + # Restricted input. + Decimal = C.Decimal + InvalidOperation = C.InvalidOperation + localcontext = C.localcontext + MAX_EMAX = C.MAX_EMAX + MIN_ETINY = C.MIN_ETINY + int_max = 2**63-1 if C.MAX_PREC > 425000000 else 2**31-1 + + with localcontext() as c: + c.traps[InvalidOperation] = True + self.assertRaises(InvalidOperation, Decimal("1.23").__round__, + -int_max-1) + self.assertRaises(InvalidOperation, Decimal("1.23").__round__, + int_max) + self.assertRaises(InvalidOperation, Decimal("1").__round__, + int(MAX_EMAX+1)) + self.assertRaises(C.InvalidOperation, Decimal("1").__round__, + -int(MIN_ETINY-1)) + self.assertRaises(OverflowError, Decimal("1.23").__round__, + -int_max-2) + self.assertRaises(OverflowError, Decimal("1.23").__round__, + int_max+1) + + def test_c_format(self): + # Restricted input + Decimal = C.Decimal + HAVE_CONFIG_64 = (C.MAX_PREC > 425000000) + + self.assertRaises(TypeError, Decimal(1).__format__, "=10.10", [], 9) + self.assertRaises(TypeError, Decimal(1).__format__, "=10.10", 9) + self.assertRaises(TypeError, Decimal(1).__format__, []) + + self.assertRaises(ValueError, Decimal(1).__format__, "<>=10.10") + maxsize = 2**63-1 if HAVE_CONFIG_64 else 2**31-1 + self.assertRaises(ValueError, Decimal("1.23456789").__format__, + "=%d.1" % maxsize) + + def test_c_integral(self): + Decimal = C.Decimal + Inexact = C.Inexact + localcontext = C.localcontext + + x = Decimal(10) + self.assertEqual(x.to_integral(), 10) + self.assertRaises(TypeError, x.to_integral, '10') + self.assertRaises(TypeError, x.to_integral, 10, 'x') + self.assertRaises(TypeError, x.to_integral, 10) + + self.assertEqual(x.to_integral_value(), 10) + self.assertRaises(TypeError, x.to_integral_value, '10') + self.assertRaises(TypeError, x.to_integral_value, 10, 'x') + self.assertRaises(TypeError, x.to_integral_value, 10) + + self.assertEqual(x.to_integral_exact(), 10) + self.assertRaises(TypeError, x.to_integral_exact, '10') + self.assertRaises(TypeError, x.to_integral_exact, 10, 'x') + self.assertRaises(TypeError, x.to_integral_exact, 10) + + with localcontext() as c: + x = Decimal("99999999999999999999999999.9").to_integral_value(P.ROUND_UP) + self.assertEqual(x, Decimal('100000000000000000000000000')) + + x = Decimal("99999999999999999999999999.9").to_integral_exact(P.ROUND_UP) + self.assertEqual(x, Decimal('100000000000000000000000000')) + + c.traps[Inexact] = True + self.assertRaises(Inexact, Decimal("999.9").to_integral_exact, P.ROUND_UP) + + def test_c_funcs(self): + # Invalid arguments + Decimal = C.Decimal + InvalidOperation = C.InvalidOperation + DivisionByZero = C.DivisionByZero + getcontext = C.getcontext + localcontext = C.localcontext + + self.assertEqual(Decimal('9.99e10').to_eng_string(), '99.9E+9') + + self.assertRaises(TypeError, pow, Decimal(1), 2, "3") + self.assertRaises(TypeError, Decimal(9).number_class, "x", "y") + self.assertRaises(TypeError, Decimal(9).same_quantum, 3, "x", "y") + + self.assertRaises( + TypeError, + Decimal("1.23456789").quantize, Decimal('1e-100000'), [] + ) + self.assertRaises( + TypeError, + Decimal("1.23456789").quantize, Decimal('1e-100000'), getcontext() + ) + self.assertRaises( + TypeError, + Decimal("1.23456789").quantize, Decimal('1e-100000'), 10 + ) + self.assertRaises( + TypeError, + Decimal("1.23456789").quantize, Decimal('1e-100000'), P.ROUND_UP, 1000 + ) + + with localcontext() as c: + c.clear_traps() + + # Invalid arguments + self.assertRaises(TypeError, c.copy_sign, Decimal(1), "x", "y") + self.assertRaises(TypeError, c.canonical, 200) + self.assertRaises(TypeError, c.is_canonical, 200) + self.assertRaises(TypeError, c.divmod, 9, 8, "x", "y") + self.assertRaises(TypeError, c.same_quantum, 9, 3, "x", "y") + + self.assertEqual(str(c.canonical(Decimal(200))), '200') + self.assertEqual(c.radix(), 10) + + c.traps[DivisionByZero] = True + self.assertRaises(DivisionByZero, Decimal(9).__divmod__, 0) + self.assertRaises(DivisionByZero, c.divmod, 9, 0) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + c.traps[InvalidOperation] = True + self.assertRaises(InvalidOperation, Decimal(9).__divmod__, 0) + self.assertRaises(InvalidOperation, c.divmod, 9, 0) + self.assertTrue(c.flags[DivisionByZero]) + + c.traps[InvalidOperation] = True + c.prec = 2 + self.assertRaises(InvalidOperation, pow, Decimal(1000), 1, 501) + + def test_va_args_exceptions(self): + Decimal = C.Decimal + Context = C.Context + + x = Decimal("10001111111") + + for attr in ['exp', 'is_normal', 'is_subnormal', 'ln', 'log10', + 'logb', 'logical_invert', 'next_minus', 'next_plus', + 'normalize', 'number_class', 'sqrt', 'to_eng_string']: + func = getattr(x, attr) + self.assertRaises(TypeError, func, context="x") + self.assertRaises(TypeError, func, "x", context=None) + + for attr in ['compare', 'compare_signal', 'logical_and', + 'logical_or', 'max', 'max_mag', 'min', 'min_mag', + 'remainder_near', 'rotate', 'scaleb', 'shift']: + func = getattr(x, attr) + self.assertRaises(TypeError, func, context="x") + self.assertRaises(TypeError, func, "x", context=None) + + self.assertRaises(TypeError, x.to_integral, rounding=None, context=[]) + self.assertRaises(TypeError, x.to_integral, rounding={}, context=[]) + self.assertRaises(TypeError, x.to_integral, [], []) + + self.assertRaises(TypeError, x.to_integral_value, rounding=None, context=[]) + self.assertRaises(TypeError, x.to_integral_value, rounding={}, context=[]) + self.assertRaises(TypeError, x.to_integral_value, [], []) + + self.assertRaises(TypeError, x.to_integral_exact, rounding=None, context=[]) + self.assertRaises(TypeError, x.to_integral_exact, rounding={}, context=[]) + self.assertRaises(TypeError, x.to_integral_exact, [], []) + + self.assertRaises(TypeError, x.fma, 1, 2, context="x") + self.assertRaises(TypeError, x.fma, 1, 2, "x", context=None) + + self.assertRaises(TypeError, x.quantize, 1, [], context=None) + self.assertRaises(TypeError, x.quantize, 1, [], rounding=None) + self.assertRaises(TypeError, x.quantize, 1, [], []) + + c = Context() + self.assertRaises(TypeError, c.power, 1, 2, mod="x") + self.assertRaises(TypeError, c.power, 1, "x", mod=None) + self.assertRaises(TypeError, c.power, "x", 2, mod=None) + + @requires_extra_functionality + def test_c_context_templates(self): + self.assertEqual( + C.BasicContext._traps, + C.DecIEEEInvalidOperation|C.DecDivisionByZero|C.DecOverflow| + C.DecUnderflow|C.DecClamped + ) + self.assertEqual( + C.DefaultContext._traps, + C.DecIEEEInvalidOperation|C.DecDivisionByZero|C.DecOverflow + ) + + @requires_extra_functionality + def test_c_signal_dict(self): + + # SignalDict coverage + Context = C.Context + DefaultContext = C.DefaultContext + + InvalidOperation = C.InvalidOperation + FloatOperation = C.FloatOperation + DivisionByZero = C.DivisionByZero + Overflow = C.Overflow + Subnormal = C.Subnormal + Underflow = C.Underflow + Rounded = C.Rounded + Inexact = C.Inexact + Clamped = C.Clamped + + DecClamped = C.DecClamped + DecInvalidOperation = C.DecInvalidOperation + DecIEEEInvalidOperation = C.DecIEEEInvalidOperation + + def assertIsExclusivelySet(signal, signal_dict): + for sig in signal_dict: + if sig == signal: + self.assertTrue(signal_dict[sig]) + else: + self.assertFalse(signal_dict[sig]) + + c = DefaultContext.copy() + + # Signal dict methods + self.assertTrue(Overflow in c.traps) + c.clear_traps() + for k in c.traps.keys(): + c.traps[k] = True + for v in c.traps.values(): + self.assertTrue(v) + c.clear_traps() + for k, v in c.traps.items(): + self.assertFalse(v) + + self.assertFalse(c.flags.get(Overflow)) + self.assertIs(c.flags.get("x"), None) + self.assertEqual(c.flags.get("x", "y"), "y") + self.assertRaises(TypeError, c.flags.get, "x", "y", "z") + + self.assertEqual(len(c.flags), len(c.traps)) + s = sys.getsizeof(c.flags) + s = sys.getsizeof(c.traps) + s = c.flags.__repr__() + + # Set flags/traps. + c.clear_flags() + c._flags = DecClamped + self.assertTrue(c.flags[Clamped]) + + c.clear_traps() + c._traps = DecInvalidOperation + self.assertTrue(c.traps[InvalidOperation]) + + # Set flags/traps from dictionary. + c.clear_flags() + d = c.flags.copy() + d[DivisionByZero] = True + c.flags = d + assertIsExclusivelySet(DivisionByZero, c.flags) + + c.clear_traps() + d = c.traps.copy() + d[Underflow] = True + c.traps = d + assertIsExclusivelySet(Underflow, c.traps) + + # Random constructors + IntSignals = { + Clamped: C.DecClamped, + Rounded: C.DecRounded, + Inexact: C.DecInexact, + Subnormal: C.DecSubnormal, + Underflow: C.DecUnderflow, + Overflow: C.DecOverflow, + DivisionByZero: C.DecDivisionByZero, + FloatOperation: C.DecFloatOperation, + InvalidOperation: C.DecIEEEInvalidOperation + } + IntCond = [ + C.DecDivisionImpossible, C.DecDivisionUndefined, C.DecFpuError, + C.DecInvalidContext, C.DecInvalidOperation, C.DecMallocError, + C.DecConversionSyntax, + ] + + lim = len(OrderedSignals[C]) + for r in range(lim): + for t in range(lim): + for round in RoundingModes: + flags = random.sample(OrderedSignals[C], r) + traps = random.sample(OrderedSignals[C], t) + prec = random.randrange(1, 10000) + emin = random.randrange(-10000, 0) + emax = random.randrange(0, 10000) + clamp = random.randrange(0, 2) + caps = random.randrange(0, 2) + cr = random.randrange(0, 2) + c = Context(prec=prec, rounding=round, Emin=emin, Emax=emax, + capitals=caps, clamp=clamp, flags=list(flags), + traps=list(traps)) + + self.assertEqual(c.prec, prec) + self.assertEqual(c.rounding, round) + self.assertEqual(c.Emin, emin) + self.assertEqual(c.Emax, emax) + self.assertEqual(c.capitals, caps) + self.assertEqual(c.clamp, clamp) + + f = 0 + for x in flags: + f |= IntSignals[x] + self.assertEqual(c._flags, f) + + f = 0 + for x in traps: + f |= IntSignals[x] + self.assertEqual(c._traps, f) + + for cond in IntCond: + c._flags = cond + self.assertTrue(c._flags&DecIEEEInvalidOperation) + assertIsExclusivelySet(InvalidOperation, c.flags) + + for cond in IntCond: + c._traps = cond + self.assertTrue(c._traps&DecIEEEInvalidOperation) + assertIsExclusivelySet(InvalidOperation, c.traps) + + def test_invalid_override(self): + Decimal = C.Decimal + + try: + from locale import CHAR_MAX + except ImportError: + self.skipTest('locale.CHAR_MAX not available') + + def make_grouping(lst): + return ''.join([chr(x) for x in lst]) + + def get_fmt(x, override=None, fmt='n'): + return Decimal(x).__format__(fmt, override) + + invalid_grouping = { + 'decimal_point' : ',', + 'grouping' : make_grouping([255, 255, 0]), + 'thousands_sep' : ',' + } + invalid_dot = { + 'decimal_point' : 'xxxxx', + 'grouping' : make_grouping([3, 3, 0]), + 'thousands_sep' : ',' + } + invalid_sep = { + 'decimal_point' : '.', + 'grouping' : make_grouping([3, 3, 0]), + 'thousands_sep' : 'yyyyy' + } + + if CHAR_MAX == 127: # negative grouping in override + self.assertRaises(ValueError, get_fmt, 12345, + invalid_grouping, 'g') + + self.assertRaises(ValueError, get_fmt, 12345, invalid_dot, 'g') + self.assertRaises(ValueError, get_fmt, 12345, invalid_sep, 'g') + + def test_exact_conversion(self): + Decimal = C.Decimal + localcontext = C.localcontext + InvalidOperation = C.InvalidOperation + + with localcontext() as c: + + c.traps[InvalidOperation] = True + + # Clamped + x = "0e%d" % sys.maxsize + self.assertRaises(InvalidOperation, Decimal, x) + + x = "0e%d" % (-sys.maxsize-1) + self.assertRaises(InvalidOperation, Decimal, x) + + # Overflow + x = "1e%d" % sys.maxsize + self.assertRaises(InvalidOperation, Decimal, x) + + # Underflow + x = "1e%d" % (-sys.maxsize-1) + self.assertRaises(InvalidOperation, Decimal, x) + + def test_from_tuple(self): + Decimal = C.Decimal + localcontext = C.localcontext + InvalidOperation = C.InvalidOperation + Overflow = C.Overflow + Underflow = C.Underflow + + with localcontext() as c: + + c.prec = 9 + c.traps[InvalidOperation] = True + c.traps[Overflow] = True + c.traps[Underflow] = True + + # SSIZE_MAX + x = (1, (), sys.maxsize) + self.assertEqual(str(c.create_decimal(x)), '-0E+999999') + self.assertRaises(InvalidOperation, Decimal, x) + + x = (1, (0, 1, 2), sys.maxsize) + self.assertRaises(Overflow, c.create_decimal, x) + self.assertRaises(InvalidOperation, Decimal, x) + + # SSIZE_MIN + x = (1, (), -sys.maxsize-1) + self.assertEqual(str(c.create_decimal(x)), '-0E-1000007') + self.assertRaises(InvalidOperation, Decimal, x) + + x = (1, (0, 1, 2), -sys.maxsize-1) + self.assertRaises(Underflow, c.create_decimal, x) + self.assertRaises(InvalidOperation, Decimal, x) + + # OverflowError + x = (1, (), sys.maxsize+1) + self.assertRaises(OverflowError, c.create_decimal, x) + self.assertRaises(OverflowError, Decimal, x) + + x = (1, (), -sys.maxsize-2) + self.assertRaises(OverflowError, c.create_decimal, x) + self.assertRaises(OverflowError, Decimal, x) + + # Specials + x = (1, (), "N") + self.assertEqual(str(Decimal(x)), '-sNaN') + x = (1, (0,), "N") + self.assertEqual(str(Decimal(x)), '-sNaN') + x = (1, (0, 1), "N") + self.assertEqual(str(Decimal(x)), '-sNaN1') + + def test_sizeof(self): + Decimal = C.Decimal + HAVE_CONFIG_64 = (C.MAX_PREC > 425000000) + + self.assertGreater(Decimal(0).__sizeof__(), 0) + if HAVE_CONFIG_64: + x = Decimal(10**(19*24)).__sizeof__() + y = Decimal(10**(19*25)).__sizeof__() + self.assertEqual(y, x+8) + else: + x = Decimal(10**(9*24)).__sizeof__() + y = Decimal(10**(9*25)).__sizeof__() + self.assertEqual(y, x+4) + + def test_internal_use_of_overridden_methods(self): + Decimal = C.Decimal + + # Unsound subtyping + class X(float): + def as_integer_ratio(self): + return 1 + def __abs__(self): + return self + + class Y(float): + def __abs__(self): + return [1]*200 + + class I(int): + def bit_length(self): + return [1]*200 + + class Z(float): + def as_integer_ratio(self): + return (I(1), I(1)) + def __abs__(self): + return self + + for cls in X, Y, Z: + self.assertEqual(Decimal.from_float(cls(101.1)), + Decimal.from_float(101.1)) + + def test_c_immutable_types(self): + SignalDict = type(C.Context().flags) + SignalDictMixin = SignalDict.__bases__[0] + ContextManager = type(C.localcontext()) + types = ( + SignalDictMixin, + ContextManager, + C.Decimal, + C.Context, + ) + for tp in types: + with self.subTest(tp=tp): + with self.assertRaisesRegex(TypeError, "immutable"): + tp.foo = 1 + + def test_c_disallow_instantiation(self): + ContextManager = type(C.localcontext()) + check_disallow_instantiation(self, ContextManager) + + def test_c_signaldict_segfault(self): + # See gh-106263 for details. + SignalDict = type(C.Context().flags) + sd = SignalDict() + err_msg = "invalid signal dict" + + with self.assertRaisesRegex(ValueError, err_msg): + len(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + iter(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + repr(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + sd[C.InvalidOperation] = True + + with self.assertRaisesRegex(ValueError, err_msg): + sd[C.InvalidOperation] + + with self.assertRaisesRegex(ValueError, err_msg): + sd == C.Context().flags + + with self.assertRaisesRegex(ValueError, err_msg): + C.Context().flags == sd + + with self.assertRaisesRegex(ValueError, err_msg): + sd.copy() + + def test_format_fallback_capitals(self): + # Fallback to _pydecimal formatting (triggered by `#` format which + # is unsupported by mpdecimal) should honor the current context. + x = C.Decimal('6.09e+23') + self.assertEqual(format(x, '#'), '6.09E+23') + with C.localcontext(capitals=0): + self.assertEqual(format(x, '#'), '6.09e+23') + + def test_format_fallback_rounding(self): + y = C.Decimal('6.09') + self.assertEqual(format(y, '#.1f'), '6.1') + with C.localcontext(rounding=C.ROUND_DOWN): + self.assertEqual(format(y, '#.1f'), '6.0') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_construction.py b/Lib/test/test_decimal/test_construction.py new file mode 100644 index 00000000000000..b35984a52c2e62 --- /dev/null +++ b/Lib/test/test_decimal/test_construction.py @@ -0,0 +1,446 @@ +import unittest +import copy +import random +from test.support import requires_IEEE_754 +from . import (load_tests_for_base_classes, + setUpModule, tearDownModule) + +# The following classes test the behaviour of Decimal according to PEP 327 + +class ExplicitConstructionTest: + '''Unit tests for Explicit Construction cases of Decimal.''' + + def test_explicit_empty(self): + Decimal = self.decimal.Decimal + self.assertEqual(Decimal(), Decimal("0")) + + def test_explicit_from_None(self): + Decimal = self.decimal.Decimal + self.assertRaises(TypeError, Decimal, None) + + def test_explicit_from_int(self): + Decimal = self.decimal.Decimal + + #positive + d = Decimal(45) + self.assertEqual(str(d), '45') + + #very large positive + d = Decimal(500000123) + self.assertEqual(str(d), '500000123') + + #negative + d = Decimal(-45) + self.assertEqual(str(d), '-45') + + #zero + d = Decimal(0) + self.assertEqual(str(d), '0') + + # single word longs + for n in range(0, 32): + for sign in (-1, 1): + for x in range(-5, 5): + i = sign * (2**n + x) + d = Decimal(i) + self.assertEqual(str(d), str(i)) + + def test_explicit_from_string(self): + Decimal = self.decimal.Decimal + InvalidOperation = self.decimal.InvalidOperation + localcontext = self.decimal.localcontext + + #empty + self.assertEqual(str(Decimal('')), 'NaN') + + #int + self.assertEqual(str(Decimal('45')), '45') + + #float + self.assertEqual(str(Decimal('45.34')), '45.34') + + #engineer notation + self.assertEqual(str(Decimal('45e2')), '4.5E+3') + + #just not a number + self.assertEqual(str(Decimal('ugly')), 'NaN') + + #leading and trailing whitespace permitted + self.assertEqual(str(Decimal('1.3E4 \n')), '1.3E+4') + self.assertEqual(str(Decimal(' -7.89')), '-7.89') + self.assertEqual(str(Decimal(" 3.45679 ")), '3.45679') + + # underscores + self.assertEqual(str(Decimal('1_3.3e4_0')), '1.33E+41') + self.assertEqual(str(Decimal('1_0_0_0')), '1000') + + # unicode whitespace + for lead in ["", ' ', '\u00a0', '\u205f']: + for trail in ["", ' ', '\u00a0', '\u205f']: + self.assertEqual(str(Decimal(lead + '9.311E+28' + trail)), + '9.311E+28') + + with localcontext() as c: + c.traps[InvalidOperation] = True + # Invalid string + self.assertRaises(InvalidOperation, Decimal, "xyz") + # Two arguments max + self.assertRaises(TypeError, Decimal, "1234", "x", "y") + + # space within the numeric part + self.assertRaises(InvalidOperation, Decimal, "1\u00a02\u00a03") + self.assertRaises(InvalidOperation, Decimal, "\u00a01\u00a02\u00a0") + + # unicode whitespace + self.assertRaises(InvalidOperation, Decimal, "\u00a0") + self.assertRaises(InvalidOperation, Decimal, "\u00a0\u00a0") + + # embedded NUL + self.assertRaises(InvalidOperation, Decimal, "12\u00003") + + # underscores don't prevent errors + self.assertRaises(InvalidOperation, Decimal, "1_2_\u00003") + + def test_explicit_from_tuples(self): + Decimal = self.decimal.Decimal + + #zero + d = Decimal( (0, (0,), 0) ) + self.assertEqual(str(d), '0') + + #int + d = Decimal( (1, (4, 5), 0) ) + self.assertEqual(str(d), '-45') + + #float + d = Decimal( (0, (4, 5, 3, 4), -2) ) + self.assertEqual(str(d), '45.34') + + #weird + d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) + self.assertEqual(str(d), '-4.34913534E-17') + + #inf + d = Decimal( (0, (), "F") ) + self.assertEqual(str(d), 'Infinity') + + #wrong number of items + self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) ) + + #bad sign + self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) ) + self.assertRaises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) ) + self.assertRaises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2)) + + #bad exp + self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') ) + self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) ) + self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') ) + + #bad coefficients + self.assertRaises(ValueError, Decimal, (1, "xyz", 2) ) + self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) ) + self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) ) + self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) ) + self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) ) + + def test_explicit_from_list(self): + Decimal = self.decimal.Decimal + + d = Decimal([0, [0], 0]) + self.assertEqual(str(d), '0') + + d = Decimal([1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25]) + self.assertEqual(str(d), '-4.34913534E-17') + + d = Decimal([1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25]) + self.assertEqual(str(d), '-4.34913534E-17') + + d = Decimal((1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25)) + self.assertEqual(str(d), '-4.34913534E-17') + + def test_explicit_from_bool(self): + Decimal = self.decimal.Decimal + + self.assertIs(bool(Decimal(0)), False) + self.assertIs(bool(Decimal(1)), True) + self.assertEqual(Decimal(False), Decimal(0)) + self.assertEqual(Decimal(True), Decimal(1)) + + def test_explicit_from_Decimal(self): + Decimal = self.decimal.Decimal + + #positive + d = Decimal(45) + e = Decimal(d) + self.assertEqual(str(e), '45') + + #very large positive + d = Decimal(500000123) + e = Decimal(d) + self.assertEqual(str(e), '500000123') + + #negative + d = Decimal(-45) + e = Decimal(d) + self.assertEqual(str(e), '-45') + + #zero + d = Decimal(0) + e = Decimal(d) + self.assertEqual(str(e), '0') + + @requires_IEEE_754 + def test_explicit_from_float(self): + Decimal = self.decimal.Decimal + + r = Decimal(0.1) + self.assertEqual(type(r), Decimal) + self.assertEqual(str(r), + '0.1000000000000000055511151231257827021181583404541015625') + self.assertTrue(Decimal(float('nan')).is_qnan()) + self.assertTrue(Decimal(float('inf')).is_infinite()) + self.assertTrue(Decimal(float('-inf')).is_infinite()) + self.assertEqual(str(Decimal(float('nan'))), + str(Decimal('NaN'))) + self.assertEqual(str(Decimal(float('inf'))), + str(Decimal('Infinity'))) + self.assertEqual(str(Decimal(float('-inf'))), + str(Decimal('-Infinity'))) + self.assertEqual(str(Decimal(float('-0.0'))), + str(Decimal('-0'))) + for i in range(200): + x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) + self.assertEqual(x, float(Decimal(x))) # roundtrip + + def test_explicit_context_create_decimal(self): + Decimal = self.decimal.Decimal + InvalidOperation = self.decimal.InvalidOperation + Rounded = self.decimal.Rounded + + nc = copy.copy(self.decimal.getcontext()) + nc.prec = 3 + + # empty + d = Decimal() + self.assertEqual(str(d), '0') + d = nc.create_decimal() + self.assertEqual(str(d), '0') + + # from None + self.assertRaises(TypeError, nc.create_decimal, None) + + # from int + d = nc.create_decimal(456) + self.assertIsInstance(d, Decimal) + self.assertEqual(nc.create_decimal(45678), + nc.create_decimal('457E+2')) + + # from string + d = Decimal('456789') + self.assertEqual(str(d), '456789') + d = nc.create_decimal('456789') + self.assertEqual(str(d), '4.57E+5') + # leading and trailing whitespace should result in a NaN; + # spaces are already checked in Cowlishaw's test-suite, so + # here we just check that a trailing newline results in a NaN + self.assertEqual(str(nc.create_decimal('3.14\n')), 'NaN') + + # from tuples + d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) + self.assertEqual(str(d), '-4.34913534E-17') + d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) + self.assertEqual(str(d), '-4.35E-17') + + # from Decimal + prevdec = Decimal(500000123) + d = Decimal(prevdec) + self.assertEqual(str(d), '500000123') + d = nc.create_decimal(prevdec) + self.assertEqual(str(d), '5.00E+8') + + # more integers + nc.prec = 28 + nc.traps[InvalidOperation] = True + + for v in [-2**63-1, -2**63, -2**31-1, -2**31, 0, + 2**31-1, 2**31, 2**63-1, 2**63]: + d = nc.create_decimal(v) + self.assertIsInstance(d, Decimal) + self.assertEqual(int(d), v) + + nc.prec = 3 + nc.traps[Rounded] = True + self.assertRaises(Rounded, nc.create_decimal, 1234) + + # from string + nc.prec = 28 + self.assertEqual(str(nc.create_decimal('0E-017')), '0E-17') + self.assertEqual(str(nc.create_decimal('45')), '45') + self.assertEqual(str(nc.create_decimal('-Inf')), '-Infinity') + self.assertEqual(str(nc.create_decimal('NaN123')), 'NaN123') + + # invalid arguments + self.assertRaises(InvalidOperation, nc.create_decimal, "xyz") + self.assertRaises(ValueError, nc.create_decimal, (1, "xyz", -25)) + self.assertRaises(TypeError, nc.create_decimal, "1234", "5678") + # no whitespace and underscore stripping is done with this method + self.assertRaises(InvalidOperation, nc.create_decimal, " 1234") + self.assertRaises(InvalidOperation, nc.create_decimal, "12_34") + + # too many NaN payload digits + nc.prec = 3 + self.assertRaises(InvalidOperation, nc.create_decimal, 'NaN12345') + self.assertRaises(InvalidOperation, nc.create_decimal, + Decimal('NaN12345')) + + nc.traps[InvalidOperation] = False + self.assertEqual(str(nc.create_decimal('NaN12345')), 'NaN') + self.assertTrue(nc.flags[InvalidOperation]) + + nc.flags[InvalidOperation] = False + self.assertEqual(str(nc.create_decimal(Decimal('NaN12345'))), 'NaN') + self.assertTrue(nc.flags[InvalidOperation]) + + def test_explicit_context_create_from_float(self): + Decimal = self.decimal.Decimal + + nc = self.decimal.Context() + r = nc.create_decimal(0.1) + self.assertEqual(type(r), Decimal) + self.assertEqual(str(r), '0.1000000000000000055511151231') + self.assertTrue(nc.create_decimal(float('nan')).is_qnan()) + self.assertTrue(nc.create_decimal(float('inf')).is_infinite()) + self.assertTrue(nc.create_decimal(float('-inf')).is_infinite()) + self.assertEqual(str(nc.create_decimal(float('nan'))), + str(nc.create_decimal('NaN'))) + self.assertEqual(str(nc.create_decimal(float('inf'))), + str(nc.create_decimal('Infinity'))) + self.assertEqual(str(nc.create_decimal(float('-inf'))), + str(nc.create_decimal('-Infinity'))) + self.assertEqual(str(nc.create_decimal(float('-0.0'))), + str(nc.create_decimal('-0'))) + nc.prec = 100 + for i in range(200): + x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) + self.assertEqual(x, float(nc.create_decimal(x))) # roundtrip + + def test_from_number(self, cls=None): + Decimal = self.decimal.Decimal + if cls is None: + cls = Decimal + + def check(arg, expected): + d = cls.from_number(arg) + self.assertIs(type(d), cls) + self.assertEqual(d, expected) + + check(314, Decimal(314)) + check(3.14, Decimal.from_float(3.14)) + check(Decimal('3.14'), Decimal('3.14')) + self.assertRaises(TypeError, cls.from_number, 3+4j) + self.assertRaises(TypeError, cls.from_number, '314') + self.assertRaises(TypeError, cls.from_number, (0, (3, 1, 4), 0)) + self.assertRaises(TypeError, cls.from_number, object()) + + def test_from_number_subclass(self, cls=None): + class DecimalSubclass(self.decimal.Decimal): + pass + self.test_from_number(DecimalSubclass) + + def test_unicode_digits(self): + Decimal = self.decimal.Decimal + + test_values = { + '\uff11': '1', + '\u0660.\u0660\u0663\u0667\u0662e-\u0663' : '0.0000372', + '-nan\u0c68\u0c6a\u0c66\u0c66' : '-NaN2400', + } + for input, expected in test_values.items(): + self.assertEqual(str(Decimal(input)), expected) + +class ImplicitConstructionTest: + '''Unit tests for Implicit Construction cases of Decimal.''' + + def test_implicit_from_None(self): + Decimal = self.decimal.Decimal + self.assertRaises(TypeError, eval, 'Decimal(5) + None', locals()) + + def test_implicit_from_int(self): + Decimal = self.decimal.Decimal + + #normal + self.assertEqual(str(Decimal(5) + 45), '50') + #exceeding precision + self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000)) + + def test_implicit_from_string(self): + Decimal = self.decimal.Decimal + self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', locals()) + + def test_implicit_from_float(self): + Decimal = self.decimal.Decimal + self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', locals()) + + def test_implicit_from_Decimal(self): + Decimal = self.decimal.Decimal + self.assertEqual(Decimal(5) + Decimal(45), Decimal(50)) + + def test_rop(self): + Decimal = self.decimal.Decimal + + # Allow other classes to be trained to interact with Decimals + class E: + def __divmod__(self, other): + return 'divmod ' + str(other) + def __rdivmod__(self, other): + return str(other) + ' rdivmod' + def __lt__(self, other): + return 'lt ' + str(other) + def __gt__(self, other): + return 'gt ' + str(other) + def __le__(self, other): + return 'le ' + str(other) + def __ge__(self, other): + return 'ge ' + str(other) + def __eq__(self, other): + return 'eq ' + str(other) + def __ne__(self, other): + return 'ne ' + str(other) + + self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10') + self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod') + self.assertEqual(eval('Decimal(10) < E()'), 'gt 10') + self.assertEqual(eval('Decimal(10) > E()'), 'lt 10') + self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10') + self.assertEqual(eval('Decimal(10) >= E()'), 'le 10') + self.assertEqual(eval('Decimal(10) == E()'), 'eq 10') + self.assertEqual(eval('Decimal(10) != E()'), 'ne 10') + + # insert operator methods and then exercise them + oplist = [ + ('+', '__add__', '__radd__'), + ('-', '__sub__', '__rsub__'), + ('*', '__mul__', '__rmul__'), + ('/', '__truediv__', '__rtruediv__'), + ('%', '__mod__', '__rmod__'), + ('//', '__floordiv__', '__rfloordiv__'), + ('**', '__pow__', '__rpow__') + ] + + for sym, lop, rop in oplist: + setattr(E, lop, lambda self, other: 'str' + lop + str(other)) + setattr(E, rop, lambda self, other: str(other) + rop + 'str') + self.assertEqual(eval('E()' + sym + 'Decimal(10)'), + 'str' + lop + '10') + self.assertEqual(eval('Decimal(10)' + sym + 'E()'), + '10' + rop + 'str') + + +def load_tests(loader, tests, pattern): + base_classes = [ExplicitConstructionTest, ImplicitConstructionTest] + return load_tests_for_base_classes(loader, tests, base_classes) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_contexts.py b/Lib/test/test_decimal/test_contexts.py new file mode 100644 index 00000000000000..3d8eddc321c09d --- /dev/null +++ b/Lib/test/test_decimal/test_contexts.py @@ -0,0 +1,747 @@ +import unittest +from test.support import requires_IEEE_754 +from . import (C, P, load_tests_for_base_classes, + assert_signals, OrderedSignals, + setUpModule, tearDownModule) + + +class ContextWithStatement: + # Can't do these as docstrings until Python 2.6 + # as doctest can't handle __future__ statements + + def test_localcontext(self): + # Use a copy of the current context in the block + getcontext = self.decimal.getcontext + localcontext = self.decimal.localcontext + + orig_ctx = getcontext() + with localcontext() as enter_ctx: + set_ctx = getcontext() + final_ctx = getcontext() + self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') + self.assertIsNot(orig_ctx, set_ctx, 'did not copy the context') + self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context') + + def test_localcontextarg(self): + # Use a copy of the supplied context in the block + Context = self.decimal.Context + getcontext = self.decimal.getcontext + localcontext = self.decimal.localcontext + + orig_ctx = getcontext() + new_ctx = Context(prec=42) + with localcontext(new_ctx) as enter_ctx: + set_ctx = getcontext() + final_ctx = getcontext() + self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') + self.assertEqual(set_ctx.prec, new_ctx.prec, 'did not set correct context') + self.assertIsNot(new_ctx, set_ctx, 'did not copy the context') + self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context') + + def test_localcontext_kwargs(self): + with self.decimal.localcontext( + prec=10, rounding=P.ROUND_HALF_DOWN, + Emin=-20, Emax=20, capitals=0, + clamp=1 + ) as ctx: + self.assertEqual(ctx.prec, 10) + self.assertEqual(ctx.rounding, self.decimal.ROUND_HALF_DOWN) + self.assertEqual(ctx.Emin, -20) + self.assertEqual(ctx.Emax, 20) + self.assertEqual(ctx.capitals, 0) + self.assertEqual(ctx.clamp, 1) + + self.assertRaises(TypeError, self.decimal.localcontext, precision=10) + + self.assertRaises(ValueError, self.decimal.localcontext, Emin=1) + self.assertRaises(ValueError, self.decimal.localcontext, Emax=-1) + self.assertRaises(ValueError, self.decimal.localcontext, capitals=2) + self.assertRaises(ValueError, self.decimal.localcontext, clamp=2) + + self.assertRaises(TypeError, self.decimal.localcontext, rounding="") + self.assertRaises(TypeError, self.decimal.localcontext, rounding=1) + + self.assertRaises(TypeError, self.decimal.localcontext, flags="") + self.assertRaises(TypeError, self.decimal.localcontext, traps="") + self.assertRaises(TypeError, self.decimal.localcontext, Emin="") + self.assertRaises(TypeError, self.decimal.localcontext, Emax="") + + def test_local_context_kwargs_does_not_overwrite_existing_argument(self): + ctx = self.decimal.getcontext() + orig_prec = ctx.prec + with self.decimal.localcontext(prec=10) as ctx2: + self.assertEqual(ctx2.prec, 10) + self.assertEqual(ctx.prec, orig_prec) + with self.decimal.localcontext(prec=20) as ctx2: + self.assertEqual(ctx2.prec, 20) + self.assertEqual(ctx.prec, orig_prec) + + def test_nested_with_statements(self): + # Use a copy of the supplied context in the block + Decimal = self.decimal.Decimal + Context = self.decimal.Context + getcontext = self.decimal.getcontext + localcontext = self.decimal.localcontext + Clamped = self.decimal.Clamped + Overflow = self.decimal.Overflow + + orig_ctx = getcontext() + orig_ctx.clear_flags() + new_ctx = Context(Emax=384) + with localcontext() as c1: + self.assertEqual(c1.flags, orig_ctx.flags) + self.assertEqual(c1.traps, orig_ctx.traps) + c1.traps[Clamped] = True + c1.Emin = -383 + self.assertNotEqual(orig_ctx.Emin, -383) + self.assertRaises(Clamped, c1.create_decimal, '0e-999') + self.assertTrue(c1.flags[Clamped]) + with localcontext(new_ctx) as c2: + self.assertEqual(c2.flags, new_ctx.flags) + self.assertEqual(c2.traps, new_ctx.traps) + self.assertRaises(Overflow, c2.power, Decimal('3.4e200'), 2) + self.assertFalse(c2.flags[Clamped]) + self.assertTrue(c2.flags[Overflow]) + del c2 + self.assertFalse(c1.flags[Overflow]) + del c1 + self.assertNotEqual(orig_ctx.Emin, -383) + self.assertFalse(orig_ctx.flags[Clamped]) + self.assertFalse(orig_ctx.flags[Overflow]) + self.assertFalse(new_ctx.flags[Clamped]) + self.assertFalse(new_ctx.flags[Overflow]) + + def test_with_statements_gc1(self): + localcontext = self.decimal.localcontext + + with localcontext() as c1: + del c1 + with localcontext() as c2: + del c2 + with localcontext() as c3: + del c3 + with localcontext() as c4: + del c4 + + def test_with_statements_gc2(self): + localcontext = self.decimal.localcontext + + with localcontext() as c1: + with localcontext(c1) as c2: + del c1 + with localcontext(c2) as c3: + del c2 + with localcontext(c3) as c4: + del c3 + del c4 + + def test_with_statements_gc3(self): + Context = self.decimal.Context + localcontext = self.decimal.localcontext + getcontext = self.decimal.getcontext + setcontext = self.decimal.setcontext + + with localcontext() as c1: + del c1 + n1 = Context(prec=1) + setcontext(n1) + with localcontext(n1) as c2: + del n1 + self.assertEqual(c2.prec, 1) + del c2 + n2 = Context(prec=2) + setcontext(n2) + del n2 + self.assertEqual(getcontext().prec, 2) + n3 = Context(prec=3) + setcontext(n3) + self.assertEqual(getcontext().prec, 3) + with localcontext(n3) as c3: + del n3 + self.assertEqual(c3.prec, 3) + del c3 + n4 = Context(prec=4) + setcontext(n4) + del n4 + self.assertEqual(getcontext().prec, 4) + with localcontext() as c4: + self.assertEqual(c4.prec, 4) + del c4 + + +class ContextFlags: + + def test_flags_irrelevant(self): + # check that the result (numeric result + flags raised) of an + # arithmetic operation doesn't depend on the current flags + Decimal = self.decimal.Decimal + Context = self.decimal.Context + Inexact = self.decimal.Inexact + Rounded = self.decimal.Rounded + Underflow = self.decimal.Underflow + Clamped = self.decimal.Clamped + Subnormal = self.decimal.Subnormal + + def raise_error(context, flag): + if self.decimal == C: + context.flags[flag] = True + if context.traps[flag]: + raise flag + else: + context._raise_error(flag) + + context = Context(prec=9, Emin = -425000000, Emax = 425000000, + rounding=P.ROUND_HALF_EVEN, traps=[], flags=[]) + + # operations that raise various flags, in the form (function, arglist) + operations = [ + (context._apply, [Decimal("100E-425000010")]), + (context.sqrt, [Decimal(2)]), + (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]), + (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]), + (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]), + ] + + # try various flags individually, then a whole lot at once + flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal], + [Inexact, Rounded, Underflow, Clamped, Subnormal]] + + for fn, args in operations: + # find answer and flags raised using a clean context + context.clear_flags() + ans = fn(*args) + flags = [k for k, v in context.flags.items() if v] + + for extra_flags in flagsets: + # set flags, before calling operation + context.clear_flags() + for flag in extra_flags: + raise_error(context, flag) + new_ans = fn(*args) + + # flags that we expect to be set after the operation + expected_flags = list(flags) + for flag in extra_flags: + if flag not in expected_flags: + expected_flags.append(flag) + expected_flags.sort(key=id) + + # flags we actually got + new_flags = [k for k,v in context.flags.items() if v] + new_flags.sort(key=id) + + self.assertEqual(ans, new_ans, + "operation produces different answers depending on flags set: " + + "expected %s, got %s." % (ans, new_ans)) + self.assertEqual(new_flags, expected_flags, + "operation raises different flags depending on flags set: " + + "expected %s, got %s" % (expected_flags, new_flags)) + + def test_flag_comparisons(self): + Context = self.decimal.Context + Inexact = self.decimal.Inexact + Rounded = self.decimal.Rounded + + c = Context() + + # Valid SignalDict + self.assertNotEqual(c.flags, c.traps) + self.assertNotEqual(c.traps, c.flags) + + c.flags = c.traps + self.assertEqual(c.flags, c.traps) + self.assertEqual(c.traps, c.flags) + + c.flags[Rounded] = True + c.traps = c.flags + self.assertEqual(c.flags, c.traps) + self.assertEqual(c.traps, c.flags) + + d = {} + d.update(c.flags) + self.assertEqual(d, c.flags) + self.assertEqual(c.flags, d) + + d[Inexact] = True + self.assertNotEqual(d, c.flags) + self.assertNotEqual(c.flags, d) + + # Invalid SignalDict + d = {Inexact:False} + self.assertNotEqual(d, c.flags) + self.assertNotEqual(c.flags, d) + + d = ["xyz"] + self.assertNotEqual(d, c.flags) + self.assertNotEqual(c.flags, d) + + @requires_IEEE_754 + def test_float_operation(self): + Decimal = self.decimal.Decimal + FloatOperation = self.decimal.FloatOperation + localcontext = self.decimal.localcontext + + with localcontext() as c: + ##### trap is off by default + self.assertFalse(c.traps[FloatOperation]) + + # implicit conversion sets the flag + c.clear_flags() + self.assertEqual(Decimal(7.5), 7.5) + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + self.assertEqual(c.create_decimal(7.5), 7.5) + self.assertTrue(c.flags[FloatOperation]) + + # explicit conversion does not set the flag + c.clear_flags() + x = Decimal.from_float(7.5) + self.assertFalse(c.flags[FloatOperation]) + # comparison sets the flag + self.assertEqual(x, 7.5) + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + x = c.create_decimal_from_float(7.5) + self.assertFalse(c.flags[FloatOperation]) + self.assertEqual(x, 7.5) + self.assertTrue(c.flags[FloatOperation]) + + ##### set the trap + c.traps[FloatOperation] = True + + # implicit conversion raises + c.clear_flags() + self.assertRaises(FloatOperation, Decimal, 7.5) + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + self.assertRaises(FloatOperation, c.create_decimal, 7.5) + self.assertTrue(c.flags[FloatOperation]) + + # explicit conversion is silent + c.clear_flags() + x = Decimal.from_float(7.5) + self.assertFalse(c.flags[FloatOperation]) + + c.clear_flags() + x = c.create_decimal_from_float(7.5) + self.assertFalse(c.flags[FloatOperation]) + + def test_float_comparison(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + FloatOperation = self.decimal.FloatOperation + localcontext = self.decimal.localcontext + + def assert_attr(a, b, attr, context, signal=None): + context.clear_flags() + f = getattr(a, attr) + if signal == FloatOperation: + self.assertRaises(signal, f, b) + else: + self.assertIs(f(b), True) + self.assertTrue(context.flags[FloatOperation]) + + small_d = Decimal('0.25') + big_d = Decimal('3.0') + small_f = 0.25 + big_f = 3.0 + + zero_d = Decimal('0.0') + neg_zero_d = Decimal('-0.0') + zero_f = 0.0 + neg_zero_f = -0.0 + + inf_d = Decimal('Infinity') + neg_inf_d = Decimal('-Infinity') + inf_f = float('inf') + neg_inf_f = float('-inf') + + def doit(c, signal=None): + # Order + for attr in '__lt__', '__le__': + assert_attr(small_d, big_f, attr, c, signal) + + for attr in '__gt__', '__ge__': + assert_attr(big_d, small_f, attr, c, signal) + + # Equality + assert_attr(small_d, small_f, '__eq__', c, None) + + assert_attr(neg_zero_d, neg_zero_f, '__eq__', c, None) + assert_attr(neg_zero_d, zero_f, '__eq__', c, None) + + assert_attr(zero_d, neg_zero_f, '__eq__', c, None) + assert_attr(zero_d, zero_f, '__eq__', c, None) + + assert_attr(neg_inf_d, neg_inf_f, '__eq__', c, None) + assert_attr(inf_d, inf_f, '__eq__', c, None) + + # Inequality + assert_attr(small_d, big_f, '__ne__', c, None) + + assert_attr(Decimal('0.1'), 0.1, '__ne__', c, None) + + assert_attr(neg_inf_d, inf_f, '__ne__', c, None) + assert_attr(inf_d, neg_inf_f, '__ne__', c, None) + + assert_attr(Decimal('NaN'), float('nan'), '__ne__', c, None) + + def test_containers(c, signal=None): + c.clear_flags() + s = set([100.0, Decimal('100.0')]) + self.assertEqual(len(s), 1) + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + if signal: + self.assertRaises(signal, sorted, [1.0, Decimal('10.0')]) + else: + s = sorted([10.0, Decimal('10.0')]) + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + b = 10.0 in [Decimal('10.0'), 1.0] + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + b = 10.0 in {Decimal('10.0'):'a', 1.0:'b'} + self.assertTrue(c.flags[FloatOperation]) + + nc = Context() + with localcontext(nc) as c: + self.assertFalse(c.traps[FloatOperation]) + doit(c, signal=None) + test_containers(c, signal=None) + + c.traps[FloatOperation] = True + doit(c, signal=FloatOperation) + test_containers(c, signal=FloatOperation) + + def test_float_operation_default(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + Inexact = self.decimal.Inexact + FloatOperation= self.decimal.FloatOperation + + context = Context() + self.assertFalse(context.flags[FloatOperation]) + self.assertFalse(context.traps[FloatOperation]) + + context.clear_traps() + context.traps[Inexact] = True + context.traps[FloatOperation] = True + self.assertTrue(context.traps[FloatOperation]) + self.assertTrue(context.traps[Inexact]) + + +class SpecialContexts: + """Test the context templates.""" + + def test_context_templates(self): + BasicContext = self.decimal.BasicContext + ExtendedContext = self.decimal.ExtendedContext + getcontext = self.decimal.getcontext + setcontext = self.decimal.setcontext + InvalidOperation = self.decimal.InvalidOperation + DivisionByZero = self.decimal.DivisionByZero + Overflow = self.decimal.Overflow + Underflow = self.decimal.Underflow + Clamped = self.decimal.Clamped + + assert_signals(self, BasicContext, 'traps', + [InvalidOperation, DivisionByZero, Overflow, Underflow, Clamped] + ) + + savecontext = getcontext().copy() + basic_context_prec = BasicContext.prec + extended_context_prec = ExtendedContext.prec + + ex = None + try: + BasicContext.prec = ExtendedContext.prec = 441 + for template in BasicContext, ExtendedContext: + setcontext(template) + c = getcontext() + self.assertIsNot(c, template) + self.assertEqual(c.prec, 441) + except Exception as e: + ex = e.__class__ + finally: + BasicContext.prec = basic_context_prec + ExtendedContext.prec = extended_context_prec + setcontext(savecontext) + if ex: + raise ex + + def test_default_context(self): + DefaultContext = self.decimal.DefaultContext + BasicContext = self.decimal.BasicContext + ExtendedContext = self.decimal.ExtendedContext + getcontext = self.decimal.getcontext + setcontext = self.decimal.setcontext + InvalidOperation = self.decimal.InvalidOperation + DivisionByZero = self.decimal.DivisionByZero + Overflow = self.decimal.Overflow + + self.assertEqual(BasicContext.prec, 9) + self.assertEqual(ExtendedContext.prec, 9) + + assert_signals(self, DefaultContext, 'traps', + [InvalidOperation, DivisionByZero, Overflow] + ) + + savecontext = getcontext().copy() + default_context_prec = DefaultContext.prec + + ex = None + try: + c = getcontext() + saveprec = c.prec + + DefaultContext.prec = 961 + c = getcontext() + self.assertEqual(c.prec, saveprec) + + setcontext(DefaultContext) + c = getcontext() + self.assertIsNot(c, DefaultContext) + self.assertEqual(c.prec, 961) + except Exception as e: + ex = e.__class__ + finally: + DefaultContext.prec = default_context_prec + setcontext(savecontext) + if ex: + raise ex + + +class ContextInputValidation: + + def test_invalid_context(self): + Context = self.decimal.Context + DefaultContext = self.decimal.DefaultContext + + c = DefaultContext.copy() + + # prec, Emax + for attr in ['prec', 'Emax']: + setattr(c, attr, 999999) + self.assertEqual(getattr(c, attr), 999999) + self.assertRaises(ValueError, setattr, c, attr, -1) + self.assertRaises(TypeError, setattr, c, attr, 'xyz') + + # Emin + setattr(c, 'Emin', -999999) + self.assertEqual(getattr(c, 'Emin'), -999999) + self.assertRaises(ValueError, setattr, c, 'Emin', 1) + self.assertRaises(TypeError, setattr, c, 'Emin', (1,2,3)) + + self.assertRaises(TypeError, setattr, c, 'rounding', -1) + self.assertRaises(TypeError, setattr, c, 'rounding', 9) + self.assertRaises(TypeError, setattr, c, 'rounding', 1.0) + self.assertRaises(TypeError, setattr, c, 'rounding', 'xyz') + + # capitals, clamp + for attr in ['capitals', 'clamp']: + self.assertRaises(ValueError, setattr, c, attr, -1) + self.assertRaises(ValueError, setattr, c, attr, 2) + self.assertRaises(TypeError, setattr, c, attr, [1,2,3]) + + # Invalid attribute + self.assertRaises(AttributeError, setattr, c, 'emax', 100) + + # Invalid signal dict + self.assertRaises(TypeError, setattr, c, 'flags', []) + self.assertRaises(KeyError, setattr, c, 'flags', {}) + self.assertRaises(KeyError, setattr, c, 'traps', + {'InvalidOperation':0}) + + # Attributes cannot be deleted + for attr in ['prec', 'Emax', 'Emin', 'rounding', 'capitals', 'clamp', + 'flags', 'traps']: + self.assertRaises(AttributeError, c.__delattr__, attr) + + # Invalid attributes + self.assertRaises(TypeError, getattr, c, 9) + self.assertRaises(TypeError, setattr, c, 9) + + # Invalid values in constructor + self.assertRaises(TypeError, Context, rounding=999999) + self.assertRaises(TypeError, Context, rounding='xyz') + self.assertRaises(ValueError, Context, clamp=2) + self.assertRaises(ValueError, Context, capitals=-1) + self.assertRaises(KeyError, Context, flags=["P"]) + self.assertRaises(KeyError, Context, traps=["Q"]) + + # Type error in conversion + self.assertRaises(TypeError, Context, flags=(0,1)) + self.assertRaises(TypeError, Context, traps=(1,0)) + + +class ContextSubclassing: + + def test_context_subclassing(self): + decimal = self.decimal + Decimal = decimal.Decimal + Context = decimal.Context + Clamped = decimal.Clamped + DivisionByZero = decimal.DivisionByZero + Inexact = decimal.Inexact + Overflow = decimal.Overflow + Rounded = decimal.Rounded + Subnormal = decimal.Subnormal + Underflow = decimal.Underflow + InvalidOperation = decimal.InvalidOperation + + class MyContext(Context): + def __init__(self, prec=None, rounding=None, Emin=None, Emax=None, + capitals=None, clamp=None, flags=None, + traps=None): + Context.__init__(self) + if prec is not None: + self.prec = prec + if rounding is not None: + self.rounding = rounding + if Emin is not None: + self.Emin = Emin + if Emax is not None: + self.Emax = Emax + if capitals is not None: + self.capitals = capitals + if clamp is not None: + self.clamp = clamp + if flags is not None: + if isinstance(flags, list): + flags = {v:(v in flags) for v in OrderedSignals[decimal] + flags} + self.flags = flags + if traps is not None: + if isinstance(traps, list): + traps = {v:(v in traps) for v in OrderedSignals[decimal] + traps} + self.traps = traps + + c = Context() + d = MyContext() + for attr in ('prec', 'rounding', 'Emin', 'Emax', 'capitals', 'clamp', + 'flags', 'traps'): + self.assertEqual(getattr(c, attr), getattr(d, attr)) + + # prec + self.assertRaises(ValueError, MyContext, **{'prec':-1}) + c = MyContext(prec=1) + self.assertEqual(c.prec, 1) + self.assertRaises(InvalidOperation, c.quantize, Decimal('9e2'), 0) + + # rounding + self.assertRaises(TypeError, MyContext, **{'rounding':'XYZ'}) + c = MyContext(rounding=P.ROUND_DOWN, prec=1) + self.assertEqual(c.rounding, P.ROUND_DOWN) + self.assertEqual(c.plus(Decimal('9.9')), 9) + + # Emin + self.assertRaises(ValueError, MyContext, **{'Emin':5}) + c = MyContext(Emin=-1, prec=1) + self.assertEqual(c.Emin, -1) + x = c.add(Decimal('1e-99'), Decimal('2.234e-2000')) + self.assertEqual(x, Decimal('0.0')) + for signal in (Inexact, Underflow, Subnormal, Rounded, Clamped): + self.assertTrue(c.flags[signal]) + + # Emax + self.assertRaises(ValueError, MyContext, **{'Emax':-1}) + c = MyContext(Emax=1, prec=1) + self.assertEqual(c.Emax, 1) + self.assertRaises(Overflow, c.add, Decimal('1e99'), Decimal('2.234e2000')) + if self.decimal == C: + for signal in (Inexact, Overflow, Rounded): + self.assertTrue(c.flags[signal]) + + # capitals + self.assertRaises(ValueError, MyContext, **{'capitals':-1}) + c = MyContext(capitals=0) + self.assertEqual(c.capitals, 0) + x = c.create_decimal('1E222') + self.assertEqual(c.to_sci_string(x), '1e+222') + + # clamp + self.assertRaises(ValueError, MyContext, **{'clamp':2}) + c = MyContext(clamp=1, Emax=99) + self.assertEqual(c.clamp, 1) + x = c.plus(Decimal('1e99')) + self.assertEqual(str(x), '1.000000000000000000000000000E+99') + + # flags + self.assertRaises(TypeError, MyContext, **{'flags':'XYZ'}) + c = MyContext(flags=[Rounded, DivisionByZero]) + for signal in (Rounded, DivisionByZero): + self.assertTrue(c.flags[signal]) + c.clear_flags() + for signal in OrderedSignals[decimal]: + self.assertFalse(c.flags[signal]) + + # traps + self.assertRaises(TypeError, MyContext, **{'traps':'XYZ'}) + c = MyContext(traps=[Rounded, DivisionByZero]) + for signal in (Rounded, DivisionByZero): + self.assertTrue(c.traps[signal]) + c.clear_traps() + for signal in OrderedSignals[decimal]: + self.assertFalse(c.traps[signal]) + + +class IEEEContexts: + + def test_ieee_context(self): + # issue 8786: Add support for IEEE 754 contexts to decimal module. + IEEEContext = self.decimal.IEEEContext + + def assert_rest(self, context): + self.assertEqual(context.clamp, 1) + assert_signals(self, context, 'traps', []) + assert_signals(self, context, 'flags', []) + + c = IEEEContext(32) + self.assertEqual(c.prec, 7) + self.assertEqual(c.Emax, 96) + self.assertEqual(c.Emin, -95) + assert_rest(self, c) + + c = IEEEContext(64) + self.assertEqual(c.prec, 16) + self.assertEqual(c.Emax, 384) + self.assertEqual(c.Emin, -383) + assert_rest(self, c) + + c = IEEEContext(128) + self.assertEqual(c.prec, 34) + self.assertEqual(c.Emax, 6144) + self.assertEqual(c.Emin, -6143) + assert_rest(self, c) + + # Invalid values + self.assertRaises(ValueError, IEEEContext, -1) + self.assertRaises(ValueError, IEEEContext, 123) + self.assertRaises(ValueError, IEEEContext, 1024) + + def test_constants(self): + # IEEEContext + IEEE_CONTEXT_MAX_BITS = self.decimal.IEEE_CONTEXT_MAX_BITS + self.assertIn(IEEE_CONTEXT_MAX_BITS, {256, 512}) + + +def load_tests(loader, tests, pattern): + base_classes = [ + ContextWithStatement, + ContextFlags, + SpecialContexts, + ContextInputValidation, + ContextSubclassing, + IEEEContexts, + ] + return load_tests_for_base_classes(loader, tests, base_classes) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_coverage.py b/Lib/test/test_decimal/test_coverage.py new file mode 100644 index 00000000000000..c0ddaa0291d8ff --- /dev/null +++ b/Lib/test/test_decimal/test_coverage.py @@ -0,0 +1,290 @@ +import unittest +import sys +from . import (C, P, requires_cdecimal, OrderedSignals, + setUpModule, tearDownModule) + + +class Coverage: + def test_adjusted(self): + Decimal = self.decimal.Decimal + + self.assertEqual(Decimal('1234e9999').adjusted(), 10002) + # XXX raise? + self.assertEqual(Decimal('nan').adjusted(), 0) + self.assertEqual(Decimal('inf').adjusted(), 0) + + def test_canonical(self): + Decimal = self.decimal.Decimal + getcontext = self.decimal.getcontext + + x = Decimal(9).canonical() + self.assertEqual(x, 9) + + c = getcontext() + x = c.canonical(Decimal(9)) + self.assertEqual(x, 9) + + def test_context_repr(self): + c = self.decimal.DefaultContext.copy() + + c.prec = 425000000 + c.Emax = 425000000 + c.Emin = -425000000 + c.rounding = P.ROUND_HALF_DOWN + c.capitals = 0 + c.clamp = 1 + for sig in OrderedSignals[self.decimal]: + c.flags[sig] = False + c.traps[sig] = False + + s = c.__repr__() + t = "Context(prec=425000000, rounding=ROUND_HALF_DOWN, " \ + "Emin=-425000000, Emax=425000000, capitals=0, clamp=1, " \ + "flags=[], traps=[])" + self.assertEqual(s, t) + + def test_implicit_context(self): + Decimal = self.decimal.Decimal + localcontext = self.decimal.localcontext + + with localcontext() as c: + c.prec = 1 + c.Emax = 1 + c.Emin = -1 + + # abs + self.assertEqual(abs(Decimal("-10")), 10) + # add + self.assertEqual(Decimal("7") + 1, 8) + # divide + self.assertEqual(Decimal("10") / 5, 2) + # divide_int + self.assertEqual(Decimal("10") // 7, 1) + # fma + self.assertEqual(Decimal("1.2").fma(Decimal("0.01"), 1), 1) + self.assertIs(Decimal("NaN").fma(7, 1).is_nan(), True) + # three arg power + self.assertEqual(pow(Decimal(10), 2, 7), 2) + self.assertEqual(pow(10, Decimal(2), 7), 2) + if self.decimal == C: + self.assertEqual(pow(10, 2, Decimal(7)), 2) + else: + # XXX: There is no special method to dispatch on the + # third arg of three-arg power. + self.assertRaises(TypeError, pow, 10, 2, Decimal(7)) + # exp + self.assertEqual(Decimal("1.01").exp(), 3) + # is_normal + self.assertIs(Decimal("0.01").is_normal(), False) + # is_subnormal + self.assertIs(Decimal("0.01").is_subnormal(), True) + # ln + self.assertEqual(Decimal("20").ln(), 3) + # log10 + self.assertEqual(Decimal("20").log10(), 1) + # logb + self.assertEqual(Decimal("580").logb(), 2) + # logical_invert + self.assertEqual(Decimal("10").logical_invert(), 1) + # minus + self.assertEqual(-Decimal("-10"), 10) + # multiply + self.assertEqual(Decimal("2") * 4, 8) + # next_minus + self.assertEqual(Decimal("10").next_minus(), 9) + # next_plus + self.assertEqual(Decimal("10").next_plus(), Decimal('2E+1')) + # normalize + self.assertEqual(Decimal("-10").normalize(), Decimal('-1E+1')) + # number_class + self.assertEqual(Decimal("10").number_class(), '+Normal') + # plus + self.assertEqual(+Decimal("-1"), -1) + # remainder + self.assertEqual(Decimal("10") % 7, 3) + # subtract + self.assertEqual(Decimal("10") - 7, 3) + # to_integral_exact + self.assertEqual(Decimal("1.12345").to_integral_exact(), 1) + + # Boolean functions + self.assertTrue(Decimal("1").is_canonical()) + self.assertTrue(Decimal("1").is_finite()) + self.assertTrue(Decimal("1").is_finite()) + self.assertTrue(Decimal("snan").is_snan()) + self.assertTrue(Decimal("-1").is_signed()) + self.assertTrue(Decimal("0").is_zero()) + self.assertTrue(Decimal("0").is_zero()) + + # Copy + with localcontext() as c: + c.prec = 10000 + x = 1228 ** 1523 + y = -Decimal(x) + + z = y.copy_abs() + self.assertEqual(z, x) + + z = y.copy_negate() + self.assertEqual(z, x) + + z = y.copy_sign(Decimal(1)) + self.assertEqual(z, x) + + def test_divmod(self): + Decimal = self.decimal.Decimal + localcontext = self.decimal.localcontext + InvalidOperation = self.decimal.InvalidOperation + DivisionByZero = self.decimal.DivisionByZero + + with localcontext() as c: + q, r = divmod(Decimal("10912837129"), 1001) + self.assertEqual(q, Decimal('10901935')) + self.assertEqual(r, Decimal('194')) + + q, r = divmod(Decimal("NaN"), 7) + self.assertTrue(q.is_nan() and r.is_nan()) + + c.traps[InvalidOperation] = False + q, r = divmod(Decimal("NaN"), 7) + self.assertTrue(q.is_nan() and r.is_nan()) + + c.traps[InvalidOperation] = False + c.clear_flags() + q, r = divmod(Decimal("inf"), Decimal("inf")) + self.assertTrue(q.is_nan() and r.is_nan()) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + q, r = divmod(Decimal("inf"), 101) + self.assertTrue(q.is_infinite() and r.is_nan()) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + q, r = divmod(Decimal(0), 0) + self.assertTrue(q.is_nan() and r.is_nan()) + self.assertTrue(c.flags[InvalidOperation]) + + c.traps[DivisionByZero] = False + c.clear_flags() + q, r = divmod(Decimal(11), 0) + self.assertTrue(q.is_infinite() and r.is_nan()) + self.assertTrue(c.flags[InvalidOperation] and + c.flags[DivisionByZero]) + + def test_power(self): + Decimal = self.decimal.Decimal + localcontext = self.decimal.localcontext + Overflow = self.decimal.Overflow + Rounded = self.decimal.Rounded + + with localcontext() as c: + c.prec = 3 + c.clear_flags() + self.assertEqual(Decimal("1.0") ** 100, Decimal('1.00')) + self.assertTrue(c.flags[Rounded]) + + c.prec = 1 + c.Emax = 1 + c.Emin = -1 + c.clear_flags() + c.traps[Overflow] = False + self.assertEqual(Decimal(10000) ** Decimal("0.5"), Decimal('inf')) + self.assertTrue(c.flags[Overflow]) + + def test_quantize(self): + Decimal = self.decimal.Decimal + localcontext = self.decimal.localcontext + InvalidOperation = self.decimal.InvalidOperation + + with localcontext() as c: + c.prec = 1 + c.Emax = 1 + c.Emin = -1 + c.traps[InvalidOperation] = False + x = Decimal(99).quantize(Decimal("1e1")) + self.assertTrue(x.is_nan()) + + def test_radix(self): + Decimal = self.decimal.Decimal + getcontext = self.decimal.getcontext + + c = getcontext() + self.assertEqual(Decimal("1").radix(), 10) + self.assertEqual(c.radix(), 10) + + def test_rop(self): + Decimal = self.decimal.Decimal + + for attr in ('__radd__', '__rsub__', '__rmul__', '__rtruediv__', + '__rdivmod__', '__rmod__', '__rfloordiv__', '__rpow__'): + self.assertIs(getattr(Decimal("1"), attr)("xyz"), NotImplemented) + + def test_round(self): + # Python3 behavior: round() returns Decimal + Decimal = self.decimal.Decimal + localcontext = self.decimal.localcontext + + with localcontext() as c: + c.prec = 28 + + self.assertEqual(str(Decimal("9.99").__round__()), "10") + self.assertEqual(str(Decimal("9.99e-5").__round__()), "0") + self.assertEqual(str(Decimal("1.23456789").__round__(5)), "1.23457") + self.assertEqual(str(Decimal("1.2345").__round__(10)), "1.2345000000") + self.assertEqual(str(Decimal("1.2345").__round__(-10)), "0E+10") + + self.assertRaises(TypeError, Decimal("1.23").__round__, "5") + self.assertRaises(TypeError, Decimal("1.23").__round__, 5, 8) + + def test_create_decimal(self): + c = self.decimal.Context() + self.assertRaises(ValueError, c.create_decimal, ["%"]) + + def test_int(self): + Decimal = self.decimal.Decimal + localcontext = self.decimal.localcontext + + with localcontext() as c: + c.prec = 9999 + x = Decimal(1221**1271) / 10**3923 + self.assertEqual(int(x), 1) + self.assertEqual(x.to_integral(), 2) + + def test_copy(self): + Context = self.decimal.Context + + c = Context() + c.prec = 10000 + x = -(1172 ** 1712) + + y = c.copy_abs(x) + self.assertEqual(y, -x) + + y = c.copy_negate(x) + self.assertEqual(y, -x) + + y = c.copy_sign(x, 1) + self.assertEqual(y, -x) + + +@requires_cdecimal +class CCoverage(Coverage, unittest.TestCase): + decimal = C + + +class PyCoverage(Coverage, unittest.TestCase): + decimal = P + + def setUp(self): + super().setUp() + self._previous_int_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(7000) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_int_limit) + super().tearDown() + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_doc.py b/Lib/test/test_decimal/test_doc.py new file mode 100644 index 00000000000000..3a2a7fcd190cd4 --- /dev/null +++ b/Lib/test/test_decimal/test_doc.py @@ -0,0 +1,31 @@ +import unittest +import sys +from doctest import DocTestSuite, IGNORE_EXCEPTION_DETAIL +from . import C, P, orig_sys_decimal + +def get_test_suite_for_module(mod): + if not mod: + return None + + def setUp(self, mod=mod): + sys.modules['decimal'] = mod + + def tearDown(self): + sys.modules['decimal'] = orig_sys_decimal + + optionflags = IGNORE_EXCEPTION_DETAIL if mod is C else 0 + return DocTestSuite(mod, setUp=setUp, tearDown=tearDown, + optionflags=optionflags) + +def load_tests(loader, tests, pattern): + for mod in C, P: + sys.modules['decimal'] = mod + test_suite = get_test_suite_for_module(mod) + if test_suite: + tests.addTest(test_suite) + + return tests + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_format.py b/Lib/test/test_decimal/test_format.py new file mode 100644 index 00000000000000..3da32b871b1243 --- /dev/null +++ b/Lib/test/test_decimal/test_format.py @@ -0,0 +1,403 @@ +import unittest +import locale +from test.support import run_with_locale, warnings_helper +from . import (C, P, load_tests_for_base_classes, + setUpModule, tearDownModule) + + +class FormatTest: + '''Unit tests for the format function.''' + + def test_formatting(self): + Decimal = self.decimal.Decimal + + # triples giving a format, a Decimal, and the expected result + test_values = [ + ('e', '0E-15', '0e-15'), + ('e', '2.3E-15', '2.3e-15'), + ('e', '2.30E+2', '2.30e+2'), # preserve significant zeros + ('e', '2.30000E-15', '2.30000e-15'), + ('e', '1.23456789123456789e40', '1.23456789123456789e+40'), + ('e', '1.5', '1.5e+0'), + ('e', '0.15', '1.5e-1'), + ('e', '0.015', '1.5e-2'), + ('e', '0.0000000000015', '1.5e-12'), + ('e', '15.0', '1.50e+1'), + ('e', '-15', '-1.5e+1'), + ('e', '0', '0e+0'), + ('e', '0E1', '0e+1'), + ('e', '0.0', '0e-1'), + ('e', '0.00', '0e-2'), + ('.6e', '0E-15', '0.000000e-9'), + ('.6e', '0', '0.000000e+6'), + ('.6e', '9.999999', '9.999999e+0'), + ('.6e', '9.9999999', '1.000000e+1'), + ('.6e', '-1.23e5', '-1.230000e+5'), + ('.6e', '1.23456789e-3', '1.234568e-3'), + ('f', '0', '0'), + ('f', '0.0', '0.0'), + ('f', '0E-2', '0.00'), + ('f', '0.00E-8', '0.0000000000'), + ('f', '0E1', '0'), # loses exponent information + ('f', '3.2E1', '32'), + ('f', '3.2E2', '320'), + ('f', '3.20E2', '320'), + ('f', '3.200E2', '320.0'), + ('f', '3.2E-6', '0.0000032'), + ('.6f', '0E-15', '0.000000'), # all zeros treated equally + ('.6f', '0E1', '0.000000'), + ('.6f', '0', '0.000000'), + ('.0f', '0', '0'), # no decimal point + ('.0f', '0e-2', '0'), + ('.0f', '3.14159265', '3'), + ('.1f', '3.14159265', '3.1'), + ('.01f', '3.14159265', '3.1'), # leading zero in precision + ('.4f', '3.14159265', '3.1416'), + ('.6f', '3.14159265', '3.141593'), + ('.7f', '3.14159265', '3.1415926'), # round-half-even! + ('.8f', '3.14159265', '3.14159265'), + ('.9f', '3.14159265', '3.141592650'), + + ('g', '0', '0'), + ('g', '0.0', '0.0'), + ('g', '0E1', '0e+1'), + ('G', '0E1', '0E+1'), + ('g', '0E-5', '0.00000'), + ('g', '0E-6', '0.000000'), + ('g', '0E-7', '0e-7'), + ('g', '-0E2', '-0e+2'), + ('.0g', '3.14159265', '3'), # 0 sig fig -> 1 sig fig + ('.0n', '3.14159265', '3'), # same for 'n' + ('.1g', '3.14159265', '3'), + ('.2g', '3.14159265', '3.1'), + ('.5g', '3.14159265', '3.1416'), + ('.7g', '3.14159265', '3.141593'), + ('.8g', '3.14159265', '3.1415926'), # round-half-even! + ('.9g', '3.14159265', '3.14159265'), + ('.10g', '3.14159265', '3.14159265'), # don't pad + + ('%', '0E1', '0%'), + ('%', '0E0', '0%'), + ('%', '0E-1', '0%'), + ('%', '0E-2', '0%'), + ('%', '0E-3', '0.0%'), + ('%', '0E-4', '0.00%'), + + ('.3%', '0', '0.000%'), # all zeros treated equally + ('.3%', '0E10', '0.000%'), + ('.3%', '0E-10', '0.000%'), + ('.3%', '2.34', '234.000%'), + ('.3%', '1.234567', '123.457%'), + ('.0%', '1.23', '123%'), + + ('e', 'NaN', 'NaN'), + ('f', '-NaN123', '-NaN123'), + ('+g', 'NaN456', '+NaN456'), + ('.3e', 'Inf', 'Infinity'), + ('.16f', '-Inf', '-Infinity'), + ('.0g', '-sNaN', '-sNaN'), + + ('', '1.00', '1.00'), + + # test alignment and padding + ('6', '123', ' 123'), + ('<6', '123', '123 '), + ('>6', '123', ' 123'), + ('^6', '123', ' 123 '), + ('=+6', '123', '+ 123'), + ('#<10', 'NaN', 'NaN#######'), + ('#<10', '-4.3', '-4.3######'), + ('#<+10', '0.0130', '+0.0130###'), + ('#< 10', '0.0130', ' 0.0130###'), + ('@>10', '-Inf', '@-Infinity'), + ('#>5', '-Inf', '-Infinity'), + ('?^5', '123', '?123?'), + ('%^6', '123', '%123%%'), + (' ^6', '-45.6', '-45.6 '), + ('/=10', '-45.6', '-/////45.6'), + ('/=+10', '45.6', '+/////45.6'), + ('/= 10', '45.6', ' /////45.6'), + ('\x00=10', '-inf', '-\x00Infinity'), + ('\x00^16', '-inf', '\x00\x00\x00-Infinity\x00\x00\x00\x00'), + ('\x00>10', '1.2345', '\x00\x00\x00\x001.2345'), + ('\x00<10', '1.2345', '1.2345\x00\x00\x00\x00'), + + # thousands separator + (',', '1234567', '1,234,567'), + (',', '123456', '123,456'), + (',', '12345', '12,345'), + (',', '1234', '1,234'), + (',', '123', '123'), + (',', '12', '12'), + (',', '1', '1'), + (',', '0', '0'), + (',', '-1234567', '-1,234,567'), + (',', '-123456', '-123,456'), + ('7,', '123456', '123,456'), + ('8,', '123456', ' 123,456'), + ('08,', '123456', '0,123,456'), # special case: extra 0 needed + ('+08,', '123456', '+123,456'), # but not if there's a sign + ('008,', '123456', '0,123,456'), # leading zero in width + (' 08,', '123456', ' 123,456'), + ('08,', '-123456', '-123,456'), + ('+09,', '123456', '+0,123,456'), + # ... with fractional part... + ('07,', '1234.56', '1,234.56'), + ('08,', '1234.56', '1,234.56'), + ('09,', '1234.56', '01,234.56'), + ('010,', '1234.56', '001,234.56'), + ('011,', '1234.56', '0,001,234.56'), + ('012,', '1234.56', '0,001,234.56'), + ('08,.1f', '1234.5', '01,234.5'), + # no thousands separators in fraction part + (',', '1.23456789', '1.23456789'), + (',%', '123.456789', '12,345.6789%'), + (',e', '123456', '1.23456e+5'), + (',E', '123456', '1.23456E+5'), + # ... with '_' instead + ('_', '1234567', '1_234_567'), + ('07_', '1234.56', '1_234.56'), + ('_', '1.23456789', '1.23456789'), + ('_%', '123.456789', '12_345.6789%'), + # and now for something completely different... + ('.,', '1.23456789', '1.234,567,89'), + ('._', '1.23456789', '1.234_567_89'), + ('.6_f', '12345.23456789', '12345.234_568'), + (',._%', '123.456789', '12,345.678_9%'), + (',._e', '123456', '1.234_56e+5'), + (',.4_e', '123456', '1.234_6e+5'), + (',.3_e', '123456', '1.235e+5'), + (',._E', '123456', '1.234_56E+5'), + + # negative zero: default behavior + ('.1f', '-0', '-0.0'), + ('.1f', '-.0', '-0.0'), + ('.1f', '-.01', '-0.0'), + + # negative zero: z option + ('z.1f', '0.', '0.0'), + ('z6.1f', '0.', ' 0.0'), + ('z6.1f', '-1.', ' -1.0'), + ('z.1f', '-0.', '0.0'), + ('z.1f', '.01', '0.0'), + ('z.1f', '-.01', '0.0'), + ('z.2f', '0.', '0.00'), + ('z.2f', '-0.', '0.00'), + ('z.2f', '.001', '0.00'), + ('z.2f', '-.001', '0.00'), + + ('z.1e', '0.', '0.0e+1'), + ('z.1e', '-0.', '0.0e+1'), + ('z.1E', '0.', '0.0E+1'), + ('z.1E', '-0.', '0.0E+1'), + + ('z.2e', '-0.001', '-1.00e-3'), # tests for mishandled rounding + ('z.2g', '-0.001', '-0.001'), + ('z.2%', '-0.001', '-0.10%'), + + ('zf', '-0.0000', '0.0000'), # non-normalized form is preserved + + ('z.1f', '-00000.000001', '0.0'), + ('z.1f', '-00000.', '0.0'), + ('z.1f', '-.0000000000', '0.0'), + + ('z.2f', '-00000.000001', '0.00'), + ('z.2f', '-00000.', '0.00'), + ('z.2f', '-.0000000000', '0.00'), + + ('z.1f', '.09', '0.1'), + ('z.1f', '-.09', '-0.1'), + + (' z.0f', '-0.', ' 0'), + ('+z.0f', '-0.', '+0'), + ('-z.0f', '-0.', '0'), + (' z.0f', '-1.', '-1'), + ('+z.0f', '-1.', '-1'), + ('-z.0f', '-1.', '-1'), + + ('z>6.1f', '-0.', 'zz-0.0'), + ('z>z6.1f', '-0.', 'zzz0.0'), + ('x>z6.1f', '-0.', 'xxx0.0'), + ('🖤>z6.1f', '-0.', '🖤🖤🖤0.0'), # multi-byte fill char + ('\x00>z6.1f', '-0.', '\x00\x00\x000.0'), # null fill char + + # issue 114563 ('z' format on F type in cdecimal) + ('z3,.10F', '-6.24E-323', '0.0000000000'), + + # issue 91060 ('#' format in cdecimal) + ('#', '0', '0.'), + + # issue 6850 + ('a=-7.0', '0.12345', 'aaaa0.1'), + + # issue 22090 + ('<^+15.20%', 'inf', '<<+Infinity%<<<'), + ('\x07>,%', 'sNaN1234567', 'sNaN1234567%'), + ('=10.10%', 'NaN123', ' NaN123%'), + ] + for fmt, d, result in test_values: + self.assertEqual(format(Decimal(d), fmt), result) + + # bytes format argument + self.assertRaises(TypeError, Decimal(1).__format__, b'-020') + + # precision or fractional part separator should follow after dot + self.assertRaises(ValueError, format, Decimal(1), '.f') + self.assertRaises(ValueError, format, Decimal(1), '._6f') + + def test_negative_zero_format_directed_rounding(self): + with self.decimal.localcontext() as ctx: + ctx.rounding = P.ROUND_CEILING + self.assertEqual(format(self.decimal.Decimal('-0.001'), 'z.2f'), + '0.00') + + def test_negative_zero_bad_format(self): + self.assertRaises(ValueError, format, self.decimal.Decimal('1.23'), 'fz') + + def test_n_format(self): + Decimal = self.decimal.Decimal + + try: + from locale import CHAR_MAX + except ImportError: + self.skipTest('locale.CHAR_MAX not available') + + def make_grouping(lst): + return ''.join([chr(x) for x in lst]) if self.decimal == C else lst + + def get_fmt(x, override=None, fmt='n'): + if self.decimal == C: + return Decimal(x).__format__(fmt, override) + else: + return Decimal(x).__format__(fmt, _localeconv=override) + + # Set up some localeconv-like dictionaries + en_US = { + 'decimal_point' : '.', + 'grouping' : make_grouping([3, 3, 0]), + 'thousands_sep' : ',' + } + + fr_FR = { + 'decimal_point' : ',', + 'grouping' : make_grouping([CHAR_MAX]), + 'thousands_sep' : '' + } + + ru_RU = { + 'decimal_point' : ',', + 'grouping': make_grouping([3, 3, 0]), + 'thousands_sep' : ' ' + } + + crazy = { + 'decimal_point' : '&', + 'grouping': make_grouping([1, 4, 2, CHAR_MAX]), + 'thousands_sep' : '-' + } + + dotsep_wide = { + 'decimal_point' : b'\xc2\xbf'.decode('utf-8'), + 'grouping': make_grouping([3, 3, 0]), + 'thousands_sep' : b'\xc2\xb4'.decode('utf-8') + } + + self.assertEqual(get_fmt(Decimal('12.7'), en_US), '12.7') + self.assertEqual(get_fmt(Decimal('12.7'), fr_FR), '12,7') + self.assertEqual(get_fmt(Decimal('12.7'), ru_RU), '12,7') + self.assertEqual(get_fmt(Decimal('12.7'), crazy), '1-2&7') + + self.assertEqual(get_fmt(123456789, en_US), '123,456,789') + self.assertEqual(get_fmt(123456789, fr_FR), '123456789') + self.assertEqual(get_fmt(123456789, ru_RU), '123 456 789') + self.assertEqual(get_fmt(1234567890123, crazy), '123456-78-9012-3') + + self.assertEqual(get_fmt(123456789, en_US, '.6n'), '1.23457e+8') + self.assertEqual(get_fmt(123456789, fr_FR, '.6n'), '1,23457e+8') + self.assertEqual(get_fmt(123456789, ru_RU, '.6n'), '1,23457e+8') + self.assertEqual(get_fmt(123456789, crazy, '.6n'), '1&23457e+8') + + # zero padding + self.assertEqual(get_fmt(1234, fr_FR, '03n'), '1234') + self.assertEqual(get_fmt(1234, fr_FR, '04n'), '1234') + self.assertEqual(get_fmt(1234, fr_FR, '05n'), '01234') + self.assertEqual(get_fmt(1234, fr_FR, '06n'), '001234') + + self.assertEqual(get_fmt(12345, en_US, '05n'), '12,345') + self.assertEqual(get_fmt(12345, en_US, '06n'), '12,345') + self.assertEqual(get_fmt(12345, en_US, '07n'), '012,345') + self.assertEqual(get_fmt(12345, en_US, '08n'), '0,012,345') + self.assertEqual(get_fmt(12345, en_US, '09n'), '0,012,345') + self.assertEqual(get_fmt(12345, en_US, '010n'), '00,012,345') + + self.assertEqual(get_fmt(123456, crazy, '06n'), '1-2345-6') + self.assertEqual(get_fmt(123456, crazy, '07n'), '1-2345-6') + self.assertEqual(get_fmt(123456, crazy, '08n'), '1-2345-6') + self.assertEqual(get_fmt(123456, crazy, '09n'), '01-2345-6') + self.assertEqual(get_fmt(123456, crazy, '010n'), '0-01-2345-6') + self.assertEqual(get_fmt(123456, crazy, '011n'), '0-01-2345-6') + self.assertEqual(get_fmt(123456, crazy, '012n'), '00-01-2345-6') + self.assertEqual(get_fmt(123456, crazy, '013n'), '000-01-2345-6') + + # wide char separator and decimal point + self.assertEqual(get_fmt(Decimal('-1.5'), dotsep_wide, '020n'), + '-0\u00b4000\u00b4000\u00b4000\u00b4001\u00bf5') + + def test_deprecated_N_format(self): + Decimal = self.decimal.Decimal + h = Decimal('6.62607015e-34') + if self.decimal == C: + with self.assertWarns(DeprecationWarning) as cm: + r = format(h, 'N') + self.assertEqual(cm.filename, __file__) + self.assertEqual(r, format(h, 'n').upper()) + with self.assertWarns(DeprecationWarning) as cm: + r = format(h, '010.3N') + self.assertEqual(cm.filename, __file__) + self.assertEqual(r, format(h, '010.3n').upper()) + else: + self.assertRaises(ValueError, format, h, 'N') + self.assertRaises(ValueError, format, h, '010.3N') + with warnings_helper.check_no_warnings(self): + self.assertEqual(format(h, 'N>10.3'), 'NN6.63E-34') + self.assertEqual(format(h, 'N>10.3n'), 'NN6.63e-34') + self.assertEqual(format(h, 'N>10.3e'), 'N6.626e-34') + self.assertEqual(format(h, 'N>10.3f'), 'NNNNN0.000') + self.assertRaises(ValueError, format, h, '>Nf') + self.assertRaises(ValueError, format, h, '10Nf') + self.assertRaises(ValueError, format, h, 'Nx') + + @run_with_locale('LC_ALL', 'ps_AF', '') + def test_wide_char_separator_decimal_point(self): + # locale with wide char separator and decimal point + Decimal = self.decimal.Decimal + + decimal_point = locale.localeconv()['decimal_point'] + thousands_sep = locale.localeconv()['thousands_sep'] + if decimal_point != '\u066b': + self.skipTest('inappropriate decimal point separator ' + '({!a} not {!a})'.format(decimal_point, '\u066b')) + if thousands_sep != '\u066c': + self.skipTest('inappropriate thousands separator ' + '({!a} not {!a})'.format(thousands_sep, '\u066c')) + + self.assertEqual(format(Decimal('100000000.123'), 'n'), + '100\u066c000\u066c000\u066b123') + + def test_decimal_from_float_argument_type(self): + class A(self.decimal.Decimal): + def __init__(self, a): + self.a_type = type(a) + a = A.from_float(42.5) + self.assertEqual(self.decimal.Decimal, a.a_type) + + a = A.from_float(42) + self.assertEqual(self.decimal.Decimal, a.a_type) + + +def load_tests(loader, tests, pattern): + return load_tests_for_base_classes(loader, tests, [FormatTest]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_misc.py b/Lib/test/test_decimal/test_misc.py new file mode 100644 index 00000000000000..18cbbcb23ed113 --- /dev/null +++ b/Lib/test/test_decimal/test_misc.py @@ -0,0 +1,190 @@ +import unittest +from test.support import (requires_docstrings, TestFailed) +import inspect +from . import (C, P, load_tests_for_base_classes, + requires_cdecimal, skip_if_extra_functionality, + setUpModule, tearDownModule) + + +@skip_if_extra_functionality +@requires_cdecimal +class CheckAttributes(unittest.TestCase): + def test_module_attributes(self): + # Architecture dependent context limits + self.assertEqual(C.MAX_PREC, P.MAX_PREC) + self.assertEqual(C.MAX_EMAX, P.MAX_EMAX) + self.assertEqual(C.MIN_EMIN, P.MIN_EMIN) + self.assertEqual(C.MIN_ETINY, P.MIN_ETINY) + self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, P.IEEE_CONTEXT_MAX_BITS) + + self.assertTrue(C.HAVE_THREADS is True or C.HAVE_THREADS is False) + self.assertTrue(P.HAVE_THREADS is True or P.HAVE_THREADS is False) + + self.assertEqual(C.SPEC_VERSION, P.SPEC_VERSION) + + self.assertLessEqual(set(dir(C)), set(dir(P))) + self.assertEqual([n for n in dir(C) if n[:2] != '__'], sorted(P.__all__)) + + def test_context_attributes(self): + x = [s for s in dir(C.Context()) if '__' in s or not s.startswith('_')] + y = [s for s in dir(P.Context()) if '__' in s or not s.startswith('_')] + self.assertEqual(set(x) - set(y), set()) + + def test_decimal_attributes(self): + x = [s for s in dir(C.Decimal(9)) if '__' in s or not s.startswith('_')] + y = [s for s in dir(C.Decimal(9)) if '__' in s or not s.startswith('_')] + self.assertEqual(set(x) - set(y), set()) + + +@requires_docstrings +@requires_cdecimal +class SignatureTest(unittest.TestCase): + """Function signatures""" + + def test_inspect_module(self): + for attr in dir(P): + if attr.startswith('_'): + continue + p_func = getattr(P, attr) + c_func = getattr(C, attr) + if (attr == 'Decimal' or attr == 'Context' or + inspect.isfunction(p_func)): + p_sig = inspect.signature(p_func) + c_sig = inspect.signature(c_func) + + # parameter names: + c_names = list(c_sig.parameters.keys()) + p_names = [x for x in p_sig.parameters.keys() if not + x.startswith('_')] + + self.assertEqual(c_names, p_names, + msg="parameter name mismatch in %s" % p_func) + + c_kind = [x.kind for x in c_sig.parameters.values()] + p_kind = [x[1].kind for x in p_sig.parameters.items() if not + x[0].startswith('_')] + + # parameters: + if attr != 'setcontext': + self.assertEqual(c_kind, p_kind, + msg="parameter kind mismatch in %s" % p_func) + + def test_inspect_types(self): + POS = inspect._ParameterKind.POSITIONAL_ONLY + POS_KWD = inspect._ParameterKind.POSITIONAL_OR_KEYWORD + + # Type heuristic (type annotations would help!): + pdict = {C: {'other': C.Decimal(1), + 'third': C.Decimal(1), + 'x': C.Decimal(1), + 'y': C.Decimal(1), + 'z': C.Decimal(1), + 'a': C.Decimal(1), + 'b': C.Decimal(1), + 'c': C.Decimal(1), + 'exp': C.Decimal(1), + 'modulo': C.Decimal(1), + 'num': "1", + 'f': 1.0, + 'rounding': C.ROUND_HALF_UP, + 'context': C.getcontext()}, + P: {'other': P.Decimal(1), + 'third': P.Decimal(1), + 'a': P.Decimal(1), + 'b': P.Decimal(1), + 'c': P.Decimal(1), + 'exp': P.Decimal(1), + 'modulo': P.Decimal(1), + 'num': "1", + 'f': 1.0, + 'rounding': P.ROUND_HALF_UP, + 'context': P.getcontext()}} + + def mkargs(module, sig): + args = [] + kwargs = {} + for name, param in sig.parameters.items(): + if name == 'self': continue + if param.kind == POS: + args.append(pdict[module][name]) + elif param.kind == POS_KWD: + kwargs[name] = pdict[module][name] + else: + raise TestFailed("unexpected parameter kind") + return args, kwargs + + def tr(s): + """The C Context docstrings use 'x' in order to prevent confusion + with the article 'a' in the descriptions.""" + if s == 'x': return 'a' + if s == 'y': return 'b' + if s == 'z': return 'c' + return s + + def doit(ty): + p_type = getattr(P, ty) + c_type = getattr(C, ty) + for attr in dir(p_type): + if attr.startswith('_'): + continue + p_func = getattr(p_type, attr) + c_func = getattr(c_type, attr) + if inspect.isfunction(p_func): + p_sig = inspect.signature(p_func) + c_sig = inspect.signature(c_func) + + # parameter names: + p_names = list(p_sig.parameters.keys()) + c_names = [tr(x) for x in c_sig.parameters.keys()] + + self.assertEqual(c_names, p_names, + msg="parameter name mismatch in %s" % p_func) + + p_kind = [x.kind for x in p_sig.parameters.values()] + c_kind = [x.kind for x in c_sig.parameters.values()] + + # 'self' parameter: + self.assertIs(p_kind[0], POS_KWD) + self.assertIs(c_kind[0], POS) + + # remaining parameters: + if ty == 'Decimal': + self.assertEqual(c_kind[1:], p_kind[1:], + msg="parameter kind mismatch in %s" % p_func) + else: # Context methods are positional only in the C version. + self.assertEqual(len(c_kind), len(p_kind), + msg="parameter kind mismatch in %s" % p_func) + + # Run the function: + args, kwds = mkargs(C, c_sig) + try: + getattr(c_type(9), attr)(*args, **kwds) + except Exception: + raise TestFailed("invalid signature for %s: %s %s" % (c_func, args, kwds)) + + args, kwds = mkargs(P, p_sig) + try: + getattr(p_type(9), attr)(*args, **kwds) + except Exception: + raise TestFailed("invalid signature for %s: %s %s" % (p_func, args, kwds)) + + doit('Decimal') + doit('Context') + + +class TestModule: + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(self.decimal, "__version__") + self.assertEqual(cm.filename, __file__) + + +def load_tests(loader, tests, pattern): + return load_tests_for_base_classes(loader, tests, [TestModule]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_operators.py b/Lib/test/test_decimal/test_operators.py new file mode 100644 index 00000000000000..06941a29b0215b --- /dev/null +++ b/Lib/test/test_decimal/test_operators.py @@ -0,0 +1,312 @@ +import unittest +import operator +from . import (load_tests_for_base_classes, + setUpModule, tearDownModule) + + +class ArithmeticOperatorsTest: + '''Unit tests for all arithmetic operators, binary and unary.''' + + def test_addition(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('-11.1') + d2 = Decimal('22.2') + + #two Decimals + self.assertEqual(d1+d2, Decimal('11.1')) + self.assertEqual(d2+d1, Decimal('11.1')) + + #with other type, left + c = d1 + 5 + self.assertEqual(c, Decimal('-6.1')) + self.assertEqual(type(c), type(d1)) + + #with other type, right + c = 5 + d1 + self.assertEqual(c, Decimal('-6.1')) + self.assertEqual(type(c), type(d1)) + + #inline with decimal + d1 += d2 + self.assertEqual(d1, Decimal('11.1')) + + #inline with other type + d1 += 5 + self.assertEqual(d1, Decimal('16.1')) + + def test_subtraction(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('-11.1') + d2 = Decimal('22.2') + + #two Decimals + self.assertEqual(d1-d2, Decimal('-33.3')) + self.assertEqual(d2-d1, Decimal('33.3')) + + #with other type, left + c = d1 - 5 + self.assertEqual(c, Decimal('-16.1')) + self.assertEqual(type(c), type(d1)) + + #with other type, right + c = 5 - d1 + self.assertEqual(c, Decimal('16.1')) + self.assertEqual(type(c), type(d1)) + + #inline with decimal + d1 -= d2 + self.assertEqual(d1, Decimal('-33.3')) + + #inline with other type + d1 -= 5 + self.assertEqual(d1, Decimal('-38.3')) + + def test_multiplication(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('-5') + d2 = Decimal('3') + + #two Decimals + self.assertEqual(d1*d2, Decimal('-15')) + self.assertEqual(d2*d1, Decimal('-15')) + + #with other type, left + c = d1 * 5 + self.assertEqual(c, Decimal('-25')) + self.assertEqual(type(c), type(d1)) + + #with other type, right + c = 5 * d1 + self.assertEqual(c, Decimal('-25')) + self.assertEqual(type(c), type(d1)) + + #inline with decimal + d1 *= d2 + self.assertEqual(d1, Decimal('-15')) + + #inline with other type + d1 *= 5 + self.assertEqual(d1, Decimal('-75')) + + def test_division(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('-5') + d2 = Decimal('2') + + #two Decimals + self.assertEqual(d1/d2, Decimal('-2.5')) + self.assertEqual(d2/d1, Decimal('-0.4')) + + #with other type, left + c = d1 / 4 + self.assertEqual(c, Decimal('-1.25')) + self.assertEqual(type(c), type(d1)) + + #with other type, right + c = 4 / d1 + self.assertEqual(c, Decimal('-0.8')) + self.assertEqual(type(c), type(d1)) + + #inline with decimal + d1 /= d2 + self.assertEqual(d1, Decimal('-2.5')) + + #inline with other type + d1 /= 4 + self.assertEqual(d1, Decimal('-0.625')) + + def test_floor_division(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('5') + d2 = Decimal('2') + + #two Decimals + self.assertEqual(d1//d2, Decimal('2')) + self.assertEqual(d2//d1, Decimal('0')) + + #with other type, left + c = d1 // 4 + self.assertEqual(c, Decimal('1')) + self.assertEqual(type(c), type(d1)) + + #with other type, right + c = 7 // d1 + self.assertEqual(c, Decimal('1')) + self.assertEqual(type(c), type(d1)) + + #inline with decimal + d1 //= d2 + self.assertEqual(d1, Decimal('2')) + + #inline with other type + d1 //= 2 + self.assertEqual(d1, Decimal('1')) + + def test_powering(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('5') + d2 = Decimal('2') + + #two Decimals + self.assertEqual(d1**d2, Decimal('25')) + self.assertEqual(d2**d1, Decimal('32')) + + #with other type, left + c = d1 ** 4 + self.assertEqual(c, Decimal('625')) + self.assertEqual(type(c), type(d1)) + + #with other type, right + c = 7 ** d1 + self.assertEqual(c, Decimal('16807')) + self.assertEqual(type(c), type(d1)) + + #inline with decimal + d1 **= d2 + self.assertEqual(d1, Decimal('25')) + + #inline with other type + d1 **= 4 + self.assertEqual(d1, Decimal('390625')) + + def test_module(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('5') + d2 = Decimal('2') + + #two Decimals + self.assertEqual(d1%d2, Decimal('1')) + self.assertEqual(d2%d1, Decimal('2')) + + #with other type, left + c = d1 % 4 + self.assertEqual(c, Decimal('1')) + self.assertEqual(type(c), type(d1)) + + #with other type, right + c = 7 % d1 + self.assertEqual(c, Decimal('2')) + self.assertEqual(type(c), type(d1)) + + #inline with decimal + d1 %= d2 + self.assertEqual(d1, Decimal('1')) + + #inline with other type + d1 %= 4 + self.assertEqual(d1, Decimal('1')) + + def test_floor_div_module(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('5') + d2 = Decimal('2') + + #two Decimals + (p, q) = divmod(d1, d2) + self.assertEqual(p, Decimal('2')) + self.assertEqual(q, Decimal('1')) + self.assertEqual(type(p), type(d1)) + self.assertEqual(type(q), type(d1)) + + #with other type, left + (p, q) = divmod(d1, 4) + self.assertEqual(p, Decimal('1')) + self.assertEqual(q, Decimal('1')) + self.assertEqual(type(p), type(d1)) + self.assertEqual(type(q), type(d1)) + + #with other type, right + (p, q) = divmod(7, d1) + self.assertEqual(p, Decimal('1')) + self.assertEqual(q, Decimal('2')) + self.assertEqual(type(p), type(d1)) + self.assertEqual(type(q), type(d1)) + + def test_unary_operators(self): + Decimal = self.decimal.Decimal + + self.assertEqual(+Decimal(45), Decimal(+45)) # + + self.assertEqual(-Decimal(45), Decimal(-45)) # - + self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs + + def test_nan_comparisons(self): + # comparisons involving signaling nans signal InvalidOperation + + # order comparisons (<, <=, >, >=) involving only quiet nans + # also signal InvalidOperation + + # equality comparisons (==, !=) involving only quiet nans + # don't signal, but return False or True respectively. + Decimal = self.decimal.Decimal + InvalidOperation = self.decimal.InvalidOperation + localcontext = self.decimal.localcontext + + n = Decimal('NaN') + s = Decimal('sNaN') + i = Decimal('Inf') + f = Decimal('2') + + qnan_pairs = (n, n), (n, i), (i, n), (n, f), (f, n) + snan_pairs = (s, n), (n, s), (s, i), (i, s), (s, f), (f, s), (s, s) + order_ops = operator.lt, operator.le, operator.gt, operator.ge + equality_ops = operator.eq, operator.ne + + # results when InvalidOperation is not trapped + with localcontext() as ctx: + ctx.traps[InvalidOperation] = 0 + + for x, y in qnan_pairs + snan_pairs: + for op in order_ops + equality_ops: + got = op(x, y) + expected = True if op is operator.ne else False + self.assertIs(expected, got, + "expected {0!r} for operator.{1}({2!r}, {3!r}); " + "got {4!r}".format( + expected, op.__name__, x, y, got)) + + # repeat the above, but this time trap the InvalidOperation + with localcontext() as ctx: + ctx.traps[InvalidOperation] = 1 + + for x, y in qnan_pairs: + for op in equality_ops: + got = op(x, y) + expected = True if op is operator.ne else False + self.assertIs(expected, got, + "expected {0!r} for " + "operator.{1}({2!r}, {3!r}); " + "got {4!r}".format( + expected, op.__name__, x, y, got)) + + for x, y in snan_pairs: + for op in equality_ops: + self.assertRaises(InvalidOperation, operator.eq, x, y) + self.assertRaises(InvalidOperation, operator.ne, x, y) + + for x, y in qnan_pairs + snan_pairs: + for op in order_ops: + self.assertRaises(InvalidOperation, op, x, y) + + def test_copy_sign(self): + Decimal = self.decimal.Decimal + + d = Decimal(1).copy_sign(Decimal(-2)) + self.assertEqual(Decimal(1).copy_sign(-2), d) + self.assertRaises(TypeError, Decimal(1).copy_sign, '-2') + + +def load_tests(loader, tests, pattern): + return load_tests_for_base_classes(loader, tests, + [ArithmeticOperatorsTest]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_py_specific.py b/Lib/test/test_decimal/test_py_specific.py new file mode 100644 index 00000000000000..b3e30b991f5990 --- /dev/null +++ b/Lib/test/test_decimal/test_py_specific.py @@ -0,0 +1,187 @@ +import unittest +from . import P, OrderedSignals + + +class PyFunctionality(unittest.TestCase): + """Extra functionality in decimal.py""" + + def test_py_alternate_formatting(self): + # triples giving a format, a Decimal, and the expected result + Decimal = P.Decimal + localcontext = P.localcontext + + test_values = [ + # Issue 7094: Alternate formatting (specified by #) + ('.0e', '1.0', '1e+0'), + ('#.0e', '1.0', '1.e+0'), + ('.0f', '1.0', '1'), + ('#.0f', '1.0', '1.'), + ('g', '1.1', '1.1'), + ('#g', '1.1', '1.1'), + ('.0g', '1', '1'), + ('#.0g', '1', '1.'), + ('.0%', '1.0', '100%'), + ('#.0%', '1.0', '100.%'), + ] + for fmt, d, result in test_values: + self.assertEqual(format(Decimal(d), fmt), result) + +class PyWhitebox(unittest.TestCase): + """White box testing for decimal.py""" + + def test_py_exact_power(self): + # Rarely exercised lines in _power_exact. + Decimal = P.Decimal + localcontext = P.localcontext + + with localcontext() as c: + c.prec = 8 + x = Decimal(2**16) ** Decimal("-0.5") + self.assertEqual(x, Decimal('0.00390625')) + + x = Decimal(2**16) ** Decimal("-0.6") + self.assertEqual(x, Decimal('0.0012885819')) + + x = Decimal("256e7") ** Decimal("-0.5") + + x = Decimal(152587890625) ** Decimal('-0.0625') + self.assertEqual(x, Decimal("0.2")) + + x = Decimal("152587890625e7") ** Decimal('-0.0625') + + x = Decimal(5**2659) ** Decimal('-0.0625') + + c.prec = 1 + x = Decimal("152587890625") ** Decimal('-0.5') + self.assertEqual(x, Decimal('3e-6')) + c.prec = 2 + x = Decimal("152587890625") ** Decimal('-0.5') + self.assertEqual(x, Decimal('2.6e-6')) + c.prec = 3 + x = Decimal("152587890625") ** Decimal('-0.5') + self.assertEqual(x, Decimal('2.56e-6')) + c.prec = 28 + x = Decimal("152587890625") ** Decimal('-0.5') + self.assertEqual(x, Decimal('2.56e-6')) + + c.prec = 201 + x = Decimal(2**578) ** Decimal("-0.5") + + # See https://github.com/python/cpython/issues/118027 + # Testing for an exact power could appear to hang, in the Python + # version, as it attempted to compute 10**(MAX_EMAX + 1). + # Fixed via https://github.com/python/cpython/pull/118503. + c.prec = P.MAX_PREC + c.Emax = P.MAX_EMAX + c.Emin = P.MIN_EMIN + c.traps[P.Inexact] = 1 + D2 = Decimal(2) + # If the bug is still present, the next statement won't complete. + res = D2 ** 117 + self.assertEqual(res, 1 << 117) + + def test_py_immutability_operations(self): + # Do operations and check that it didn't change internal objects. + Decimal = P.Decimal + DefaultContext = P.DefaultContext + setcontext = P.setcontext + + c = DefaultContext.copy() + c.traps = dict((s, 0) for s in OrderedSignals[P]) + setcontext(c) + + d1 = Decimal('-25e55') + b1 = Decimal('-25e55') + d2 = Decimal('33e+33') + b2 = Decimal('33e+33') + + def checkSameDec(operation, useOther=False): + if useOther: + eval("d1." + operation + "(d2)") + self.assertEqual(d1._sign, b1._sign) + self.assertEqual(d1._int, b1._int) + self.assertEqual(d1._exp, b1._exp) + self.assertEqual(d2._sign, b2._sign) + self.assertEqual(d2._int, b2._int) + self.assertEqual(d2._exp, b2._exp) + else: + eval("d1." + operation + "()") + self.assertEqual(d1._sign, b1._sign) + self.assertEqual(d1._int, b1._int) + self.assertEqual(d1._exp, b1._exp) + + Decimal(d1) + self.assertEqual(d1._sign, b1._sign) + self.assertEqual(d1._int, b1._int) + self.assertEqual(d1._exp, b1._exp) + + checkSameDec("__abs__") + checkSameDec("__add__", True) + checkSameDec("__divmod__", True) + checkSameDec("__eq__", True) + checkSameDec("__ne__", True) + checkSameDec("__le__", True) + checkSameDec("__lt__", True) + checkSameDec("__ge__", True) + checkSameDec("__gt__", True) + checkSameDec("__float__") + checkSameDec("__floordiv__", True) + checkSameDec("__hash__") + checkSameDec("__int__") + checkSameDec("__trunc__") + checkSameDec("__mod__", True) + checkSameDec("__mul__", True) + checkSameDec("__neg__") + checkSameDec("__bool__") + checkSameDec("__pos__") + checkSameDec("__pow__", True) + checkSameDec("__radd__", True) + checkSameDec("__rdivmod__", True) + checkSameDec("__repr__") + checkSameDec("__rfloordiv__", True) + checkSameDec("__rmod__", True) + checkSameDec("__rmul__", True) + checkSameDec("__rpow__", True) + checkSameDec("__rsub__", True) + checkSameDec("__str__") + checkSameDec("__sub__", True) + checkSameDec("__truediv__", True) + checkSameDec("adjusted") + checkSameDec("as_tuple") + checkSameDec("compare", True) + checkSameDec("max", True) + checkSameDec("min", True) + checkSameDec("normalize") + checkSameDec("quantize", True) + checkSameDec("remainder_near", True) + checkSameDec("same_quantum", True) + checkSameDec("sqrt") + checkSameDec("to_eng_string") + checkSameDec("to_integral") + + def test_py_decimal_id(self): + Decimal = P.Decimal + + d = Decimal(45) + e = Decimal(d) + self.assertEqual(str(e), '45') + self.assertNotEqual(id(d), id(e)) + + def test_py_rescale(self): + # Coverage + Decimal = P.Decimal + localcontext = P.localcontext + + with localcontext() as c: + x = Decimal("NaN")._rescale(3, P.ROUND_UP) + self.assertTrue(x.is_nan()) + + def test_py__round(self): + # Coverage + Decimal = P.Decimal + + self.assertRaises(ValueError, Decimal("3.1234")._round, 0, P.ROUND_UP) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_threading.py b/Lib/test/test_decimal/test_threading.py new file mode 100644 index 00000000000000..f25db400757488 --- /dev/null +++ b/Lib/test/test_decimal/test_threading.py @@ -0,0 +1,151 @@ +import unittest +from test.support import threading_helper +import threading +import contextvars +from . import (C, load_tests_for_base_classes, Signals, + setUpModule, tearDownModule) + + +# The following are two functions used to test threading in the next class + +def thfunc1(cls): + Decimal = cls.decimal.Decimal + InvalidOperation = cls.decimal.InvalidOperation + DivisionByZero = cls.decimal.DivisionByZero + Overflow = cls.decimal.Overflow + Underflow = cls.decimal.Underflow + Inexact = cls.decimal.Inexact + getcontext = cls.decimal.getcontext + localcontext = cls.decimal.localcontext + + d1 = Decimal(1) + d3 = Decimal(3) + test1 = d1/d3 + + cls.finish1.set() + cls.synchro.wait() + + test2 = d1/d3 + with localcontext() as c2: + cls.assertTrue(c2.flags[Inexact]) + cls.assertRaises(DivisionByZero, c2.divide, d1, 0) + cls.assertTrue(c2.flags[DivisionByZero]) + with localcontext() as c3: + cls.assertTrue(c3.flags[Inexact]) + cls.assertTrue(c3.flags[DivisionByZero]) + cls.assertRaises(InvalidOperation, c3.compare, d1, Decimal('sNaN')) + cls.assertTrue(c3.flags[InvalidOperation]) + del c3 + cls.assertFalse(c2.flags[InvalidOperation]) + del c2 + + cls.assertEqual(test1, Decimal('0.333333333333333333333333')) + cls.assertEqual(test2, Decimal('0.333333333333333333333333')) + + c1 = getcontext() + cls.assertTrue(c1.flags[Inexact]) + for sig in Overflow, Underflow, DivisionByZero, InvalidOperation: + cls.assertFalse(c1.flags[sig]) + +def thfunc2(cls): + Decimal = cls.decimal.Decimal + InvalidOperation = cls.decimal.InvalidOperation + DivisionByZero = cls.decimal.DivisionByZero + Overflow = cls.decimal.Overflow + Underflow = cls.decimal.Underflow + Inexact = cls.decimal.Inexact + getcontext = cls.decimal.getcontext + localcontext = cls.decimal.localcontext + + d1 = Decimal(1) + d3 = Decimal(3) + test1 = d1/d3 + + thiscontext = getcontext() + thiscontext.prec = 18 + test2 = d1/d3 + + with localcontext() as c2: + cls.assertTrue(c2.flags[Inexact]) + cls.assertRaises(Overflow, c2.multiply, Decimal('1e425000000'), 999) + cls.assertTrue(c2.flags[Overflow]) + with localcontext(thiscontext) as c3: + cls.assertTrue(c3.flags[Inexact]) + cls.assertFalse(c3.flags[Overflow]) + c3.traps[Underflow] = True + cls.assertRaises(Underflow, c3.divide, Decimal('1e-425000000'), 999) + cls.assertTrue(c3.flags[Underflow]) + del c3 + cls.assertFalse(c2.flags[Underflow]) + cls.assertFalse(c2.traps[Underflow]) + del c2 + + cls.synchro.set() + cls.finish2.set() + + cls.assertEqual(test1, Decimal('0.333333333333333333333333')) + cls.assertEqual(test2, Decimal('0.333333333333333333')) + + cls.assertFalse(thiscontext.traps[Underflow]) + cls.assertTrue(thiscontext.flags[Inexact]) + for sig in Overflow, Underflow, DivisionByZero, InvalidOperation: + cls.assertFalse(thiscontext.flags[sig]) + + +@threading_helper.requires_working_threading() +class ThreadingTest: + '''Unit tests for thread local contexts in Decimal.''' + + # Take care executing this test from IDLE, there's an issue in threading + # that hangs IDLE and I couldn't find it + + def test_threading(self): + DefaultContext = self.decimal.DefaultContext + + if self.decimal == C and not self.decimal.HAVE_THREADS: + self.skipTest("compiled without threading") + # Test the "threading isolation" of a Context. Also test changing + # the DefaultContext, which acts as a template for the thread-local + # contexts. + save_prec = DefaultContext.prec + save_emax = DefaultContext.Emax + save_emin = DefaultContext.Emin + DefaultContext.prec = 24 + DefaultContext.Emax = 425000000 + DefaultContext.Emin = -425000000 + + self.synchro = threading.Event() + self.finish1 = threading.Event() + self.finish2 = threading.Event() + + # This test wants to start threads with an empty context, no matter + # the setting of sys.flags.thread_inherit_context. We pass the + # 'context' argument explicitly with an empty context instance. + th1 = threading.Thread(target=thfunc1, args=(self,), + context=contextvars.Context()) + th2 = threading.Thread(target=thfunc2, args=(self,), + context=contextvars.Context()) + + th1.start() + th2.start() + + self.finish1.wait() + self.finish2.wait() + + for sig in Signals[self.decimal]: + self.assertFalse(DefaultContext.flags[sig]) + + th1.join() + th2.join() + + DefaultContext.prec = save_prec + DefaultContext.Emax = save_emax + DefaultContext.Emin = save_emin + + +def load_tests(loader, tests, pattern): + return load_tests_for_base_classes(loader, tests, [ThreadingTest]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_decimal/test_usability.py b/Lib/test/test_decimal/test_usability.py new file mode 100644 index 00000000000000..cd10c129d00ce1 --- /dev/null +++ b/Lib/test/test_decimal/test_usability.py @@ -0,0 +1,858 @@ +import unittest +import math +import sys +import copy +import random +from test.support.import_helper import import_fresh_module +from . import (C, P, requires_cdecimal, orig_sys_decimal, + setUpModule, tearDownModule) + + +# fractions module must import the correct decimal module. +sys.modules['decimal'] = C +cfractions = import_fresh_module('fractions', fresh=['fractions']) +sys.modules['decimal'] = P +pfractions = import_fresh_module('fractions', fresh=['fractions']) +sys.modules['decimal'] = orig_sys_decimal + +fractions = {C: cfractions, P: pfractions} + +class UsabilityTest: + '''Unit tests for Usability cases of Decimal.''' + + def test_comparison_operators(self): + + Decimal = self.decimal.Decimal + + da = Decimal('23.42') + db = Decimal('23.42') + dc = Decimal('45') + + #two Decimals + self.assertGreater(dc, da) + self.assertGreaterEqual(dc, da) + self.assertLess(da, dc) + self.assertLessEqual(da, dc) + self.assertEqual(da, db) + self.assertNotEqual(da, dc) + self.assertLessEqual(da, db) + self.assertGreaterEqual(da, db) + + #a Decimal and an int + self.assertGreater(dc, 23) + self.assertLess(23, dc) + self.assertEqual(dc, 45) + + #a Decimal and uncomparable + self.assertNotEqual(da, 'ugly') + self.assertNotEqual(da, 32.7) + self.assertNotEqual(da, object()) + self.assertNotEqual(da, object) + + # sortable + a = list(map(Decimal, range(100))) + b = a[:] + random.shuffle(a) + a.sort() + self.assertEqual(a, b) + + def test_decimal_float_comparison(self): + Decimal = self.decimal.Decimal + + da = Decimal('0.25') + db = Decimal('3.0') + self.assertLess(da, 3.0) + self.assertLessEqual(da, 3.0) + self.assertGreater(db, 0.25) + self.assertGreaterEqual(db, 0.25) + self.assertNotEqual(da, 1.5) + self.assertEqual(da, 0.25) + self.assertGreater(3.0, da) + self.assertGreaterEqual(3.0, da) + self.assertLess(0.25, db) + self.assertLessEqual(0.25, db) + self.assertNotEqual(0.25, db) + self.assertEqual(3.0, db) + self.assertNotEqual(0.1, Decimal('0.1')) + + def test_decimal_complex_comparison(self): + Decimal = self.decimal.Decimal + + da = Decimal('0.25') + db = Decimal('3.0') + self.assertNotEqual(da, (1.5+0j)) + self.assertNotEqual((1.5+0j), da) + self.assertEqual(da, (0.25+0j)) + self.assertEqual((0.25+0j), da) + self.assertEqual((3.0+0j), db) + self.assertEqual(db, (3.0+0j)) + + self.assertNotEqual(db, (3.0+1j)) + self.assertNotEqual((3.0+1j), db) + + self.assertIs(db.__lt__(3.0+0j), NotImplemented) + self.assertIs(db.__le__(3.0+0j), NotImplemented) + self.assertIs(db.__gt__(3.0+0j), NotImplemented) + self.assertIs(db.__le__(3.0+0j), NotImplemented) + + def test_decimal_fraction_comparison(self): + D = self.decimal.Decimal + F = fractions[self.decimal].Fraction + Context = self.decimal.Context + localcontext = self.decimal.localcontext + InvalidOperation = self.decimal.InvalidOperation + + + emax = C.MAX_EMAX if C else 999999999 + emin = C.MIN_EMIN if C else -999999999 + etiny = C.MIN_ETINY if C else -1999999997 + c = Context(Emax=emax, Emin=emin) + + with localcontext(c): + c.prec = emax + self.assertLess(D(0), F(1,9999999999999999999999999999999999999)) + self.assertLess(F(-1,9999999999999999999999999999999999999), D(0)) + self.assertLess(F(0,1), D("1e" + str(etiny))) + self.assertLess(D("-1e" + str(etiny)), F(0,1)) + self.assertLess(F(0,9999999999999999999999999), D("1e" + str(etiny))) + self.assertLess(D("-1e" + str(etiny)), F(0,9999999999999999999999999)) + + self.assertEqual(D("0.1"), F(1,10)) + self.assertEqual(F(1,10), D("0.1")) + + c.prec = 300 + self.assertNotEqual(D(1)/3, F(1,3)) + self.assertNotEqual(F(1,3), D(1)/3) + + self.assertLessEqual(F(120984237, 9999999999), D("9e" + str(emax))) + self.assertGreaterEqual(D("9e" + str(emax)), F(120984237, 9999999999)) + + self.assertGreater(D('inf'), F(99999999999,123)) + self.assertGreater(D('inf'), F(-99999999999,123)) + self.assertLess(D('-inf'), F(99999999999,123)) + self.assertLess(D('-inf'), F(-99999999999,123)) + + self.assertRaises(InvalidOperation, D('nan').__gt__, F(-9,123)) + self.assertIs(NotImplemented, F(-9,123).__lt__(D('nan'))) + self.assertNotEqual(D('nan'), F(-9,123)) + self.assertNotEqual(F(-9,123), D('nan')) + + def test_copy_and_deepcopy_methods(self): + Decimal = self.decimal.Decimal + + d = Decimal('43.24') + c = copy.copy(d) + self.assertEqual(id(c), id(d)) + dc = copy.deepcopy(d) + self.assertEqual(id(dc), id(d)) + + def test_hash_method(self): + + Decimal = self.decimal.Decimal + localcontext = self.decimal.localcontext + + def hashit(d): + a = hash(d) + b = d.__hash__() + self.assertEqual(a, b) + return a + + #just that it's hashable + hashit(Decimal(23)) + hashit(Decimal('Infinity')) + hashit(Decimal('-Infinity')) + hashit(Decimal('nan123')) + hashit(Decimal('-NaN')) + + test_values = [Decimal(sign*(2**m + n)) + for m in [0, 14, 15, 16, 17, 30, 31, + 32, 33, 61, 62, 63, 64, 65, 66] + for n in range(-10, 10) + for sign in [-1, 1]] + test_values.extend([ + Decimal("-1"), # ==> -2 + Decimal("-0"), # zeros + Decimal("0.00"), + Decimal("-0.000"), + Decimal("0E10"), + Decimal("-0E12"), + Decimal("10.0"), # negative exponent + Decimal("-23.00000"), + Decimal("1230E100"), # positive exponent + Decimal("-4.5678E50"), + # a value for which hash(n) != hash(n % (2**64-1)) + # in Python pre-2.6 + Decimal(2**64 + 2**32 - 1), + # selection of values which fail with the old (before + # version 2.6) long.__hash__ + Decimal("1.634E100"), + Decimal("90.697E100"), + Decimal("188.83E100"), + Decimal("1652.9E100"), + Decimal("56531E100"), + ]) + + # check that hash(d) == hash(int(d)) for integral values + for value in test_values: + self.assertEqual(hashit(value), hash(int(value))) + + # check that the hashes of a Decimal float match when they + # represent exactly the same values + test_strings = ['inf', '-Inf', '0.0', '-.0e1', + '34.0', '2.5', '112390.625', '-0.515625'] + for s in test_strings: + f = float(s) + d = Decimal(s) + self.assertEqual(hashit(d), hash(f)) + + with localcontext() as c: + # check that the value of the hash doesn't depend on the + # current context (issue #1757) + x = Decimal("123456789.1") + + c.prec = 6 + h1 = hashit(x) + c.prec = 10 + h2 = hashit(x) + c.prec = 16 + h3 = hashit(x) + + self.assertEqual(h1, h2) + self.assertEqual(h1, h3) + + c.prec = 10000 + x = 1100 ** 1248 + self.assertEqual(hashit(Decimal(x)), hashit(x)) + + def test_hash_method_nan(self): + Decimal = self.decimal.Decimal + self.assertRaises(TypeError, hash, Decimal('sNaN')) + value = Decimal('NaN') + self.assertEqual(hash(value), object.__hash__(value)) + class H: + def __hash__(self): + return 42 + class D(Decimal, H): + pass + value = D('NaN') + self.assertEqual(hash(value), object.__hash__(value)) + + def test_min_and_max_methods(self): + Decimal = self.decimal.Decimal + + d1 = Decimal('15.32') + d2 = Decimal('28.5') + l1 = 15 + l2 = 28 + + #between Decimals + self.assertIs(min(d1,d2), d1) + self.assertIs(min(d2,d1), d1) + self.assertIs(max(d1,d2), d2) + self.assertIs(max(d2,d1), d2) + + #between Decimal and int + self.assertIs(min(d1,l2), d1) + self.assertIs(min(l2,d1), d1) + self.assertIs(max(l1,d2), d2) + self.assertIs(max(d2,l1), d2) + + def test_as_nonzero(self): + Decimal = self.decimal.Decimal + + #as false + self.assertFalse(Decimal(0)) + #as true + self.assertTrue(Decimal('0.372')) + + def test_tostring_methods(self): + #Test str and repr methods. + Decimal = self.decimal.Decimal + + d = Decimal('15.32') + self.assertEqual(str(d), '15.32') # str + self.assertEqual(repr(d), "Decimal('15.32')") # repr + + def test_tonum_methods(self): + #Test float and int methods. + Decimal = self.decimal.Decimal + + d1 = Decimal('66') + d2 = Decimal('15.32') + + #int + self.assertEqual(int(d1), 66) + self.assertEqual(int(d2), 15) + + #float + self.assertEqual(float(d1), 66) + self.assertEqual(float(d2), 15.32) + + #floor + test_pairs = [ + ('123.00', 123), + ('3.2', 3), + ('3.54', 3), + ('3.899', 3), + ('-2.3', -3), + ('-11.0', -11), + ('0.0', 0), + ('-0E3', 0), + ('89891211712379812736.1', 89891211712379812736), + ] + for d, i in test_pairs: + self.assertEqual(math.floor(Decimal(d)), i) + self.assertRaises(ValueError, math.floor, Decimal('-NaN')) + self.assertRaises(ValueError, math.floor, Decimal('sNaN')) + self.assertRaises(ValueError, math.floor, Decimal('NaN123')) + self.assertRaises(OverflowError, math.floor, Decimal('Inf')) + self.assertRaises(OverflowError, math.floor, Decimal('-Inf')) + + #ceiling + test_pairs = [ + ('123.00', 123), + ('3.2', 4), + ('3.54', 4), + ('3.899', 4), + ('-2.3', -2), + ('-11.0', -11), + ('0.0', 0), + ('-0E3', 0), + ('89891211712379812736.1', 89891211712379812737), + ] + for d, i in test_pairs: + self.assertEqual(math.ceil(Decimal(d)), i) + self.assertRaises(ValueError, math.ceil, Decimal('-NaN')) + self.assertRaises(ValueError, math.ceil, Decimal('sNaN')) + self.assertRaises(ValueError, math.ceil, Decimal('NaN123')) + self.assertRaises(OverflowError, math.ceil, Decimal('Inf')) + self.assertRaises(OverflowError, math.ceil, Decimal('-Inf')) + + #round, single argument + test_pairs = [ + ('123.00', 123), + ('3.2', 3), + ('3.54', 4), + ('3.899', 4), + ('-2.3', -2), + ('-11.0', -11), + ('0.0', 0), + ('-0E3', 0), + ('-3.5', -4), + ('-2.5', -2), + ('-1.5', -2), + ('-0.5', 0), + ('0.5', 0), + ('1.5', 2), + ('2.5', 2), + ('3.5', 4), + ] + for d, i in test_pairs: + self.assertEqual(round(Decimal(d)), i) + self.assertRaises(ValueError, round, Decimal('-NaN')) + self.assertRaises(ValueError, round, Decimal('sNaN')) + self.assertRaises(ValueError, round, Decimal('NaN123')) + self.assertRaises(OverflowError, round, Decimal('Inf')) + self.assertRaises(OverflowError, round, Decimal('-Inf')) + + #round, two arguments; this is essentially equivalent + #to quantize, which is already extensively tested + test_triples = [ + ('123.456', -4, '0E+4'), + ('-123.456', -4, '-0E+4'), + ('123.456', -3, '0E+3'), + ('-123.456', -3, '-0E+3'), + ('123.456', -2, '1E+2'), + ('123.456', -1, '1.2E+2'), + ('123.456', 0, '123'), + ('123.456', 1, '123.5'), + ('123.456', 2, '123.46'), + ('123.456', 3, '123.456'), + ('123.456', 4, '123.4560'), + ('123.455', 2, '123.46'), + ('123.445', 2, '123.44'), + ('Inf', 4, 'NaN'), + ('-Inf', -23, 'NaN'), + ('sNaN314', 3, 'NaN314'), + ] + for d, n, r in test_triples: + self.assertEqual(str(round(Decimal(d), n)), r) + + def test_nan_to_float(self): + # Test conversions of decimal NANs to float. + # See http://bugs.python.org/issue15544 + Decimal = self.decimal.Decimal + for s in ('nan', 'nan1234', '-nan', '-nan2468'): + f = float(Decimal(s)) + self.assertTrue(math.isnan(f)) + sign = math.copysign(1.0, f) + self.assertEqual(sign, -1.0 if s.startswith('-') else 1.0) + + def test_snan_to_float(self): + Decimal = self.decimal.Decimal + for s in ('snan', '-snan', 'snan1357', '-snan1234'): + d = Decimal(s) + self.assertRaises(ValueError, float, d) + + def test_eval_round_trip(self): + Decimal = self.decimal.Decimal + + #with zero + d = Decimal( (0, (0,), 0) ) + self.assertEqual(d, eval(repr(d))) + + #int + d = Decimal( (1, (4, 5), 0) ) + self.assertEqual(d, eval(repr(d))) + + #float + d = Decimal( (0, (4, 5, 3, 4), -2) ) + self.assertEqual(d, eval(repr(d))) + + #weird + d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) + self.assertEqual(d, eval(repr(d))) + + def test_as_tuple(self): + Decimal = self.decimal.Decimal + + #with zero + d = Decimal(0) + self.assertEqual(d.as_tuple(), (0, (0,), 0) ) + + #int + d = Decimal(-45) + self.assertEqual(d.as_tuple(), (1, (4, 5), 0) ) + + #complicated string + d = Decimal("-4.34913534E-17") + self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) + + # The '0' coefficient is implementation specific to decimal.py. + # It has no meaning in the C-version and is ignored there. + d = Decimal("Infinity") + self.assertEqual(d.as_tuple(), (0, (0,), 'F') ) + + #leading zeros in coefficient should be stripped + d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), -2) ) + self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), -2) ) + d = Decimal( (1, (0, 0, 0), 37) ) + self.assertEqual(d.as_tuple(), (1, (0,), 37)) + d = Decimal( (1, (), 37) ) + self.assertEqual(d.as_tuple(), (1, (0,), 37)) + + #leading zeros in NaN diagnostic info should be stripped + d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), 'n') ) + self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), 'n') ) + d = Decimal( (1, (0, 0, 0), 'N') ) + self.assertEqual(d.as_tuple(), (1, (), 'N') ) + d = Decimal( (1, (), 'n') ) + self.assertEqual(d.as_tuple(), (1, (), 'n') ) + + # For infinities, decimal.py has always silently accepted any + # coefficient tuple. + d = Decimal( (0, (0,), 'F') ) + self.assertEqual(d.as_tuple(), (0, (0,), 'F')) + d = Decimal( (0, (4, 5, 3, 4), 'F') ) + self.assertEqual(d.as_tuple(), (0, (0,), 'F')) + d = Decimal( (1, (0, 2, 7, 1), 'F') ) + self.assertEqual(d.as_tuple(), (1, (0,), 'F')) + + def test_as_integer_ratio(self): + Decimal = self.decimal.Decimal + + # exceptional cases + self.assertRaises(OverflowError, + Decimal.as_integer_ratio, Decimal('inf')) + self.assertRaises(OverflowError, + Decimal.as_integer_ratio, Decimal('-inf')) + self.assertRaises(ValueError, + Decimal.as_integer_ratio, Decimal('-nan')) + self.assertRaises(ValueError, + Decimal.as_integer_ratio, Decimal('snan123')) + + for exp in range(-4, 2): + for coeff in range(1000): + for sign in '+', '-': + d = Decimal('%s%dE%d' % (sign, coeff, exp)) + pq = d.as_integer_ratio() + p, q = pq + + # check return type + self.assertIsInstance(pq, tuple) + self.assertIsInstance(p, int) + self.assertIsInstance(q, int) + + # check normalization: q should be positive; + # p should be relatively prime to q. + self.assertGreater(q, 0) + self.assertEqual(math.gcd(p, q), 1) + + # check that p/q actually gives the correct value + self.assertEqual(Decimal(p) / Decimal(q), d) + + def test_subclassing(self): + # Different behaviours when subclassing Decimal + Decimal = self.decimal.Decimal + + class MyDecimal(Decimal): + y = None + + d1 = MyDecimal(1) + d2 = MyDecimal(2) + d = d1 + d2 + self.assertIs(type(d), Decimal) + + d = d1.max(d2) + self.assertIs(type(d), Decimal) + + d = copy.copy(d1) + self.assertIs(type(d), MyDecimal) + self.assertEqual(d, d1) + + d = copy.deepcopy(d1) + self.assertIs(type(d), MyDecimal) + self.assertEqual(d, d1) + + # Decimal(Decimal) + d = Decimal('1.0') + x = Decimal(d) + self.assertIs(type(x), Decimal) + self.assertEqual(x, d) + + # MyDecimal(Decimal) + m = MyDecimal(d) + self.assertIs(type(m), MyDecimal) + self.assertEqual(m, d) + self.assertIs(m.y, None) + + # Decimal(MyDecimal) + x = Decimal(m) + self.assertIs(type(x), Decimal) + self.assertEqual(x, d) + + # MyDecimal(MyDecimal) + m.y = 9 + x = MyDecimal(m) + self.assertIs(type(x), MyDecimal) + self.assertEqual(x, d) + self.assertIs(x.y, None) + + def test_implicit_context(self): + Decimal = self.decimal.Decimal + getcontext = self.decimal.getcontext + + # Check results when context given implicitly. (Issue 2478) + c = getcontext() + self.assertEqual(str(Decimal(0).sqrt()), + str(c.sqrt(Decimal(0)))) + + def test_none_args(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + localcontext = self.decimal.localcontext + InvalidOperation = self.decimal.InvalidOperation + DivisionByZero = self.decimal.DivisionByZero + Overflow = self.decimal.Overflow + Underflow = self.decimal.Underflow + Subnormal = self.decimal.Subnormal + Inexact = self.decimal.Inexact + Rounded = self.decimal.Rounded + Clamped = self.decimal.Clamped + + with localcontext(Context()) as c: + c.prec = 7 + c.Emax = 999 + c.Emin = -999 + + x = Decimal("111") + y = Decimal("1e9999") + z = Decimal("1e-9999") + + ##### Unary functions + c.clear_flags() + self.assertEqual(str(x.exp(context=None)), '1.609487E+48') + self.assertTrue(c.flags[Inexact]) + self.assertTrue(c.flags[Rounded]) + c.clear_flags() + self.assertRaises(Overflow, y.exp, context=None) + self.assertTrue(c.flags[Overflow]) + + self.assertIs(z.is_normal(context=None), False) + self.assertIs(z.is_subnormal(context=None), True) + + c.clear_flags() + self.assertEqual(str(x.ln(context=None)), '4.709530') + self.assertTrue(c.flags[Inexact]) + self.assertTrue(c.flags[Rounded]) + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal(-1).ln, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(x.log10(context=None)), '2.045323') + self.assertTrue(c.flags[Inexact]) + self.assertTrue(c.flags[Rounded]) + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal(-1).log10, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(x.logb(context=None)), '2') + self.assertRaises(DivisionByZero, Decimal(0).logb, context=None) + self.assertTrue(c.flags[DivisionByZero]) + + c.clear_flags() + self.assertEqual(str(x.logical_invert(context=None)), '1111000') + self.assertRaises(InvalidOperation, y.logical_invert, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(y.next_minus(context=None)), '9.999999E+999') + self.assertRaises(InvalidOperation, Decimal('sNaN').next_minus, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(y.next_plus(context=None)), 'Infinity') + self.assertRaises(InvalidOperation, Decimal('sNaN').next_plus, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(z.normalize(context=None)), '0') + self.assertRaises(Overflow, y.normalize, context=None) + self.assertTrue(c.flags[Overflow]) + + self.assertEqual(str(z.number_class(context=None)), '+Subnormal') + + c.clear_flags() + self.assertEqual(str(z.sqrt(context=None)), '0E-1005') + self.assertTrue(c.flags[Clamped]) + self.assertTrue(c.flags[Inexact]) + self.assertTrue(c.flags[Rounded]) + self.assertTrue(c.flags[Subnormal]) + self.assertTrue(c.flags[Underflow]) + c.clear_flags() + self.assertRaises(Overflow, y.sqrt, context=None) + self.assertTrue(c.flags[Overflow]) + + c.capitals = 0 + self.assertEqual(str(z.to_eng_string(context=None)), '1e-9999') + c.capitals = 1 + + + ##### Binary functions + c.clear_flags() + ans = str(x.compare(Decimal('Nan891287828'), context=None)) + self.assertEqual(ans, 'NaN1287828') + self.assertRaises(InvalidOperation, x.compare, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.compare_signal(8224, context=None)) + self.assertEqual(ans, '-1') + self.assertRaises(InvalidOperation, x.compare_signal, Decimal('NaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.logical_and(101, context=None)) + self.assertEqual(ans, '101') + self.assertRaises(InvalidOperation, x.logical_and, 123, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.logical_or(101, context=None)) + self.assertEqual(ans, '111') + self.assertRaises(InvalidOperation, x.logical_or, 123, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.logical_xor(101, context=None)) + self.assertEqual(ans, '10') + self.assertRaises(InvalidOperation, x.logical_xor, 123, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.max(101, context=None)) + self.assertEqual(ans, '111') + self.assertRaises(InvalidOperation, x.max, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.max_mag(101, context=None)) + self.assertEqual(ans, '111') + self.assertRaises(InvalidOperation, x.max_mag, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.min(101, context=None)) + self.assertEqual(ans, '101') + self.assertRaises(InvalidOperation, x.min, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.min_mag(101, context=None)) + self.assertEqual(ans, '101') + self.assertRaises(InvalidOperation, x.min_mag, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.remainder_near(101, context=None)) + self.assertEqual(ans, '10') + self.assertRaises(InvalidOperation, y.remainder_near, 101, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.rotate(2, context=None)) + self.assertEqual(ans, '11100') + self.assertRaises(InvalidOperation, x.rotate, 101, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.scaleb(7, context=None)) + self.assertEqual(ans, '1.11E+9') + self.assertRaises(InvalidOperation, x.scaleb, 10000, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.shift(2, context=None)) + self.assertEqual(ans, '11100') + self.assertRaises(InvalidOperation, x.shift, 10000, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + + ##### Ternary functions + c.clear_flags() + ans = str(x.fma(2, 3, context=None)) + self.assertEqual(ans, '225') + self.assertRaises(Overflow, x.fma, Decimal('1e9999'), 3, context=None) + self.assertTrue(c.flags[Overflow]) + + + ##### Special cases + c.rounding = P.ROUND_HALF_EVEN + ans = str(Decimal('1.5').to_integral(rounding=None, context=None)) + self.assertEqual(ans, '2') + c.rounding = P.ROUND_DOWN + ans = str(Decimal('1.5').to_integral(rounding=None, context=None)) + self.assertEqual(ans, '1') + ans = str(Decimal('1.5').to_integral(rounding=P.ROUND_UP, context=None)) + self.assertEqual(ans, '2') + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.rounding = P.ROUND_HALF_EVEN + ans = str(Decimal('1.5').to_integral_value(rounding=None, context=None)) + self.assertEqual(ans, '2') + c.rounding = P.ROUND_DOWN + ans = str(Decimal('1.5').to_integral_value(rounding=None, context=None)) + self.assertEqual(ans, '1') + ans = str(Decimal('1.5').to_integral_value(rounding=P.ROUND_UP, context=None)) + self.assertEqual(ans, '2') + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral_value, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.rounding = P.ROUND_HALF_EVEN + ans = str(Decimal('1.5').to_integral_exact(rounding=None, context=None)) + self.assertEqual(ans, '2') + c.rounding = P.ROUND_DOWN + ans = str(Decimal('1.5').to_integral_exact(rounding=None, context=None)) + self.assertEqual(ans, '1') + ans = str(Decimal('1.5').to_integral_exact(rounding=P.ROUND_UP, context=None)) + self.assertEqual(ans, '2') + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral_exact, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.rounding = P.ROUND_UP + ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=None, context=None)) + self.assertEqual(ans, '1.501') + c.rounding = P.ROUND_DOWN + ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=None, context=None)) + self.assertEqual(ans, '1.500') + ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=P.ROUND_UP, context=None)) + self.assertEqual(ans, '1.501') + c.clear_flags() + self.assertRaises(InvalidOperation, y.quantize, Decimal('1e-10'), rounding=P.ROUND_UP, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + with localcontext(Context()) as context: + context.prec = 7 + context.Emax = 999 + context.Emin = -999 + with localcontext(ctx=None) as c: + self.assertEqual(c.prec, 7) + self.assertEqual(c.Emax, 999) + self.assertEqual(c.Emin, -999) + + def test_conversions_from_int(self): + # Check that methods taking a second Decimal argument will + # always accept an integer in place of a Decimal. + Decimal = self.decimal.Decimal + + self.assertEqual(Decimal(4).compare(3), + Decimal(4).compare(Decimal(3))) + self.assertEqual(Decimal(4).compare_signal(3), + Decimal(4).compare_signal(Decimal(3))) + self.assertEqual(Decimal(4).compare_total(3), + Decimal(4).compare_total(Decimal(3))) + self.assertEqual(Decimal(4).compare_total_mag(3), + Decimal(4).compare_total_mag(Decimal(3))) + self.assertEqual(Decimal(10101).logical_and(1001), + Decimal(10101).logical_and(Decimal(1001))) + self.assertEqual(Decimal(10101).logical_or(1001), + Decimal(10101).logical_or(Decimal(1001))) + self.assertEqual(Decimal(10101).logical_xor(1001), + Decimal(10101).logical_xor(Decimal(1001))) + self.assertEqual(Decimal(567).max(123), + Decimal(567).max(Decimal(123))) + self.assertEqual(Decimal(567).max_mag(123), + Decimal(567).max_mag(Decimal(123))) + self.assertEqual(Decimal(567).min(123), + Decimal(567).min(Decimal(123))) + self.assertEqual(Decimal(567).min_mag(123), + Decimal(567).min_mag(Decimal(123))) + self.assertEqual(Decimal(567).next_toward(123), + Decimal(567).next_toward(Decimal(123))) + self.assertEqual(Decimal(1234).quantize(100), + Decimal(1234).quantize(Decimal(100))) + self.assertEqual(Decimal(768).remainder_near(1234), + Decimal(768).remainder_near(Decimal(1234))) + self.assertEqual(Decimal(123).rotate(1), + Decimal(123).rotate(Decimal(1))) + self.assertEqual(Decimal(1234).same_quantum(1000), + Decimal(1234).same_quantum(Decimal(1000))) + self.assertEqual(Decimal('9.123').scaleb(-100), + Decimal('9.123').scaleb(Decimal(-100))) + self.assertEqual(Decimal(456).shift(-1), + Decimal(456).shift(Decimal(-1))) + + self.assertEqual(Decimal(-12).fma(Decimal(45), 67), + Decimal(-12).fma(Decimal(45), Decimal(67))) + self.assertEqual(Decimal(-12).fma(45, 67), + Decimal(-12).fma(Decimal(45), Decimal(67))) + self.assertEqual(Decimal(-12).fma(45, Decimal(67)), + Decimal(-12).fma(Decimal(45), Decimal(67))) + + +@requires_cdecimal +class CUsabilityTest(UsabilityTest, unittest.TestCase): + decimal = C + + +class PyUsabilityTest(UsabilityTest, unittest.TestCase): + decimal = P + + def setUp(self): + super().setUp() + self._previous_int_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(7000) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_int_limit) + super().tearDown() + + +if __name__ == '__main__': + unittest.main() diff --git a/Makefile.pre.in b/Makefile.pre.in index dd28ff5d2a3ed1..179f799f40353a 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2607,7 +2607,8 @@ TESTSUBDIRS= idlelib/idle_test \ test/configdata \ test/crashers \ test/data \ - test/decimaltestdata \ + test/test_decimal \ + test/test_decimal/data \ test/dtracedata \ test/encoded_modules \ test/leakers \ diff --git a/Misc/NEWS.d/next/Tests/2025-11-10-18-31-48.gh-issue-141360.VQLerk.rst b/Misc/NEWS.d/next/Tests/2025-11-10-18-31-48.gh-issue-141360.VQLerk.rst new file mode 100644 index 00000000000000..f135d6dfdb38be --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2025-11-10-18-31-48.gh-issue-141360.VQLerk.rst @@ -0,0 +1,2 @@ +Tests for the :mod:`decimal` module have been split into several files. +Patch by Mikhail Efimov.