From e5182f9d2ffb2bfad3249bea9c2f46813f3651cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 24 Aug 2024 12:37:37 +0200 Subject: [PATCH 1/2] fix `_pylong` decimal context --- Lib/_pylong.py | 22 ++++++++++++++++----- Lib/test/test_int.py | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/Lib/_pylong.py b/Lib/_pylong.py index be1acd17ce3592..e300062d10ba0e 100644 --- a/Lib/_pylong.py +++ b/Lib/_pylong.py @@ -130,11 +130,23 @@ 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 +# Do not use decimal.getcontext() since otherwise the thread's context +# will be used if this module is reloaded but not the decimal context. +_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.""" diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 8870d7aa5d663d..aaf023a0fd28d7 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -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) @@ -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') From 798abe228a553fbd8e5951b162f007d03c1657ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 24 Aug 2024 13:27:35 +0200 Subject: [PATCH 2/2] remove comment --- Lib/_pylong.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/_pylong.py b/Lib/_pylong.py index e300062d10ba0e..0b1eca93c085fd 100644 --- a/Lib/_pylong.py +++ b/Lib/_pylong.py @@ -130,8 +130,6 @@ def compute_powers(w, base, more_than, *, need_hi=False, show=False): del d[n] return d -# Do not use decimal.getcontext() since otherwise the thread's context -# will be used if this module is reloaded but not the decimal context. _unbounded_dec_context = decimal.Context( prec=decimal.MAX_PREC, rounding=decimal.ROUND_HALF_EVEN,