[Python-checkins] [3.12] gh-89392: Make test_decimal discoverable (GH-106209) (#106230)

ambv webhook-mailer at python.org
Wed Jul 5 06:52:20 EDT 2023


https://github.com/python/cpython/commit/334b95b2434880e6138d430054c3054266e6020f
commit: 334b95b2434880e6138d430054c3054266e6020f
branch: 3.12
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: ambv <lukasz at langa.pl>
date: 2023-07-05T12:52:16+02:00
summary:

[3.12] gh-89392: Make test_decimal discoverable (GH-106209) (#106230)

gh-89392: Make test_decimal discoverable (GH-106209)
(cherry picked from commit 0e24499129f3917b199a6d46fa33eeedd2c447fc)

Co-authored-by: Serhiy Storchaka <storchaka at gmail.com>

files:
M Lib/test/test_decimal.py

diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
index 67ccaab40c5ed..749496e3e9455 100644
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -20,7 +20,7 @@
 
 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_main()
+you're working through IDLE, you can import this test module and call test()
 with the corresponding argument.
 """
 
@@ -32,7 +32,7 @@
 import unittest
 import numbers
 import locale
-from test.support import (run_unittest, run_doctest, is_resource_enabled,
+from test.support import (is_resource_enabled,
                           requires_IEEE_754, requires_docstrings,
                           requires_legacy_unicode_capi, check_sanitizer)
 from test.support import (TestFailed,
@@ -62,6 +62,7 @@
 fractions = {C:cfractions, P:pfractions}
 sys.modules['decimal'] = orig_sys_decimal
 
+requires_cdecimal = unittest.skipUnless(C, "test requires C version")
 
 # Useful Test Constant
 Signals = {
@@ -99,7 +100,7 @@ def assert_signals(cls, context, attr, expected):
 ]
 
 # Tests are built around these assumed context defaults.
-# test_main() restores the original context.
+# test() restores the original context.
 ORIGINAL_CONTEXT = {
   C: C.getcontext().copy() if C else None,
   P: P.getcontext().copy()
@@ -133,7 +134,7 @@ def init(m):
   EXTRA_FUNCTIONALITY, "test requires regular build")
 
 
-class IBMTestCases(unittest.TestCase):
+class IBMTestCases:
     """Class which tests the Decimal class against the IBM test cases."""
 
     def setUp(self):
@@ -488,14 +489,10 @@ def change_max_exponent(self, exp):
     def change_clamp(self, clamp):
         self.context.clamp = clamp
 
-class CIBMTestCases(IBMTestCases):
-    decimal = C
-class PyIBMTestCases(IBMTestCases):
-    decimal = P
 
 # The following classes test the behaviour of Decimal according to PEP 327
 
-class ExplicitConstructionTest(unittest.TestCase):
+class ExplicitConstructionTest:
     '''Unit tests for Explicit Construction cases of Decimal.'''
 
     def test_explicit_empty(self):
@@ -838,12 +835,13 @@ def test_unicode_digits(self):
         for input, expected in test_values.items():
             self.assertEqual(str(Decimal(input)), expected)
 
-class CExplicitConstructionTest(ExplicitConstructionTest):
+ at requires_cdecimal
+class CExplicitConstructionTest(ExplicitConstructionTest, unittest.TestCase):
     decimal = C
-class PyExplicitConstructionTest(ExplicitConstructionTest):
+class PyExplicitConstructionTest(ExplicitConstructionTest, unittest.TestCase):
     decimal = P
 
-class ImplicitConstructionTest(unittest.TestCase):
+class ImplicitConstructionTest:
     '''Unit tests for Implicit Construction cases of Decimal.'''
 
     def test_implicit_from_None(self):
@@ -920,12 +918,13 @@ def __ne__(self, other):
             self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
                              '10' + rop + 'str')
 
-class CImplicitConstructionTest(ImplicitConstructionTest):
+ at requires_cdecimal
+class CImplicitConstructionTest(ImplicitConstructionTest, unittest.TestCase):
     decimal = C
-class PyImplicitConstructionTest(ImplicitConstructionTest):
+class PyImplicitConstructionTest(ImplicitConstructionTest, unittest.TestCase):
     decimal = P
 
-class FormatTest(unittest.TestCase):
+class FormatTest:
     '''Unit tests for the format function.'''
     def test_formatting(self):
         Decimal = self.decimal.Decimal
@@ -1262,12 +1261,13 @@ def __init__(self, a):
         a = A.from_float(42)
         self.assertEqual(self.decimal.Decimal, a.a_type)
 
-class CFormatTest(FormatTest):
+ at requires_cdecimal
+class CFormatTest(FormatTest, unittest.TestCase):
     decimal = C
-class PyFormatTest(FormatTest):
+class PyFormatTest(FormatTest, unittest.TestCase):
     decimal = P
 
-class ArithmeticOperatorsTest(unittest.TestCase):
+class ArithmeticOperatorsTest:
     '''Unit tests for all arithmetic operators, binary and unary.'''
 
     def test_addition(self):
@@ -1523,14 +1523,17 @@ def test_nan_comparisons(self):
         equality_ops = operator.eq, operator.ne
 
         # results when InvalidOperation is not trapped
-        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))
+        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:
@@ -1562,9 +1565,10 @@ def test_copy_sign(self):
         self.assertEqual(Decimal(1).copy_sign(-2), d)
         self.assertRaises(TypeError, Decimal(1).copy_sign, '-2')
 
-class CArithmeticOperatorsTest(ArithmeticOperatorsTest):
+ at requires_cdecimal
+class CArithmeticOperatorsTest(ArithmeticOperatorsTest, unittest.TestCase):
     decimal = C
-class PyArithmeticOperatorsTest(ArithmeticOperatorsTest):
+class PyArithmeticOperatorsTest(ArithmeticOperatorsTest, unittest.TestCase):
     decimal = P
 
 # The following are two functions used to test threading in the next class
@@ -1654,7 +1658,7 @@ def thfunc2(cls):
 
 
 @threading_helper.requires_working_threading()
-class ThreadingTest(unittest.TestCase):
+class ThreadingTest:
     '''Unit tests for thread local contexts in Decimal.'''
 
     # Take care executing this test from IDLE, there's an issue in threading
@@ -1699,13 +1703,14 @@ def test_threading(self):
         DefaultContext.Emin = save_emin
 
 
-class CThreadingTest(ThreadingTest):
+ at requires_cdecimal
+class CThreadingTest(ThreadingTest, unittest.TestCase):
     decimal = C
 
-class PyThreadingTest(ThreadingTest):
+class PyThreadingTest(ThreadingTest, unittest.TestCase):
     decimal = P
 
-class UsabilityTest(unittest.TestCase):
+class UsabilityTest:
     '''Unit tests for Usability cases of Decimal.'''
 
     def test_comparison_operators(self):
@@ -2521,9 +2526,10 @@ def test_conversions_from_int(self):
         self.assertEqual(Decimal(-12).fma(45, Decimal(67)),
                          Decimal(-12).fma(Decimal(45), Decimal(67)))
 
-class CUsabilityTest(UsabilityTest):
+ at requires_cdecimal
+class CUsabilityTest(UsabilityTest, unittest.TestCase):
     decimal = C
-class PyUsabilityTest(UsabilityTest):
+class PyUsabilityTest(UsabilityTest, unittest.TestCase):
     decimal = P
 
     def setUp(self):
@@ -2535,7 +2541,7 @@ def tearDown(self):
         sys.set_int_max_str_digits(self._previous_int_limit)
         super().tearDown()
 
-class PythonAPItests(unittest.TestCase):
+class PythonAPItests:
 
     def test_abc(self):
         Decimal = self.decimal.Decimal
@@ -2884,12 +2890,13 @@ def test_exception_hierarchy(self):
         self.assertTrue(issubclass(decimal.DivisionUndefined, ZeroDivisionError))
         self.assertTrue(issubclass(decimal.InvalidContext, InvalidOperation))
 
-class CPythonAPItests(PythonAPItests):
+ at requires_cdecimal
+class CPythonAPItests(PythonAPItests, unittest.TestCase):
     decimal = C
-class PyPythonAPItests(PythonAPItests):
+class PyPythonAPItests(PythonAPItests, unittest.TestCase):
     decimal = P
 
-class ContextAPItests(unittest.TestCase):
+class ContextAPItests:
 
     def test_none_args(self):
         Context = self.decimal.Context
@@ -3635,12 +3642,13 @@ def test_to_integral_value(self):
         self.assertRaises(TypeError, c.to_integral_value, '10')
         self.assertRaises(TypeError, c.to_integral_value, 10, 'x')
 
-class CContextAPItests(ContextAPItests):
+ at requires_cdecimal
+class CContextAPItests(ContextAPItests, unittest.TestCase):
     decimal = C
-class PyContextAPItests(ContextAPItests):
+class PyContextAPItests(ContextAPItests, unittest.TestCase):
     decimal = P
 
-class ContextWithStatement(unittest.TestCase):
+class ContextWithStatement:
     # Can't do these as docstrings until Python 2.6
     # as doctest can't handle __future__ statements
 
@@ -3704,9 +3712,13 @@ def test_localcontext_kwargs(self):
 
     def test_local_context_kwargs_does_not_overwrite_existing_argument(self):
         ctx = self.decimal.getcontext()
-        ctx.prec = 28
+        orig_prec = ctx.prec
         with self.decimal.localcontext(prec=10) as ctx2:
-            self.assertEqual(ctx.prec, 28)
+            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
@@ -3800,12 +3812,13 @@ def test_with_statements_gc3(self):
                         self.assertEqual(c4.prec, 4)
                         del c4
 
-class CContextWithStatement(ContextWithStatement):
+ at requires_cdecimal
+class CContextWithStatement(ContextWithStatement, unittest.TestCase):
     decimal = C
-class PyContextWithStatement(ContextWithStatement):
+class PyContextWithStatement(ContextWithStatement, unittest.TestCase):
     decimal = P
 
-class ContextFlags(unittest.TestCase):
+class ContextFlags:
 
     def test_flags_irrelevant(self):
         # check that the result (numeric result + flags raised) of an
@@ -4072,12 +4085,13 @@ def test_float_operation_default(self):
         self.assertTrue(context.traps[FloatOperation])
         self.assertTrue(context.traps[Inexact])
 
-class CContextFlags(ContextFlags):
+ at requires_cdecimal
+class CContextFlags(ContextFlags, unittest.TestCase):
     decimal = C
-class PyContextFlags(ContextFlags):
+class PyContextFlags(ContextFlags, unittest.TestCase):
     decimal = P
 
-class SpecialContexts(unittest.TestCase):
+class SpecialContexts:
     """Test the context templates."""
 
     def test_context_templates(self):
@@ -4157,12 +4171,13 @@ def test_default_context(self):
             if ex:
                 raise ex
 
-class CSpecialContexts(SpecialContexts):
+ at requires_cdecimal
+class CSpecialContexts(SpecialContexts, unittest.TestCase):
     decimal = C
-class PySpecialContexts(SpecialContexts):
+class PySpecialContexts(SpecialContexts, unittest.TestCase):
     decimal = P
 
-class ContextInputValidation(unittest.TestCase):
+class ContextInputValidation:
 
     def test_invalid_context(self):
         Context = self.decimal.Context
@@ -4224,12 +4239,13 @@ def test_invalid_context(self):
         self.assertRaises(TypeError, Context, flags=(0,1))
         self.assertRaises(TypeError, Context, traps=(1,0))
 
-class CContextInputValidation(ContextInputValidation):
+ at requires_cdecimal
+class CContextInputValidation(ContextInputValidation, unittest.TestCase):
     decimal = C
-class PyContextInputValidation(ContextInputValidation):
+class PyContextInputValidation(ContextInputValidation, unittest.TestCase):
     decimal = P
 
-class ContextSubclassing(unittest.TestCase):
+class ContextSubclassing:
 
     def test_context_subclassing(self):
         decimal = self.decimal
@@ -4338,12 +4354,14 @@ def __init__(self, prec=None, rounding=None, Emin=None, Emax=None,
         for signal in OrderedSignals[decimal]:
             self.assertFalse(c.traps[signal])
 
-class CContextSubclassing(ContextSubclassing):
+ at requires_cdecimal
+class CContextSubclassing(ContextSubclassing, unittest.TestCase):
     decimal = C
-class PyContextSubclassing(ContextSubclassing):
+class PyContextSubclassing(ContextSubclassing, unittest.TestCase):
     decimal = P
 
 @skip_if_extra_functionality
+ at requires_cdecimal
 class CheckAttributes(unittest.TestCase):
 
     def test_module_attributes(self):
@@ -4373,7 +4391,7 @@ def test_decimal_attributes(self):
         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(unittest.TestCase):
+class Coverage:
 
     def test_adjusted(self):
         Decimal = self.decimal.Decimal
@@ -4630,9 +4648,10 @@ def test_copy(self):
         y = c.copy_sign(x, 1)
         self.assertEqual(y, -x)
 
-class CCoverage(Coverage):
+ at requires_cdecimal
+class CCoverage(Coverage, unittest.TestCase):
     decimal = C
-class PyCoverage(Coverage):
+class PyCoverage(Coverage, unittest.TestCase):
     decimal = P
 
     def setUp(self):
@@ -4885,6 +4904,7 @@ def test_constants(self):
         self.assertEqual(C.DecTraps,
                          C.DecErrors|C.DecOverflow|C.DecUnderflow)
 
+ at requires_cdecimal
 class CWhitebox(unittest.TestCase):
     """Whitebox testing for _decimal"""
 
@@ -5663,7 +5683,7 @@ def test_maxcontext_exact_arith(self):
 
 
 @requires_docstrings
- at unittest.skipUnless(C, "test requires C version")
+ at requires_cdecimal
 class SignatureTest(unittest.TestCase):
     """Function signatures"""
 
@@ -5799,52 +5819,10 @@ def doit(ty):
         doit('Context')
 
 
-all_tests = [
-  CExplicitConstructionTest, PyExplicitConstructionTest,
-  CImplicitConstructionTest, PyImplicitConstructionTest,
-  CFormatTest,               PyFormatTest,
-  CArithmeticOperatorsTest,  PyArithmeticOperatorsTest,
-  CThreadingTest,            PyThreadingTest,
-  CUsabilityTest,            PyUsabilityTest,
-  CPythonAPItests,           PyPythonAPItests,
-  CContextAPItests,          PyContextAPItests,
-  CContextWithStatement,     PyContextWithStatement,
-  CContextFlags,             PyContextFlags,
-  CSpecialContexts,          PySpecialContexts,
-  CContextInputValidation,   PyContextInputValidation,
-  CContextSubclassing,       PyContextSubclassing,
-  CCoverage,                 PyCoverage,
-  CFunctionality,            PyFunctionality,
-  CWhitebox,                 PyWhitebox,
-  CIBMTestCases,             PyIBMTestCases,
-]
-
-# Delete C tests if _decimal.so is not present.
-if not C:
-    all_tests = all_tests[1::2]
-else:
-    all_tests.insert(0, CheckAttributes)
-    all_tests.insert(1, SignatureTest)
-
-
-def test_main(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
-    """
-
-    init(C)
-    init(P)
-    global TEST_ALL, DEBUG
-    TEST_ALL = arith if arith is not None else is_resource_enabled('decimal')
-    DEBUG = debug
-
-    if todo_tests is None:
-        test_classes = all_tests
-    else:
-        test_classes = [CIBMTestCases, PyIBMTestCases]
-
+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.
@@ -5852,34 +5830,69 @@ def test_main(arith=None, verbose=None, todo_tests=None, debug=None):
         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:
+        if TODO_TESTS is not None and head not in TODO_TESTS:
             continue
         tester = lambda self, f=filename: self.eval_file(directory + f)
-        setattr(CIBMTestCases, 'test_' + head, tester)
-        setattr(PyIBMTestCases, 'test_' + head, tester)
+        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
+        for mod in C, P:
+            if not mod:
+                continue
+            def setUp(slf, mod=mod):
+                sys.modules['decimal'] = mod
+            def tearDown(slf):
+                sys.modules['decimal'] = orig_sys_decimal
+            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])
+    P.setcontext(ORIGINAL_CONTEXT[P])
+    if not C:
+        warnings.warn('C tests skipped: no module named _decimal.',
+                      UserWarning)
+    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
+    """
 
-    try:
-        run_unittest(*test_classes)
-        if todo_tests is None:
-            from doctest import IGNORE_EXCEPTION_DETAIL
-            savedecimal = sys.modules['decimal']
-            if C:
-                sys.modules['decimal'] = C
-                run_doctest(C, verbose, optionflags=IGNORE_EXCEPTION_DETAIL)
-            sys.modules['decimal'] = P
-            run_doctest(P, verbose)
-            sys.modules['decimal'] = savedecimal
-    finally:
-        if C: C.setcontext(ORIGINAL_CONTEXT[C])
-        P.setcontext(ORIGINAL_CONTEXT[P])
-        if not C:
-            warnings.warn('C tests skipped: no module named _decimal.',
-                          UserWarning)
-        if not orig_sys_decimal is sys.modules['decimal']:
-            raise TestFailed("Internal error: unbalanced number of changes to "
-                             "sys.modules['decimal'].")
+    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__':
@@ -5890,8 +5903,8 @@ def test_main(arith=None, verbose=None, todo_tests=None, debug=None):
     (opt, args) = p.parse_args()
 
     if opt.skip:
-        test_main(arith=False, verbose=True)
+        test(arith=False, verbose=True)
     elif args:
-        test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
+        test(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
     else:
-        test_main(arith=True, verbose=True)
+        test(arith=True, verbose=True)



More information about the Python-checkins mailing list