Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-123284: fix _pylong decimal context creation #123285

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions Lib/_pylong.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,21 @@ def compute_powers(w, base, more_than, *, need_hi=False, show=False):
del d[n]
return d

_unbounded_dec_context = decimal.getcontext().copy()
_unbounded_dec_context.prec = decimal.MAX_PREC
_unbounded_dec_context.Emax = decimal.MAX_EMAX
_unbounded_dec_context.Emin = decimal.MIN_EMIN
_unbounded_dec_context.traps[decimal.Inexact] = 1 # sanity check
_unbounded_dec_context = decimal.Context(
prec=decimal.MAX_PREC,
rounding=decimal.ROUND_HALF_EVEN,
Emax=decimal.MAX_EMAX,
Emin=decimal.MIN_EMIN,
capitals=1,
clamp=0,
flags=[],
traps=[
decimal.DivisionByZero,
decimal.Overflow,
decimal.InvalidOperation,
decimal.Inexact,
]
)

def int_to_decimal(n):
"""Asymptotically fast conversion of an 'int' to Decimal."""
Expand Down
47 changes: 47 additions & 0 deletions Lib/test/test_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import unittest
from unittest import mock
from test import support
from test.support.import_helper import (CleanImport,
import_module,
isolated_modules)
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
INVALID_UNDERSCORE_LITERALS)

Expand Down Expand Up @@ -758,6 +761,50 @@ def _test_pylong_int_to_decimal(self, n, suffix):
s4 = b'%d' % n
self.assertEqual(s4, s.encode('ascii'))

@support.cpython_only
@unittest.skipUnless(_pylong, "_pylong module required")
def test_pylong_context_with_pydecimal(self):
with isolated_modules(), CleanImport('decimal', '_pylong'):
sys.modules['_decimal'] = None
# cannot use 'import_fresh_module' since it directly
# restores sys.modules after importing the module
decimal_module = import_module('decimal')
with mock.patch.object(decimal_module, 'getcontext') as f:
_pylong_module = import_module('_pylong')
self._test_pylong_context(_pylong_module, decimal_module)
f.assert_not_called()

@support.cpython_only
@unittest.skipUnless(_pylong, "_pylong module required")
@unittest.skipUnless(_decimal, "C _decimal module required")
def test_pylong_context_with_c_decimal(self):
with isolated_modules(), CleanImport('decimal', '_pylong'):
sys.modules['_pydecimal'] = None
# cannot use 'import_fresh_module' since it directly
# restores sys.modules after importing the module
decimal_module = import_module('decimal')
with mock.patch.object(decimal_module, 'getcontext') as f:
_pylong_module = import_module('_pylong')
self._test_pylong_context(_pylong_module, decimal_module)
f.assert_not_called()

def _test_pylong_context(self, pylong_module, decimal_module):
self.assertIs(pylong_module.decimal, decimal_module)
ctx = pylong_module._unbounded_dec_context
self.assertEqual(ctx.prec, decimal_module.MAX_PREC)
self.assertEqual(ctx.rounding, decimal_module.ROUND_HALF_EVEN)
self.assertEqual(ctx.Emax, decimal_module.MAX_EMAX)
self.assertEqual(ctx.Emin, decimal_module.MIN_EMIN)
self.assertEqual(ctx.capitals, 1)
self.assertEqual(ctx.clamp, 0)
ctx_traps = [trap for trap, trapped in ctx.traps.items() if trapped]
self.assertCountEqual(ctx_traps, [
decimal_module.InvalidOperation,
decimal_module.DivisionByZero,
decimal_module.Overflow,
decimal_module.Inexact
])

def test_pylong_int_to_decimal(self):
self._test_pylong_int_to_decimal((1 << 100_000), '9883109376')
self._test_pylong_int_to_decimal((1 << 100_000) - 1, '9883109375')
Expand Down
Loading