Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 59a4a93

Browse files
author
Stefan Krah
committed
Issue #16422: Use strings for rounding mode constants for better readability
and pickling compatibility.
1 parent 0ad344a commit 59a4a93

4 files changed

Lines changed: 126 additions & 152 deletions

File tree

Lib/test/test_decimal.py

Lines changed: 54 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,20 @@ def assert_signals(cls, context, attr, expected):
7878
d = getattr(context, attr)
7979
cls.assertTrue(all(d[s] if s in expected else not d[s] for s in d))
8080

81-
RoundingModes = {
82-
C: (C.ROUND_UP, C.ROUND_DOWN, C.ROUND_CEILING, C.ROUND_FLOOR,
83-
C.ROUND_HALF_UP, C.ROUND_HALF_DOWN, C.ROUND_HALF_EVEN,
84-
C.ROUND_05UP) if C else None,
85-
P: (P.ROUND_UP, P.ROUND_DOWN, P.ROUND_CEILING, P.ROUND_FLOOR,
86-
P.ROUND_HALF_UP, P.ROUND_HALF_DOWN, P.ROUND_HALF_EVEN,
87-
P.ROUND_05UP)
88-
}
81+
ROUND_UP = P.ROUND_UP
82+
ROUND_DOWN = P.ROUND_DOWN
83+
ROUND_CEILING = P.ROUND_CEILING
84+
ROUND_FLOOR = P.ROUND_FLOOR
85+
ROUND_HALF_UP = P.ROUND_HALF_UP
86+
ROUND_HALF_DOWN = P.ROUND_HALF_DOWN
87+
ROUND_HALF_EVEN = P.ROUND_HALF_EVEN
88+
ROUND_05UP = P.ROUND_05UP
89+
90+
RoundingModes = [
91+
ROUND_UP, ROUND_DOWN, ROUND_CEILING, ROUND_FLOOR,
92+
ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN,
93+
ROUND_05UP
94+
]
8995

9096
# Tests are built around these assumed context defaults.
9197
# test_main() restores the original context.
@@ -96,7 +102,7 @@ def assert_signals(cls, context, attr, expected):
96102
def init(m):
97103
if not m: return
98104
DefaultTestContext = m.Context(
99-
prec=9, rounding=m.ROUND_HALF_EVEN, traps=dict.fromkeys(Signals[m], 0)
105+
prec=9, rounding=ROUND_HALF_EVEN, traps=dict.fromkeys(Signals[m], 0)
100106
)
101107
m.setcontext(DefaultTestContext)
102108

@@ -229,14 +235,14 @@ def setUp(self):
229235
'xor':'logical_xor'}
230236

231237
# Map test-case names to roundings.
232-
self.RoundingDict = {'ceiling' : self.decimal.ROUND_CEILING,
233-
'down' : self.decimal.ROUND_DOWN,
234-
'floor' : self.decimal.ROUND_FLOOR,
235-
'half_down' : self.decimal.ROUND_HALF_DOWN,
236-
'half_even' : self.decimal.ROUND_HALF_EVEN,
237-
'half_up' : self.decimal.ROUND_HALF_UP,
238-
'up' : self.decimal.ROUND_UP,
239-
'05up' : self.decimal.ROUND_05UP}
238+
self.RoundingDict = {'ceiling' : ROUND_CEILING,
239+
'down' : ROUND_DOWN,
240+
'floor' : ROUND_FLOOR,
241+
'half_down' : ROUND_HALF_DOWN,
242+
'half_even' : ROUND_HALF_EVEN,
243+
'half_up' : ROUND_HALF_UP,
244+
'up' : ROUND_UP,
245+
'05up' : ROUND_05UP}
240246

241247
# Map the test cases' error names to the actual errors.
242248
self.ErrorNames = {'clamped' : self.decimal.Clamped,
@@ -2101,9 +2107,6 @@ def test_none_args(self):
21012107
Inexact = self.decimal.Inexact
21022108
Rounded = self.decimal.Rounded
21032109
Clamped = self.decimal.Clamped
2104-
ROUND_HALF_EVEN = self.decimal.ROUND_HALF_EVEN
2105-
ROUND_DOWN = self.decimal.ROUND_DOWN
2106-
ROUND_UP = self.decimal.ROUND_UP
21072110

21082111
with localcontext(Context()) as c:
21092112
c.prec = 7
@@ -2430,7 +2433,6 @@ def test_pickle(self):
24302433

24312434
def test_int(self):
24322435
Decimal = self.decimal.Decimal
2433-
ROUND_DOWN = self.decimal.ROUND_DOWN
24342436

24352437
for x in range(-250, 250):
24362438
s = '%0.2f' % (x / 100.0)
@@ -2448,7 +2450,6 @@ def test_int(self):
24482450

24492451
def test_trunc(self):
24502452
Decimal = self.decimal.Decimal
2451-
ROUND_DOWN = self.decimal.ROUND_DOWN
24522453

24532454
for x in range(-250, 250):
24542455
s = '%0.2f' % (x / 100.0)
@@ -2491,8 +2492,6 @@ class MyDecimal(Decimal):
24912492
def test_create_decimal_from_float(self):
24922493
Decimal = self.decimal.Decimal
24932494
Context = self.decimal.Context
2494-
ROUND_DOWN = self.decimal.ROUND_DOWN
2495-
ROUND_UP = self.decimal.ROUND_UP
24962495
Inexact = self.decimal.Inexact
24972496

24982497
context = Context(prec=5, rounding=ROUND_DOWN)
@@ -2522,7 +2521,6 @@ def test_quantize(self):
25222521
Decimal = self.decimal.Decimal
25232522
Context = self.decimal.Context
25242523
InvalidOperation = self.decimal.InvalidOperation
2525-
ROUND_DOWN = self.decimal.ROUND_DOWN
25262524

25272525
c = Context(Emax=99999, Emin=-99999)
25282526
self.assertEqual(
@@ -2723,7 +2721,6 @@ def test_none_args(self):
27232721
InvalidOperation = self.decimal.InvalidOperation
27242722
DivisionByZero = self.decimal.DivisionByZero
27252723
Overflow = self.decimal.Overflow
2726-
ROUND_HALF_EVEN = self.decimal.ROUND_HALF_EVEN
27272724

27282725
c1 = Context()
27292726
c2 = Context(prec=None, rounding=None, Emax=None, Emin=None,
@@ -2739,6 +2736,21 @@ def test_none_args(self):
27392736
assert_signals(self, c, 'traps', [InvalidOperation, DivisionByZero,
27402737
Overflow])
27412738

2739+
@cpython_only
2740+
def test_from_legacy_strings(self):
2741+
import _testcapi
2742+
c = self.decimal.Context()
2743+
2744+
for rnd in RoundingModes:
2745+
c.rounding = _testcapi.unicode_legacy_string(rnd)
2746+
self.assertEqual(c.rounding, rnd)
2747+
2748+
s = _testcapi.unicode_legacy_string('')
2749+
self.assertRaises(TypeError, setattr, c, 'rounding', s)
2750+
2751+
s = _testcapi.unicode_legacy_string('ROUND_\x00UP')
2752+
self.assertRaises(TypeError, setattr, c, 'rounding', s)
2753+
27422754
def test_pickle(self):
27432755

27442756
Context = self.decimal.Context
@@ -2762,7 +2774,7 @@ def test_pickle(self):
27622774
# Test interchangeability
27632775
combinations = [(C, P), (P, C)] if C else [(P, P)]
27642776
for dumper, loader in combinations:
2765-
for ri, _ in enumerate(RoundingModes[dumper]):
2777+
for ri, _ in enumerate(RoundingModes):
27662778
for fi, _ in enumerate(OrderedSignals[dumper]):
27672779
for ti, _ in enumerate(OrderedSignals[dumper]):
27682780

@@ -2776,7 +2788,7 @@ def test_pickle(self):
27762788
sys.modules['decimal'] = dumper
27772789
c = dumper.Context(
27782790
prec=prec, Emin=emin, Emax=emax,
2779-
rounding=RoundingModes[dumper][ri],
2791+
rounding=RoundingModes[ri],
27802792
capitals=caps, clamp=clamp,
27812793
flags=OrderedSignals[dumper][:fi],
27822794
traps=OrderedSignals[dumper][:ti]
@@ -2791,7 +2803,7 @@ def test_pickle(self):
27912803
self.assertEqual(d.prec, prec)
27922804
self.assertEqual(d.Emin, emin)
27932805
self.assertEqual(d.Emax, emax)
2794-
self.assertEqual(d.rounding, RoundingModes[loader][ri])
2806+
self.assertEqual(d.rounding, RoundingModes[ri])
27952807
self.assertEqual(d.capitals, caps)
27962808
self.assertEqual(d.clamp, clamp)
27972809
assert_signals(self, d, 'flags', OrderedSignals[loader][:fi])
@@ -3593,7 +3605,6 @@ def test_flags_irrelevant(self):
35933605
Underflow = self.decimal.Underflow
35943606
Clamped = self.decimal.Clamped
35953607
Subnormal = self.decimal.Subnormal
3596-
ROUND_HALF_EVEN = self.decimal.ROUND_HALF_EVEN
35973608

35983609
def raise_error(context, flag):
35993610
if self.decimal == C:
@@ -3960,17 +3971,6 @@ def test_invalid_context(self):
39603971
self.assertRaises(ValueError, setattr, c, 'Emin', 1)
39613972
self.assertRaises(TypeError, setattr, c, 'Emin', (1,2,3))
39623973

3963-
# rounding: always raise TypeError in order to get consistent
3964-
# exceptions across implementations. In decimal, rounding
3965-
# modes are strings, in _decimal they are integers. The idea
3966-
# is to view rounding as an abstract type and not mind the
3967-
# implementation details.
3968-
# Hence, a user should view the rounding modes as if they
3969-
# had been defined in a language that supports abstract
3970-
# data types, e.g. ocaml:
3971-
#
3972-
# type rounding = ROUND_DOWN | ROUND_HALF_UP | ... ;;
3973-
#
39743974
self.assertRaises(TypeError, setattr, c, 'rounding', -1)
39753975
self.assertRaises(TypeError, setattr, c, 'rounding', 9)
39763976
self.assertRaises(TypeError, setattr, c, 'rounding', 1.0)
@@ -4023,8 +4023,6 @@ def test_context_subclassing(self):
40234023
decimal = self.decimal
40244024
Decimal = decimal.Decimal
40254025
Context = decimal.Context
4026-
ROUND_HALF_EVEN = decimal.ROUND_HALF_EVEN
4027-
ROUND_DOWN = decimal.ROUND_DOWN
40284026
Clamped = decimal.Clamped
40294027
DivisionByZero = decimal.DivisionByZero
40304028
Inexact = decimal.Inexact
@@ -4192,7 +4190,7 @@ def test_context_repr(self):
41924190
c.prec = 425000000
41934191
c.Emax = 425000000
41944192
c.Emin = -425000000
4195-
c.rounding = self.decimal.ROUND_HALF_DOWN
4193+
c.rounding = ROUND_HALF_DOWN
41964194
c.capitals = 0
41974195
c.clamp = 1
41984196
for sig in OrderedSignals[self.decimal]:
@@ -4584,7 +4582,6 @@ def test_py_decimal_id(self):
45844582
def test_py_rescale(self):
45854583
# Coverage
45864584
Decimal = P.Decimal
4587-
ROUND_UP = P.ROUND_UP
45884585
localcontext = P.localcontext
45894586

45904587
with localcontext() as c:
@@ -4594,7 +4591,6 @@ def test_py_rescale(self):
45944591
def test_py__round(self):
45954592
# Coverage
45964593
Decimal = P.Decimal
4597-
ROUND_UP = P.ROUND_UP
45984594

45994595
self.assertRaises(ValueError, Decimal("3.1234")._round, 0, ROUND_UP)
46004596

@@ -4663,11 +4659,6 @@ def test_constants(self):
46634659
self.assertEqual(C.DECIMAL128, 128)
46644660
self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, 512)
46654661

4666-
# Rounding modes
4667-
for i, v in enumerate(RoundingModes[C]):
4668-
self.assertEqual(v, i)
4669-
self.assertEqual(C.ROUND_TRUNC, 8)
4670-
46714662
# Conditions
46724663
for i, v in enumerate(cond):
46734664
self.assertEqual(v, 1<<i)
@@ -4727,7 +4718,6 @@ def test_c_context_repr(self):
47274718
# in the same order.
47284719
DefaultContext = C.DefaultContext
47294720
FloatOperation = C.FloatOperation
4730-
ROUND_HALF_DOWN = C.ROUND_HALF_DOWN
47314721

47324722
c = DefaultContext.copy()
47334723

@@ -4800,7 +4790,6 @@ def test_c_context_errors(self):
48004790
self.assertRaises(OverflowError, Context, prec=int_max+1)
48014791
self.assertRaises(OverflowError, Context, Emax=int_max+1)
48024792
self.assertRaises(OverflowError, Context, Emin=-int_max-2)
4803-
self.assertRaises(OverflowError, Context, rounding=int_max+1)
48044793
self.assertRaises(OverflowError, Context, clamp=int_max+1)
48054794
self.assertRaises(OverflowError, Context, capitals=int_max+1)
48064795

@@ -4812,14 +4801,6 @@ def test_c_context_errors(self):
48124801
self.assertRaises(ValueError, setattr, c, attr, int_max)
48134802
self.assertRaises(ValueError, setattr, c, attr, -int_max-1)
48144803

4815-
# OverflowError, general TypeError
4816-
for attr in ('rounding',):
4817-
self.assertRaises(OverflowError, setattr, c, attr, int_max+1)
4818-
self.assertRaises(OverflowError, setattr, c, attr, -int_max-2)
4819-
if sys.platform != 'win32':
4820-
self.assertRaises(TypeError, setattr, c, attr, int_max)
4821-
self.assertRaises(TypeError, setattr, c, attr, -int_max-1)
4822-
48234804
# OverflowError: _unsafe_setprec, _unsafe_setemin, _unsafe_setemax
48244805
if C.MAX_PREC == 425000000:
48254806
self.assertRaises(OverflowError, getattr(c, '_unsafe_setprec'),
@@ -4862,6 +4843,17 @@ def test_c_context_errors(self):
48624843
self.assertRaises(TypeError, setcontext, "xyz")
48634844
setcontext(saved_context)
48644845

4846+
def test_rounding_strings_interned(self):
4847+
4848+
self.assertIs(C.ROUND_UP, P.ROUND_UP)
4849+
self.assertIs(C.ROUND_DOWN, P.ROUND_DOWN)
4850+
self.assertIs(C.ROUND_CEILING, P.ROUND_CEILING)
4851+
self.assertIs(C.ROUND_FLOOR, P.ROUND_FLOOR)
4852+
self.assertIs(C.ROUND_HALF_UP, P.ROUND_HALF_UP)
4853+
self.assertIs(C.ROUND_HALF_DOWN, P.ROUND_HALF_DOWN)
4854+
self.assertIs(C.ROUND_HALF_EVEN, P.ROUND_HALF_EVEN)
4855+
self.assertIs(C.ROUND_05UP, P.ROUND_05UP)
4856+
48654857
@requires_extra_functionality
48664858
def test_c_context_errors_extra(self):
48674859
Context = C.Context
@@ -4908,7 +4900,6 @@ def test_c_context_errors_extra(self):
49084900
def test_c_valid_context(self):
49094901
# These tests are for code coverage in _decimal.
49104902
DefaultContext = C.DefaultContext
4911-
ROUND_HALF_UP = C.ROUND_HALF_UP
49124903
Clamped = C.Clamped
49134904
Underflow = C.Underflow
49144905
Inexact = C.Inexact
@@ -5000,7 +4991,6 @@ def test_c_format(self):
50004991
def test_c_integral(self):
50014992
Decimal = C.Decimal
50024993
Inexact = C.Inexact
5003-
ROUND_UP = C.ROUND_UP
50044994
localcontext = C.localcontext
50054995

50064996
x = Decimal(10)
@@ -5034,7 +5024,6 @@ def test_c_funcs(self):
50345024
Decimal = C.Decimal
50355025
InvalidOperation = C.InvalidOperation
50365026
DivisionByZero = C.DivisionByZero
5037-
ROUND_UP = C.ROUND_UP
50385027
getcontext = C.getcontext
50395028
localcontext = C.localcontext
50405029

@@ -5237,7 +5226,7 @@ def assertIsExclusivelySet(signal, signal_dict):
52375226
lim = len(OrderedSignals[C])
52385227
for r in range(lim):
52395228
for t in range(lim):
5240-
for round in RoundingModes[C]:
5229+
for round in RoundingModes:
52415230
flags = random.sample(OrderedSignals[C], r)
52425231
traps = random.sample(OrderedSignals[C], t)
52435232
prec = random.randrange(1, 10000)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ Core and Builtins
150150
Library
151151
-------
152152

153+
- Issue #16422: Use strings for rounding mode constants for better readability
154+
and pickling compatibility.
155+
153156
- Issue #15861: tkinter now correctly works with lists and tuples containing
154157
strings with whitespaces, backslashes or unbalanced braces.
155158

0 commit comments

Comments
 (0)