From 5afa6f76b213f2cfd7529d2fb476fbf97aa5bd87 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:32:29 +0900 Subject: [PATCH 01/26] Update _osx_support from CPython 3.10.5 --- Lib/_osx_support.py | 132 +++++-- Lib/test/test__osx_support.py | 658 +++++++++++++++++----------------- 2 files changed, 429 insertions(+), 361 deletions(-) diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py index db6674ea2..aa66c8b9f 100644 --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -52,7 +52,7 @@ def _find_executable(executable, path=None): return executable -def _read_output(commandstring): +def _read_output(commandstring, capture_stderr=False): """Output from successful command execution or None""" # Similar to os.popen(commandstring, "r").read(), # but without actually using os.popen because that @@ -67,7 +67,10 @@ def _read_output(commandstring): os.getpid(),), "w+b") with contextlib.closing(fp) as fp: - cmd = "%s 2>/dev/null >'%s'" % (commandstring, fp.name) + if capture_stderr: + cmd = "%s >'%s' 2>&1" % (commandstring, fp.name) + else: + cmd = "%s 2>/dev/null >'%s'" % (commandstring, fp.name) return fp.read().decode('utf-8').strip() if not os.system(cmd) else None @@ -93,7 +96,7 @@ def _get_system_version(): if _SYSTEM_VERSION is None: _SYSTEM_VERSION = '' try: - f = open('/System/Library/CoreServices/SystemVersion.plist') + f = open('/System/Library/CoreServices/SystemVersion.plist', encoding="utf-8") except OSError: # We're on a plain darwin box, fall back to the default # behaviour. @@ -110,6 +113,26 @@ def _get_system_version(): return _SYSTEM_VERSION +_SYSTEM_VERSION_TUPLE = None +def _get_system_version_tuple(): + """ + Return the macOS system version as a tuple + + The return value is safe to use to compare + two version numbers. + """ + global _SYSTEM_VERSION_TUPLE + if _SYSTEM_VERSION_TUPLE is None: + osx_version = _get_system_version() + if osx_version: + try: + _SYSTEM_VERSION_TUPLE = tuple(int(i) for i in osx_version.split('.')) + except ValueError: + _SYSTEM_VERSION_TUPLE = () + + return _SYSTEM_VERSION_TUPLE + + def _remove_original_values(_config_vars): """Remove original unmodified values for testing""" # This is needed for higher-level cross-platform tests of get_platform. @@ -125,6 +148,33 @@ def _save_modified_value(_config_vars, cv, newvalue): _config_vars[_INITPRE + cv] = oldvalue _config_vars[cv] = newvalue + +_cache_default_sysroot = None +def _default_sysroot(cc): + """ Returns the root of the default SDK for this system, or '/' """ + global _cache_default_sysroot + + if _cache_default_sysroot is not None: + return _cache_default_sysroot + + contents = _read_output('%s -c -E -v - "): + in_incdirs = True + elif line.startswith("End of search list"): + in_incdirs = False + elif in_incdirs: + line = line.strip() + if line == '/usr/include': + _cache_default_sysroot = '/' + elif line.endswith(".sdk/usr/include"): + _cache_default_sysroot = line[:-12] + if _cache_default_sysroot is None: + _cache_default_sysroot = '/' + + return _cache_default_sysroot + def _supports_universal_builds(): """Returns True if universal builds are supported on this system""" # As an approximation, we assume that if we are running on 10.4 or above, @@ -132,14 +182,18 @@ def _supports_universal_builds(): # builds, in particular -isysroot and -arch arguments to the compiler. This # is in support of allowing 10.4 universal builds to run on 10.3.x systems. - osx_version = _get_system_version() - if osx_version: - try: - osx_version = tuple(int(i) for i in osx_version.split('.')) - except ValueError: - osx_version = '' + osx_version = _get_system_version_tuple() return bool(osx_version >= (10, 4)) if osx_version else False +def _supports_arm64_builds(): + """Returns True if arm64 builds are supported on this system""" + # There are two sets of systems supporting macOS/arm64 builds: + # 1. macOS 11 and later, unconditionally + # 2. macOS 10.15 with Xcode 12.2 or later + # For now the second category is ignored. + osx_version = _get_system_version_tuple() + return osx_version >= (11, 0) if osx_version else False + def _find_appropriate_compiler(_config_vars): """Find appropriate C compiler for extension module builds""" @@ -211,7 +265,7 @@ def _remove_universal_flags(_config_vars): if cv in _config_vars and cv not in os.environ: flags = _config_vars[cv] flags = re.sub(r'-arch\s+\w+\s', ' ', flags, flags=re.ASCII) - flags = re.sub('-isysroot [^ \t]*', ' ', flags) + flags = re.sub(r'-isysroot\s*\S+', ' ', flags) _save_modified_value(_config_vars, cv, flags) return _config_vars @@ -287,7 +341,7 @@ def _check_for_unavailable_sdk(_config_vars): # to /usr and /System/Library by either a standalone CLT # package or the CLT component within Xcode. cflags = _config_vars.get('CFLAGS', '') - m = re.search(r'-isysroot\s+(\S+)', cflags) + m = re.search(r'-isysroot\s*(\S+)', cflags) if m is not None: sdk = m.group(1) if not os.path.exists(sdk): @@ -295,7 +349,7 @@ def _check_for_unavailable_sdk(_config_vars): # Do not alter a config var explicitly overridden by env var if cv in _config_vars and cv not in os.environ: flags = _config_vars[cv] - flags = re.sub(r'-isysroot\s+\S+(?:\s|$)', ' ', flags) + flags = re.sub(r'-isysroot\s*\S+(?:\s|$)', ' ', flags) _save_modified_value(_config_vars, cv, flags) return _config_vars @@ -320,7 +374,7 @@ def compiler_fixup(compiler_so, cc_args): stripArch = stripSysroot = True else: stripArch = '-arch' in cc_args - stripSysroot = '-isysroot' in cc_args + stripSysroot = any(arg for arg in cc_args if arg.startswith('-isysroot')) if stripArch or 'ARCHFLAGS' in os.environ: while True: @@ -331,6 +385,12 @@ def compiler_fixup(compiler_so, cc_args): except ValueError: break + elif not _supports_arm64_builds(): + # Look for "-arch arm64" and drop that + for idx in reversed(range(len(compiler_so))): + if compiler_so[idx] == '-arch' and compiler_so[idx+1] == "arm64": + del compiler_so[idx:idx+2] + if 'ARCHFLAGS' in os.environ and not stripArch: # User specified different -arch flags in the environ, # see also distutils.sysconfig @@ -338,29 +398,39 @@ def compiler_fixup(compiler_so, cc_args): if stripSysroot: while True: - try: - index = compiler_so.index('-isysroot') + indices = [i for i,x in enumerate(compiler_so) if x.startswith('-isysroot')] + if not indices: + break + index = indices[0] + if compiler_so[index] == '-isysroot': # Strip this argument and the next one: del compiler_so[index:index+2] - except ValueError: - break + else: + # It's '-isysroot/some/path' in one arg + del compiler_so[index:index+1] # Check if the SDK that is used during compilation actually exists, # the universal build requires the usage of a universal SDK and not all # users have that installed by default. sysroot = None - if '-isysroot' in cc_args: - idx = cc_args.index('-isysroot') - sysroot = cc_args[idx+1] - elif '-isysroot' in compiler_so: - idx = compiler_so.index('-isysroot') - sysroot = compiler_so[idx+1] + argvar = cc_args + indices = [i for i,x in enumerate(cc_args) if x.startswith('-isysroot')] + if not indices: + argvar = compiler_so + indices = [i for i,x in enumerate(compiler_so) if x.startswith('-isysroot')] + + for idx in indices: + if argvar[idx] == '-isysroot': + sysroot = argvar[idx+1] + break + else: + sysroot = argvar[idx][len('-isysroot'):] + break if sysroot and not os.path.isdir(sysroot): - from distutils import log - log.warn("Compiling with an SDK that doesn't seem to exist: %s", - sysroot) - log.warn("Please check your Xcode installation") + sys.stderr.write(f"Compiling with an SDK that doesn't seem to exist: {sysroot}\n") + sys.stderr.write("Please check your Xcode installation\n") + sys.stderr.flush() return compiler_so @@ -411,7 +481,7 @@ def customize_compiler(_config_vars): This customization is performed when the first extension module build is requested - in distutils.sysconfig.customize_compiler). + in distutils.sysconfig.customize_compiler. """ # Find a compiler to use for extension module builds @@ -454,10 +524,10 @@ def get_platform_osx(_config_vars, osname, release, machine): try: macrelease = tuple(int(i) for i in macrelease.split('.')[0:2]) except ValueError: - macrelease = (10, 0) + macrelease = (10, 3) else: # assume no universal support - macrelease = (10, 0) + macrelease = (10, 3) if (macrelease >= (10, 4)) and '-arch' in cflags.strip(): # The universal build will build fat binaries, but not on @@ -470,6 +540,8 @@ def get_platform_osx(_config_vars, osname, release, machine): if len(archs) == 1: machine = archs[0] + elif archs == ('arm64', 'x86_64'): + machine = 'universal2' elif archs == ('i386', 'ppc'): machine = 'fat' elif archs == ('i386', 'x86_64'): diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py index 4e85a3e07..907ae27d5 100644 --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -1,331 +1,327 @@ -""" -Test suite for _osx_support: shared OS X support functions. -""" - -import os -import platform -import stat -import sys -import unittest - -from test.support import os_helper - -import _osx_support - -@unittest.skipUnless(sys.platform.startswith("darwin"), "requires OS X") -class Test_OSXSupport(unittest.TestCase): - - def setUp(self): - self.maxDiff = None - self.prog_name = 'bogus_program_xxxx' - self.temp_path_dir = os.path.abspath(os.getcwd()) - self.env = os_helper.EnvironmentVarGuard() - self.addCleanup(self.env.__exit__) - for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', - 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', - 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS'): - if cv in self.env: - self.env.unset(cv) - - def add_expected_saved_initial_values(self, config_vars, expected_vars): - # Ensure that the initial values for all modified config vars - # are also saved with modified keys. - expected_vars.update(('_OSX_SUPPORT_INITIAL_'+ k, - config_vars[k]) for k in config_vars - if config_vars[k] != expected_vars[k]) - - def test__find_executable(self): - if self.env['PATH']: - self.env['PATH'] = self.env['PATH'] + ':' - self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir) - os_helper.unlink(self.prog_name) - self.assertIsNone(_osx_support._find_executable(self.prog_name)) - self.addCleanup(os_helper.unlink, self.prog_name) - with open(self.prog_name, 'w') as f: - f.write("#!/bin/sh\n/bin/echo OK\n") - os.chmod(self.prog_name, stat.S_IRWXU) - self.assertEqual(self.prog_name, - _osx_support._find_executable(self.prog_name)) - - def test__read_output(self): - if self.env['PATH']: - self.env['PATH'] = self.env['PATH'] + ':' - self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir) - os_helper.unlink(self.prog_name) - self.addCleanup(os_helper.unlink, self.prog_name) - with open(self.prog_name, 'w') as f: - f.write("#!/bin/sh\n/bin/echo ExpectedOutput\n") - os.chmod(self.prog_name, stat.S_IRWXU) - self.assertEqual('ExpectedOutput', - _osx_support._read_output(self.prog_name)) - - def test__find_build_tool(self): - out = _osx_support._find_build_tool('cc') - self.assertTrue(os.path.isfile(out), - 'cc not found - check xcode-select') - - def test__get_system_version(self): - self.assertTrue(platform.mac_ver()[0].startswith( - _osx_support._get_system_version())) - - def test__remove_original_values(self): - config_vars = { - 'CC': 'gcc-test -pthreads', - } - expected_vars = { - 'CC': 'clang -pthreads', - } - cv = 'CC' - newvalue = 'clang -pthreads' - _osx_support._save_modified_value(config_vars, cv, newvalue) - self.assertNotEqual(expected_vars, config_vars) - _osx_support._remove_original_values(config_vars) - self.assertEqual(expected_vars, config_vars) - - def test__save_modified_value(self): - config_vars = { - 'CC': 'gcc-test -pthreads', - } - expected_vars = { - 'CC': 'clang -pthreads', - } - self.add_expected_saved_initial_values(config_vars, expected_vars) - cv = 'CC' - newvalue = 'clang -pthreads' - _osx_support._save_modified_value(config_vars, cv, newvalue) - self.assertEqual(expected_vars, config_vars) - - def test__save_modified_value_unchanged(self): - config_vars = { - 'CC': 'gcc-test -pthreads', - } - expected_vars = config_vars.copy() - cv = 'CC' - newvalue = 'gcc-test -pthreads' - _osx_support._save_modified_value(config_vars, cv, newvalue) - self.assertEqual(expected_vars, config_vars) - - def test__supports_universal_builds(self): - import platform - mac_ver_tuple = tuple(int(i) for i in - platform.mac_ver()[0].split('.')[0:2]) - self.assertEqual(mac_ver_tuple >= (10, 4), - _osx_support._supports_universal_builds()) - - def test__find_appropriate_compiler(self): - compilers = ( - ('gcc-test', 'i686-apple-darwin11-llvm-gcc-4.2'), - ('clang', 'clang version 3.1'), - ) - config_vars = { - 'CC': 'gcc-test -pthreads', - 'CXX': 'cc++-test', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', - 'BLDSHARED': 'gcc-test -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-test -bundle -arch ppc -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', - } - expected_vars = { - 'CC': 'clang -pthreads', - 'CXX': 'clang++', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', - 'BLDSHARED': 'clang -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'clang -bundle -arch ppc -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', - } - self.add_expected_saved_initial_values(config_vars, expected_vars) - - suffix = (':' + self.env['PATH']) if self.env['PATH'] else '' - self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix - for c_name, c_output in compilers: - os_helper.unlink(c_name) - self.addCleanup(os_helper.unlink, c_name) - with open(c_name, 'w') as f: - f.write("#!/bin/sh\n/bin/echo " + c_output) - os.chmod(c_name, stat.S_IRWXU) - self.assertEqual(expected_vars, - _osx_support._find_appropriate_compiler( - config_vars)) - - def test__remove_universal_flags(self): - config_vars = { - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', - 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', - } - expected_vars = { - 'CFLAGS': '-fno-strict-aliasing -g -O3 ', - 'LDFLAGS': ' -g', - 'CPPFLAGS': '-I. ', - 'BLDSHARED': 'gcc-4.0 -bundle -g', - 'LDSHARED': 'gcc-4.0 -bundle -g', - } - self.add_expected_saved_initial_values(config_vars, expected_vars) - - self.assertEqual(expected_vars, - _osx_support._remove_universal_flags( - config_vars)) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test__remove_universal_flags_alternate(self): - # bpo-38360: also test the alternate single-argument form of -isysroot - config_vars = { - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot/Developer/SDKs/MacOSX10.4u.sdk', - 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' - '-isysroot/Developer/SDKs/MacOSX10.4u.sdk -g', - } - expected_vars = { - 'CFLAGS': '-fno-strict-aliasing -g -O3 ', - 'LDFLAGS': ' -g', - 'CPPFLAGS': '-I. ', - 'BLDSHARED': 'gcc-4.0 -bundle -g', - 'LDSHARED': 'gcc-4.0 -bundle -g', - } - self.add_expected_saved_initial_values(config_vars, expected_vars) - - self.assertEqual(expected_vars, - _osx_support._remove_universal_flags( - config_vars)) - - def test__remove_unsupported_archs(self): - config_vars = { - 'CC': 'clang', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', - 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', - } - expected_vars = { - 'CC': 'clang', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch i386 ', - 'LDFLAGS': ' -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', - 'BLDSHARED': 'gcc-4.0 -bundle -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', - } - self.add_expected_saved_initial_values(config_vars, expected_vars) - - suffix = (':' + self.env['PATH']) if self.env['PATH'] else '' - self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix - c_name = 'clang' - os_helper.unlink(c_name) - self.addCleanup(os_helper.unlink, c_name) - # exit status 255 means no PPC support in this compiler chain - with open(c_name, 'w') as f: - f.write("#!/bin/sh\nexit 255") - os.chmod(c_name, stat.S_IRWXU) - self.assertEqual(expected_vars, - _osx_support._remove_unsupported_archs( - config_vars)) - - def test__override_all_archs(self): - self.env['ARCHFLAGS'] = '-arch x86_64' - config_vars = { - 'CC': 'clang', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', - 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', - } - expected_vars = { - 'CC': 'clang', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch x86_64', - 'LDFLAGS': ' -g -arch x86_64', - 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', - 'BLDSHARED': 'gcc-4.0 -bundle -g -arch x86_64', - 'LDSHARED': 'gcc-4.0 -bundle -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk -g -arch x86_64', - } - self.add_expected_saved_initial_values(config_vars, expected_vars) - - self.assertEqual(expected_vars, - _osx_support._override_all_archs( - config_vars)) - - def test__check_for_unavailable_sdk(self): - config_vars = { - 'CC': 'clang', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.1.sdk', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.1.sdk', - 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.1.sdk -g', - } - expected_vars = { - 'CC': 'clang', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' - ' ', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. ', - 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' - ' -g', - } - self.add_expected_saved_initial_values(config_vars, expected_vars) - - self.assertEqual(expected_vars, - _osx_support._check_for_unavailable_sdk( - config_vars)) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test__check_for_unavailable_sdk_alternate(self): - # bpo-38360: also test the alternate single-argument form of -isysroot - config_vars = { - 'CC': 'clang', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' - '-isysroot/Developer/SDKs/MacOSX10.1.sdk', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. -isysroot/Developer/SDKs/MacOSX10.1.sdk', - 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' - '-isysroot/Developer/SDKs/MacOSX10.1.sdk -g', - } - expected_vars = { - 'CC': 'clang', - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' - ' ', - 'LDFLAGS': '-arch ppc -arch i386 -g', - 'CPPFLAGS': '-I. ', - 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', - 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' - ' -g', - } - self.add_expected_saved_initial_values(config_vars, expected_vars) - - self.assertEqual(expected_vars, - _osx_support._check_for_unavailable_sdk( - config_vars)) - - def test_get_platform_osx(self): - # Note, get_platform_osx is currently tested more extensively - # indirectly by test_sysconfig and test_distutils - config_vars = { - 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' - '-isysroot /Developer/SDKs/MacOSX10.1.sdk', - 'MACOSX_DEPLOYMENT_TARGET': '10.6', - } - result = _osx_support.get_platform_osx(config_vars, ' ', ' ', ' ') - self.assertEqual(('macosx', '10.6', 'fat'), result) - -if __name__ == "__main__": - unittest.main() +""" +Test suite for _osx_support: shared OS X support functions. +""" + +import os +import platform +import stat +import sys +import unittest + +from test.support import os_helper + +import _osx_support + +@unittest.skipUnless(sys.platform.startswith("darwin"), "requires OS X") +class Test_OSXSupport(unittest.TestCase): + + def setUp(self): + self.maxDiff = None + self.prog_name = 'bogus_program_xxxx' + self.temp_path_dir = os.path.abspath(os.getcwd()) + self.env = os_helper.EnvironmentVarGuard() + self.addCleanup(self.env.__exit__) + for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', + 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', + 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS'): + if cv in self.env: + self.env.unset(cv) + + def add_expected_saved_initial_values(self, config_vars, expected_vars): + # Ensure that the initial values for all modified config vars + # are also saved with modified keys. + expected_vars.update(('_OSX_SUPPORT_INITIAL_'+ k, + config_vars[k]) for k in config_vars + if config_vars[k] != expected_vars[k]) + + def test__find_executable(self): + if self.env['PATH']: + self.env['PATH'] = self.env['PATH'] + ':' + self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir) + os_helper.unlink(self.prog_name) + self.assertIsNone(_osx_support._find_executable(self.prog_name)) + self.addCleanup(os_helper.unlink, self.prog_name) + with open(self.prog_name, 'w') as f: + f.write("#!/bin/sh\n/bin/echo OK\n") + os.chmod(self.prog_name, stat.S_IRWXU) + self.assertEqual(self.prog_name, + _osx_support._find_executable(self.prog_name)) + + def test__read_output(self): + if self.env['PATH']: + self.env['PATH'] = self.env['PATH'] + ':' + self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir) + os_helper.unlink(self.prog_name) + self.addCleanup(os_helper.unlink, self.prog_name) + with open(self.prog_name, 'w') as f: + f.write("#!/bin/sh\n/bin/echo ExpectedOutput\n") + os.chmod(self.prog_name, stat.S_IRWXU) + self.assertEqual('ExpectedOutput', + _osx_support._read_output(self.prog_name)) + + def test__find_build_tool(self): + out = _osx_support._find_build_tool('cc') + self.assertTrue(os.path.isfile(out), + 'cc not found - check xcode-select') + + def test__get_system_version(self): + self.assertTrue(platform.mac_ver()[0].startswith( + _osx_support._get_system_version())) + + def test__remove_original_values(self): + config_vars = { + 'CC': 'gcc-test -pthreads', + } + expected_vars = { + 'CC': 'clang -pthreads', + } + cv = 'CC' + newvalue = 'clang -pthreads' + _osx_support._save_modified_value(config_vars, cv, newvalue) + self.assertNotEqual(expected_vars, config_vars) + _osx_support._remove_original_values(config_vars) + self.assertEqual(expected_vars, config_vars) + + def test__save_modified_value(self): + config_vars = { + 'CC': 'gcc-test -pthreads', + } + expected_vars = { + 'CC': 'clang -pthreads', + } + self.add_expected_saved_initial_values(config_vars, expected_vars) + cv = 'CC' + newvalue = 'clang -pthreads' + _osx_support._save_modified_value(config_vars, cv, newvalue) + self.assertEqual(expected_vars, config_vars) + + def test__save_modified_value_unchanged(self): + config_vars = { + 'CC': 'gcc-test -pthreads', + } + expected_vars = config_vars.copy() + cv = 'CC' + newvalue = 'gcc-test -pthreads' + _osx_support._save_modified_value(config_vars, cv, newvalue) + self.assertEqual(expected_vars, config_vars) + + def test__supports_universal_builds(self): + import platform + mac_ver_tuple = tuple(int(i) for i in + platform.mac_ver()[0].split('.')[0:2]) + self.assertEqual(mac_ver_tuple >= (10, 4), + _osx_support._supports_universal_builds()) + + def test__find_appropriate_compiler(self): + compilers = ( + ('gcc-test', 'i686-apple-darwin11-llvm-gcc-4.2'), + ('clang', 'clang version 3.1'), + ) + config_vars = { + 'CC': 'gcc-test -pthreads', + 'CXX': 'cc++-test', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', + 'BLDSHARED': 'gcc-test -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-test -bundle -arch ppc -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', + } + expected_vars = { + 'CC': 'clang -pthreads', + 'CXX': 'clang++', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', + 'BLDSHARED': 'clang -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'clang -bundle -arch ppc -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', + } + self.add_expected_saved_initial_values(config_vars, expected_vars) + + suffix = (':' + self.env['PATH']) if self.env['PATH'] else '' + self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix + for c_name, c_output in compilers: + os_helper.unlink(c_name) + self.addCleanup(os_helper.unlink, c_name) + with open(c_name, 'w') as f: + f.write("#!/bin/sh\n/bin/echo " + c_output) + os.chmod(c_name, stat.S_IRWXU) + self.assertEqual(expected_vars, + _osx_support._find_appropriate_compiler( + config_vars)) + + def test__remove_universal_flags(self): + config_vars = { + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', + 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', + } + expected_vars = { + 'CFLAGS': '-fno-strict-aliasing -g -O3 ', + 'LDFLAGS': ' -g', + 'CPPFLAGS': '-I. ', + 'BLDSHARED': 'gcc-4.0 -bundle -g', + 'LDSHARED': 'gcc-4.0 -bundle -g', + } + self.add_expected_saved_initial_values(config_vars, expected_vars) + + self.assertEqual(expected_vars, + _osx_support._remove_universal_flags( + config_vars)) + + def test__remove_universal_flags_alternate(self): + # bpo-38360: also test the alternate single-argument form of -isysroot + config_vars = { + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot/Developer/SDKs/MacOSX10.4u.sdk', + 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' + '-isysroot/Developer/SDKs/MacOSX10.4u.sdk -g', + } + expected_vars = { + 'CFLAGS': '-fno-strict-aliasing -g -O3 ', + 'LDFLAGS': ' -g', + 'CPPFLAGS': '-I. ', + 'BLDSHARED': 'gcc-4.0 -bundle -g', + 'LDSHARED': 'gcc-4.0 -bundle -g', + } + self.add_expected_saved_initial_values(config_vars, expected_vars) + + self.assertEqual(expected_vars, + _osx_support._remove_universal_flags( + config_vars)) + + def test__remove_unsupported_archs(self): + config_vars = { + 'CC': 'clang', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', + 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', + } + expected_vars = { + 'CC': 'clang', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch i386 ', + 'LDFLAGS': ' -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', + 'BLDSHARED': 'gcc-4.0 -bundle -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', + } + self.add_expected_saved_initial_values(config_vars, expected_vars) + + suffix = (':' + self.env['PATH']) if self.env['PATH'] else '' + self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix + c_name = 'clang' + os_helper.unlink(c_name) + self.addCleanup(os_helper.unlink, c_name) + # exit status 255 means no PPC support in this compiler chain + with open(c_name, 'w') as f: + f.write("#!/bin/sh\nexit 255") + os.chmod(c_name, stat.S_IRWXU) + self.assertEqual(expected_vars, + _osx_support._remove_unsupported_archs( + config_vars)) + + def test__override_all_archs(self): + self.env['ARCHFLAGS'] = '-arch x86_64' + config_vars = { + 'CC': 'clang', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', + 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g', + } + expected_vars = { + 'CC': 'clang', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch x86_64', + 'LDFLAGS': ' -g -arch x86_64', + 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk', + 'BLDSHARED': 'gcc-4.0 -bundle -g -arch x86_64', + 'LDSHARED': 'gcc-4.0 -bundle -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk -g -arch x86_64', + } + self.add_expected_saved_initial_values(config_vars, expected_vars) + + self.assertEqual(expected_vars, + _osx_support._override_all_archs( + config_vars)) + + def test__check_for_unavailable_sdk(self): + config_vars = { + 'CC': 'clang', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.1.sdk', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.1.sdk', + 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.1.sdk -g', + } + expected_vars = { + 'CC': 'clang', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' + ' ', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. ', + 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' + ' -g', + } + self.add_expected_saved_initial_values(config_vars, expected_vars) + + self.assertEqual(expected_vars, + _osx_support._check_for_unavailable_sdk( + config_vars)) + + def test__check_for_unavailable_sdk_alternate(self): + # bpo-38360: also test the alternate single-argument form of -isysroot + config_vars = { + 'CC': 'clang', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' + '-isysroot/Developer/SDKs/MacOSX10.1.sdk', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. -isysroot/Developer/SDKs/MacOSX10.1.sdk', + 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' + '-isysroot/Developer/SDKs/MacOSX10.1.sdk -g', + } + expected_vars = { + 'CC': 'clang', + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' + ' ', + 'LDFLAGS': '-arch ppc -arch i386 -g', + 'CPPFLAGS': '-I. ', + 'BLDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 -g', + 'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 ' + ' -g', + } + self.add_expected_saved_initial_values(config_vars, expected_vars) + + self.assertEqual(expected_vars, + _osx_support._check_for_unavailable_sdk( + config_vars)) + + def test_get_platform_osx(self): + # Note, get_platform_osx is currently tested more extensively + # indirectly by test_sysconfig and test_distutils + config_vars = { + 'CFLAGS': '-fno-strict-aliasing -g -O3 -arch ppc -arch i386 ' + '-isysroot /Developer/SDKs/MacOSX10.1.sdk', + 'MACOSX_DEPLOYMENT_TARGET': '10.6', + } + result = _osx_support.get_platform_osx(config_vars, ' ', ' ', ' ') + self.assertEqual(('macosx', '10.6', 'fat'), result) + +if __name__ == "__main__": + unittest.main() From c737713c9ee8b6945b45d354923b4dfb7f971b66 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 01:57:45 +0900 Subject: [PATCH 02/26] Update test_itertools from CPython 3.10.5 --- Lib/test/test_itertools.py | 107 +++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 1210446aa..a064846d3 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -533,6 +533,7 @@ class TestBasicOps(unittest.TestCase): next(testIntermediate) self.assertEqual(list(op(testIntermediate)), list(result2)) + def test_count(self): self.assertEqual(lzip('abc',count()), [('a', 0), ('b', 1), ('c', 2)]) self.assertEqual(lzip('abc',count(3)), [('a', 3), ('b', 4), ('c', 5)]) @@ -1477,6 +1478,7 @@ class TestBasicOps(unittest.TestCase): p = weakref.proxy(a) self.assertEqual(getattr(p, '__class__'), type(b)) del a + support.gc_collect() # For PyPy or other GCs. self.assertRaises(ReferenceError, getattr, p, '__class__') ans = list('abc') @@ -2435,10 +2437,37 @@ Samuele ... else: ... return starmap(func, repeat(args, times)) ->>> def grouper(n, iterable, fillvalue=None): -... "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" +>>> def grouper(iterable, n, *, incomplete='fill', fillvalue=None): +... "Collect data into non-overlapping fixed-length chunks or blocks" +... # grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx +... # grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError +... # grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF ... args = [iter(iterable)] * n -... return zip_longest(*args, fillvalue=fillvalue) +... if incomplete == 'fill': +... return zip_longest(*args, fillvalue=fillvalue) +... if incomplete == 'strict': +... return zip(*args, strict=True) +... if incomplete == 'ignore': +... return zip(*args) +... else: +... raise ValueError('Expected fill, strict, or ignore') + +>>> def triplewise(iterable): +... "Return overlapping triplets from an iterable" +... # pairwise('ABCDEFG') -> ABC BCD CDE DEF EFG +... for (a, _), (b, c) in pairwise(pairwise(iterable)): +... yield a, b, c + +>>> import collections +>>> def sliding_window(iterable, n): +... # sliding_window('ABCDEFG', 4) -> ABCD BCDE CDEF DEFG +... it = iter(iterable) +... window = collections.deque(islice(it, n), maxlen=n) +... if len(window) == n: +... yield tuple(window) +... for x in it: +... window.append(x) +... yield tuple(window) >>> def roundrobin(*iterables): ... "roundrobin('ABC', 'D', 'EF') --> A D E B F C" @@ -2453,6 +2482,40 @@ Samuele ... pending -= 1 ... nexts = cycle(islice(nexts, pending)) +>>> def partition(pred, iterable): +... "Use a predicate to partition entries into false entries and true entries" +... # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 +... t1, t2 = tee(iterable) +... return filterfalse(pred, t1), filter(pred, t2) + +>>> def before_and_after(predicate, it): +... ''' Variant of takewhile() that allows complete +... access to the remainder of the iterator. +... +... >>> all_upper, remainder = before_and_after(str.isupper, 'ABCdEfGhI') +... >>> str.join('', all_upper) +... 'ABC' +... >>> str.join('', remainder) +... 'dEfGhI' +... +... Note that the first iterator must be fully +... consumed before the second iterator can +... generate valid results. +... ''' +... it = iter(it) +... transition = [] +... def true_iterator(): +... for elem in it: +... if predicate(elem): +... yield elem +... else: +... transition.append(elem) +... return +... def remainder_iterator(): +... yield from transition +... yield from it +... return true_iterator(), remainder_iterator() + >>> def powerset(iterable): ... "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" ... s = list(iterable) @@ -2574,12 +2637,47 @@ True >>> dotproduct([1,2,3], [4,5,6]) 32 ->>> list(grouper(3, 'abcdefg', 'x')) +>>> list(grouper('abcdefg', 3, fillvalue='x')) [('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'x', 'x')] +>>> it = grouper('abcdefg', 3, incomplete='strict') +>>> next(it) +('a', 'b', 'c') +>>> next(it) +('d', 'e', 'f') +>>> next(it) +Traceback (most recent call last): + ... +ValueError: zip() argument 2 is shorter than argument 1 + +>>> list(grouper('abcdefg', n=3, incomplete='ignore')) +[('a', 'b', 'c'), ('d', 'e', 'f')] + +>>> list(triplewise('ABCDEFG')) +[('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E'), ('D', 'E', 'F'), ('E', 'F', 'G')] + +>>> list(sliding_window('ABCDEFG', 4)) +[('A', 'B', 'C', 'D'), ('B', 'C', 'D', 'E'), ('C', 'D', 'E', 'F'), ('D', 'E', 'F', 'G')] + >>> list(roundrobin('abc', 'd', 'ef')) ['a', 'd', 'e', 'b', 'f', 'c'] +>>> def is_odd(x): +... return x % 2 == 1 + +>>> evens, odds = partition(is_odd, range(10)) +>>> list(evens) +[0, 2, 4, 6, 8] +>>> list(odds) +[1, 3, 5, 7, 9] + +>>> it = iter('ABCdEfGhI') +>>> all_upper, remainder = before_and_after(str.isupper, it) +>>> ''.join(all_upper) +'ABC' +>>> ''.join(remainder) +'dEfGhI' + >>> list(powerset([1,2,3])) [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] @@ -2635,6 +2733,7 @@ def test_main(verbose=None): counts[i] = sys.gettotalrefcount() print(counts) + # doctest the examples in the library reference support.run_doctest(sys.modules[__name__], verbose) if __name__ == "__main__": From 5b186832401dccb0be4e3cd456b4f69362186c41 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:01:17 +0900 Subject: [PATCH 03/26] Update list/tuple tests from CPython 3.10.5 --- Lib/test/list_tests.py | 31 ++++++++++----------- Lib/test/seq_tests.py | 30 ++++++++++++++------ Lib/test/test_list.py | 63 ++++++++++++++++++++++++++++++++++++++++++ Lib/test/test_tuple.py | 2 +- 4 files changed, 100 insertions(+), 26 deletions(-) diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index 3bff6c268..f7eea88c5 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -4,11 +4,10 @@ Tests common to list and UserList.UserList import sys import os -import unittest from functools import cmp_to_key from test import support, seq_tests -from test.support import os_helper +from test.support import ALWAYS_EQ, NEVER_EQ class CommonTest(seq_tests.CommonTest): @@ -67,20 +66,6 @@ class CommonTest(seq_tests.CommonTest): a = self.type2test([a]) self.assertRaises(RecursionError, repr, a) - def test_print(self): - d = self.type2test(range(200)) - d.append(d) - d.extend(range(200,400)) - d.append(d) - d.append(400) - try: - with open(os_helper.TESTFN, "w") as fo: - fo.write(str(d)) - with open(os_helper.TESTFN, "r") as fo: - self.assertEqual(fo.read(), repr(d)) - finally: - os.remove(os_helper.TESTFN) - def test_set_subscript(self): a = self.type2test(range(20)) self.assertRaises(ValueError, a.__setitem__, slice(0, 10, 0), [1,2,3]) @@ -331,6 +316,20 @@ class CommonTest(seq_tests.CommonTest): self.assertRaises(TypeError, a.remove) + a = self.type2test([1, 2]) + self.assertRaises(ValueError, a.remove, NEVER_EQ) + self.assertEqual(a, [1, 2]) + a.remove(ALWAYS_EQ) + self.assertEqual(a, [2]) + a = self.type2test([ALWAYS_EQ]) + a.remove(1) + self.assertEqual(a, []) + a = self.type2test([ALWAYS_EQ]) + a.remove(NEVER_EQ) + self.assertEqual(a, []) + a = self.type2test([NEVER_EQ]) + self.assertRaises(ValueError, a.remove, ALWAYS_EQ) + class BadExc(Exception): pass diff --git a/Lib/test/seq_tests.py b/Lib/test/seq_tests.py index e73f25be9..59db2664a 100644 --- a/Lib/test/seq_tests.py +++ b/Lib/test/seq_tests.py @@ -6,6 +6,7 @@ import unittest import sys import pickle from test import support +from test.support import ALWAYS_EQ, NEVER_EQ # Various iterables # This is used for checking the constructor (here and in test_deque.py) @@ -209,6 +210,7 @@ class CommonTest(unittest.TestCase): a = self.type2test([0,1,2,3,4]) self.assertEqual(a[ -pow(2,128): 3 ], self.type2test([0,1,2])) self.assertEqual(a[ 3: pow(2,145) ], self.type2test([3,4])) + self.assertEqual(a[3::sys.maxsize], self.type2test([3])) def test_contains(self): u = self.type2test([0, 1, 2]) @@ -220,15 +222,15 @@ class CommonTest(unittest.TestCase): self.assertRaises(TypeError, u.__contains__) def test_contains_fake(self): - class AllEq: - # Sequences must use rich comparison against each item - # (unless "is" is true, or an earlier item answered) - # So instances of AllEq must be found in all non-empty sequences. - def __eq__(self, other): - return True - __hash__ = None # Can't meet hash invariant requirements - self.assertNotIn(AllEq(), self.type2test([])) - self.assertIn(AllEq(), self.type2test([1])) + # Sequences must use rich comparison against each item + # (unless "is" is true, or an earlier item answered) + # So ALWAYS_EQ must be found in all non-empty sequences. + self.assertNotIn(ALWAYS_EQ, self.type2test([])) + self.assertIn(ALWAYS_EQ, self.type2test([1])) + self.assertIn(1, self.type2test([ALWAYS_EQ])) + self.assertNotIn(NEVER_EQ, self.type2test([])) + self.assertNotIn(ALWAYS_EQ, self.type2test([NEVER_EQ])) + self.assertIn(NEVER_EQ, self.type2test([ALWAYS_EQ])) def test_contains_order(self): # Sequences must test in-order. If a rich comparison has side @@ -349,6 +351,11 @@ class CommonTest(unittest.TestCase): self.assertEqual(a.count(1), 3) self.assertEqual(a.count(3), 0) + self.assertEqual(a.count(ALWAYS_EQ), 9) + self.assertEqual(self.type2test([ALWAYS_EQ, ALWAYS_EQ]).count(1), 2) + self.assertEqual(self.type2test([ALWAYS_EQ, ALWAYS_EQ]).count(NEVER_EQ), 2) + self.assertEqual(self.type2test([NEVER_EQ, NEVER_EQ]).count(ALWAYS_EQ), 0) + self.assertRaises(TypeError, a.count) class BadExc(Exception): @@ -377,6 +384,11 @@ class CommonTest(unittest.TestCase): self.assertEqual(u.index(0, 3, 4), 3) self.assertRaises(ValueError, u.index, 2, 0, -10) + self.assertEqual(u.index(ALWAYS_EQ), 0) + self.assertEqual(self.type2test([ALWAYS_EQ, ALWAYS_EQ]).index(1), 0) + self.assertEqual(self.type2test([ALWAYS_EQ, ALWAYS_EQ]).index(NEVER_EQ), 0) + self.assertRaises(ValueError, self.type2test([NEVER_EQ, NEVER_EQ]).index, ALWAYS_EQ) + self.assertRaises(TypeError, u.index) class BadExc(Exception): diff --git a/Lib/test/test_list.py b/Lib/test/test_list.py index b4ae26584..54c827498 100644 --- a/Lib/test/test_list.py +++ b/Lib/test/test_list.py @@ -151,6 +151,11 @@ class ListTest(list_tests.CommonTest): a[:] = data self.assertEqual(list(it), []) + def test_step_overflow(self): + a = [0, 1, 2, 3, 4] + a[1::sys.maxsize] = [0] + self.assertEqual(a[3::sys.maxsize], [3]) + def test_no_comdat_folding(self): # Issue 8847: In the PGO build, the MSVC linker's COMDAT folding # optimization causes failures in code that relies on distinct @@ -159,6 +164,31 @@ class ListTest(list_tests.CommonTest): with self.assertRaises(TypeError): (3,) + L([1,2]) + def test_equal_operator_modifying_operand(self): + # test fix for seg fault reported in bpo-38588 part 2. + class X: + def __eq__(self,other) : + list2.clear() + return NotImplemented + + class Y: + def __eq__(self, other): + list1.clear() + return NotImplemented + + class Z: + def __eq__(self, other): + list3.clear() + return NotImplemented + + list1 = [X()] + list2 = [Y()] + self.assertTrue(list1 == list2) + + list3 = [Z()] + list4 = [1] + self.assertFalse(list3 == list4) + @cpython_only def test_preallocation(self): iterable = [0] * 10 @@ -167,5 +197,38 @@ class ListTest(list_tests.CommonTest): self.assertEqual(iter_size, sys.getsizeof(list([0] * 10))) self.assertEqual(iter_size, sys.getsizeof(list(range(10)))) + def test_count_index_remove_crashes(self): + # bpo-38610: The count(), index(), and remove() methods were not + # holding strong references to list elements while calling + # PyObject_RichCompareBool(). + class X: + def __eq__(self, other): + lst.clear() + return NotImplemented + + lst = [X()] + with self.assertRaises(ValueError): + lst.index(lst) + + class L(list): + def __eq__(self, other): + str(other) + return NotImplemented + + lst = L([X()]) + lst.count(lst) + + lst = L([X()]) + with self.assertRaises(ValueError): + lst.remove(lst) + + # bpo-39453: list.__contains__ was not holding strong references + # to list elements while calling PyObject_RichCompareBool(). + lst = [X(), X()] + 3 in lst + lst = [X(), X()] + X() in lst + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_tuple.py b/Lib/test/test_tuple.py index 38704f463..26b238e08 100644 --- a/Lib/test/test_tuple.py +++ b/Lib/test/test_tuple.py @@ -1,7 +1,7 @@ from test import support, seq_tests import unittest -# import gc +import gc import pickle # For tuple hashes, we normally only run a test to ensure that we get From 003f9dbf21970c571d4b24ea48b94e5e2cf16c15 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 02:08:47 +0900 Subject: [PATCH 04/26] adjust failing markers for list tests --- Lib/test/list_tests.py | 5 +++++ Lib/test/test_list.py | 11 +++++++++++ Lib/test/test_userlist.py | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index f7eea88c5..65a5118e4 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -2,6 +2,7 @@ Tests common to list and UserList.UserList """ +import unittest import sys import os from functools import cmp_to_key @@ -303,6 +304,8 @@ class CommonTest(seq_tests.CommonTest): self.assertRaises(TypeError, a.pop, 42, 42) a = self.type2test([0, 10, 20, 30, 40]) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_remove(self): a = self.type2test([0, 0, 1]) a.remove(1) @@ -362,6 +365,8 @@ class CommonTest(seq_tests.CommonTest): # verify that original order and values are retained. self.assertIs(x, y) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_index(self): super().test_index() a = self.type2test([-2, -1, 0, 0, 1, 2]) diff --git a/Lib/test/test_list.py b/Lib/test/test_list.py index 54c827498..419eb945b 100644 --- a/Lib/test/test_list.py +++ b/Lib/test/test_list.py @@ -164,6 +164,7 @@ class ListTest(list_tests.CommonTest): with self.assertRaises(TypeError): (3,) + L([1,2]) + @unittest.skip("TODO: RUSTPYTHON; hang") def test_equal_operator_modifying_operand(self): # test fix for seg fault reported in bpo-38588 part 2. class X: @@ -197,6 +198,7 @@ class ListTest(list_tests.CommonTest): self.assertEqual(iter_size, sys.getsizeof(list([0] * 10))) self.assertEqual(iter_size, sys.getsizeof(list(range(10)))) + @unittest.skip("TODO: RUSTPYTHON; hang") def test_count_index_remove_crashes(self): # bpo-38610: The count(), index(), and remove() methods were not # holding strong references to list elements while calling @@ -229,6 +231,15 @@ class ListTest(list_tests.CommonTest): lst = [X(), X()] X() in lst + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_count(self): # XXX: RUSTPYTHON; the method also need to be removed when done + super().test_count() + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_contains_fake(self): # XXX: RUSTPYTHON; the method also need to be removed when done + super().test_contains_fake() if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_userlist.py b/Lib/test/test_userlist.py index 3b30cdc8e..9ed682afe 100644 --- a/Lib/test/test_userlist.py +++ b/Lib/test/test_userlist.py @@ -11,6 +11,16 @@ class UserListTest(list_tests.CommonTest): def test_init(self): super().test_init() + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_contains_fake(self): # XXX: RUSTPYTHON; the method also need to be removed when done + super().test_contains_fake() + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_count(self): # XXX: RUSTPYTHON; the method also need to be removed when done + super().test_count() + # TODO: RUSTPYTHON, parent method is marked but this version passes import sys @unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON, unexpectedly panics somewhere") From 8d3bac9c1e809c3aef12387e6047451ec271ff17 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:10:42 +0900 Subject: [PATCH 05/26] Update test_array from CPython 3.10.5 --- Lib/test/test_array.py | 215 ++++++++++++++++++----------------------- 1 file changed, 95 insertions(+), 120 deletions(-) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index c81e5bec8..10280876b 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -2,15 +2,16 @@ Roger E. Masse """ +import collections.abc import unittest from test import support -from test.support import _2G, os_helper +from test.support import os_helper +from test.support import _2G import weakref import pickle import operator import struct import sys -import warnings import array from array import _array_reconstructor as array_reconstructor @@ -29,12 +30,29 @@ typecodes = 'ubBhHiIlLfdqQ' class MiscTest(unittest.TestCase): + def test_array_is_sequence(self): + self.assertIsInstance(array.array("B"), collections.abc.MutableSequence) + self.assertIsInstance(array.array("B"), collections.abc.Reversible) + def test_bad_constructor(self): self.assertRaises(TypeError, array.array) self.assertRaises(TypeError, array.array, spam=42) self.assertRaises(TypeError, array.array, 'xx') self.assertRaises(ValueError, array.array, 'x') + @support.cpython_only + def test_disallow_instantiation(self): + my_array = array.array("I") + support.check_disallow_instantiation( + self, type(iter(my_array)), my_array + ) + + @support.cpython_only + def test_immutable(self): + # bpo-43908: check that array.array is immutable + with self.assertRaises(TypeError): + array.array.foo = 1 + def test_empty(self): # Exercise code for handling zero-length arrays a = array.array('B') @@ -335,6 +353,67 @@ class BaseTest: self.assertEqual(list(empit), [self.outside]) self.assertEqual(list(a), list(self.example) + [self.outside]) + def test_reverse_iterator(self): + a = array.array(self.typecode, self.example) + self.assertEqual(list(a), list(self.example)) + self.assertEqual(list(reversed(a)), list(iter(a))[::-1]) + + def test_reverse_iterator_picking(self): + orig = array.array(self.typecode, self.example) + data = list(orig) + data2 = [self.outside] + data + rev_data = data[len(data)-2::-1] + [self.outside] + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + # initial iterator + itorig = reversed(orig) + d = pickle.dumps((itorig, orig), proto) + it, a = pickle.loads(d) + a.insert(0, self.outside) + self.assertEqual(type(it), type(itorig)) + self.assertEqual(list(it), rev_data) + self.assertEqual(list(a), data2) + + # running iterator + next(itorig) + d = pickle.dumps((itorig, orig), proto) + it, a = pickle.loads(d) + a.insert(0, self.outside) + self.assertEqual(type(it), type(itorig)) + self.assertEqual(list(it), rev_data[1:]) + self.assertEqual(list(a), data2) + + # empty iterator + for i in range(1, len(data)): + next(itorig) + d = pickle.dumps((itorig, orig), proto) + it, a = pickle.loads(d) + a.insert(0, self.outside) + self.assertEqual(type(it), type(itorig)) + self.assertEqual(list(it), []) + self.assertEqual(list(a), data2) + + # exhausted iterator + self.assertRaises(StopIteration, next, itorig) + d = pickle.dumps((itorig, orig), proto) + it, a = pickle.loads(d) + a.insert(0, self.outside) + self.assertEqual(list(it), []) + self.assertEqual(list(a), data2) + + def test_exhausted_reverse_iterator(self): + a = array.array(self.typecode, self.example) + self.assertEqual(list(a), list(self.example)) + exhit = reversed(a) + empit = reversed(a) + for x in exhit: # exhaust the iterator + next(empit) # Pointing past the 0th position. + a.insert(0, self.outside) + self.assertEqual(list(exhit), []) + # The iterator index points past the 0th position so inserting + # an element in the beginning does not make it appear. + self.assertEqual(list(empit), []) + self.assertEqual(list(a), [self.outside] + list(self.example)) + def test_insert(self): a = array.array(self.typecode, self.example) a.insert(0, self.example[0]) @@ -430,28 +509,6 @@ class BaseTest: b.fromlist(a.tolist()) self.assertEqual(a, b) - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_tofromstring(self): - # Warnings not raised when arguments are incorrect as Argument Clinic - # handles that before the warning can be raised. - nb_warnings = 2 - with warnings.catch_warnings(record=True) as r: - warnings.filterwarnings("always", - message=r"(to|from)string\(\) is deprecated", - category=DeprecationWarning) - a = array.array(self.typecode, 2*self.example) - b = array.array(self.typecode) - self.assertRaises(TypeError, a.tostring, 42) - self.assertRaises(TypeError, b.fromstring) - self.assertRaises(TypeError, b.fromstring, 42) - b.fromstring(a.tostring()) - self.assertEqual(a, b) - if a.itemsize>1: - self.assertRaises(ValueError, b.fromstring, "x") - nb_warnings += 1 - self.assertEqual(len(r), nb_warnings) - def test_tofrombytes(self): a = array.array(self.typecode, 2*self.example) b = array.array(self.typecode) @@ -878,6 +935,17 @@ class BaseTest: self.assertRaises(ValueError, a.index, None) self.assertRaises(ValueError, a.index, self.outside) + a = array.array('i', [-2, -1, 0, 0, 1, 2]) + self.assertEqual(a.index(0), 2) + self.assertEqual(a.index(0, 2), 2) + self.assertEqual(a.index(0, -4), 2) + self.assertEqual(a.index(-2, -10), 0) + self.assertEqual(a.index(0, 3), 3) + self.assertEqual(a.index(0, -3), 3) + self.assertEqual(a.index(0, 3, 4), 3) + self.assertEqual(a.index(0, -3, -2), 3) + self.assertRaises(ValueError, a.index, 2, 0, -10) + def test_count(self): example = 2*self.example a = array.array(self.typecode, example) @@ -1033,6 +1101,7 @@ class BaseTest: p = weakref.proxy(s) self.assertEqual(p.tobytes(), s.tobytes()) s = None + support.gc_collect() # For PyPy or other GCs. self.assertRaises(ReferenceError, len, p) @unittest.skipUnless(hasattr(sys, 'getrefcount'), @@ -1111,105 +1180,11 @@ class UnicodeTest(StringTest, unittest.TestCase): outside = str('\x33') minitemsize = 2 - def test_add(self): - super().test_add() - - def test_buffer(self): - super().test_buffer() - - def test_buffer_info(self): - super().test_buffer_info() - - def test_byteswap(self): - super().test_byteswap() - - def test_cmp(self): - super().test_cmp() - - def test_constructor(self): - super().test_constructor() - - def test_constructor_with_iterable_argument(self): - super().test_constructor_with_iterable_argument() - - def test_copy(self): - super().test_copy() - - def test_count(self): - super().test_count() - - def test_coveritertraverse(self): - super().test_coveritertraverse() - - def test_deepcopy(self): - super().test_deepcopy() - - def test_delitem(self): - super().test_delitem() - - def test_exhausted_iterator(self): - super().test_exhausted_iterator() - - def test_extend(self): - super().test_extend() - - def test_extended_getslice(self): - super().test_extended_getslice() - - def test_extended_set_del_slice(self): - super().test_extended_set_del_slice() - - def test_fromarray(self): - super().test_fromarray() - - def test_getitem(self): - super().test_getitem() - - def test_getslice(self): - super().test_getslice() - - def test_iadd(self): - super().test_iadd() - - def test_imul(self): - super().test_imul() - + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_index(self): super().test_index() - def test_insert(self): - super().test_insert() - - def test_len(self): - super().test_len() - - def test_mul(self): - super().test_mul() - - def test_pop(self): - super().test_pop() - - def test_remove(self): - super().test_remove() - - def test_repr(self): - super().test_repr() - - def test_reverse(self): - super().test_reverse() - - def test_setslice(self): - super().test_setslice() - - def test_str(self): - super().test_str() - - def test_tofrombytes(self): - super().test_tofrombytes() - - def test_tofromlist(self): - super().test_tofromlist() - def test_unicode(self): self.assertRaises(TypeError, array.array, 'b', 'foo') From b1c29aacb1dfff546137de077e7c49aa5c1debaa Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 03:54:34 +0900 Subject: [PATCH 06/26] adjust failing markers for array tests --- Lib/test/test_array.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 10280876b..02f68ca96 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -30,6 +30,8 @@ typecodes = 'ubBhHiIlLfdqQ' class MiscTest(unittest.TestCase): + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_array_is_sequence(self): self.assertIsInstance(array.array("B"), collections.abc.MutableSequence) self.assertIsInstance(array.array("B"), collections.abc.Reversible) @@ -358,6 +360,8 @@ class BaseTest: self.assertEqual(list(a), list(self.example)) self.assertEqual(list(reversed(a)), list(iter(a))[::-1]) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_reverse_iterator_picking(self): orig = array.array(self.typecode, self.example) data = list(orig) @@ -926,6 +930,8 @@ class BaseTest: del a[start:stop:step] self.assertEqual(a, array.array(self.typecode, L)) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_index(self): example = 2*self.example a = array.array(self.typecode, example) @@ -1182,7 +1188,7 @@ class UnicodeTest(StringTest, unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure - def test_index(self): + def test_index(self): # XXX: RUSTPYTHON; the method also need to be removed when done super().test_index() def test_unicode(self): From c62076e0e926a13cc260c946514c479cc5612f11 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:15:49 +0900 Subject: [PATCH 07/26] Update test_dict from CPython 3.10.5 --- Lib/test/test_dict.py | 235 +++++++++++++++++++++++++++++++++++++- Lib/test/test_userdict.py | 9 +- 2 files changed, 236 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 99b5d6531..c095144d2 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -1,6 +1,6 @@ import collections import collections.abc -# import gc // XXX RustPython +import gc import pickle import random import string @@ -37,6 +37,38 @@ class DictTest(unittest.TestCase): dictliteral = '{' + ', '.join(formatted_items) + '}' self.assertEqual(eval(dictliteral), dict(items)) + def test_merge_operator(self): + + a = {0: 0, 1: 1, 2: 1} + b = {1: 1, 2: 2, 3: 3} + + c = a.copy() + c |= b + + self.assertEqual(a | b, {0: 0, 1: 1, 2: 2, 3: 3}) + self.assertEqual(c, {0: 0, 1: 1, 2: 2, 3: 3}) + + c = b.copy() + c |= a + + self.assertEqual(b | a, {1: 1, 2: 1, 3: 3, 0: 0}) + self.assertEqual(c, {1: 1, 2: 1, 3: 3, 0: 0}) + + c = a.copy() + c |= [(1, 1), (2, 2), (3, 3)] + + self.assertEqual(c, {0: 0, 1: 1, 2: 2, 3: 3}) + + self.assertIs(a.__or__(None), NotImplemented) + self.assertIs(a.__or__(()), NotImplemented) + self.assertIs(a.__or__("BAD"), NotImplemented) + self.assertIs(a.__or__(""), NotImplemented) + + self.assertRaises(TypeError, a.__ior__, None) + self.assertEqual(a.__ior__(()), {0: 0, 1: 1, 2: 1}) + self.assertRaises(ValueError, a.__ior__, "BAD") + self.assertEqual(a.__ior__(""), {0: 0, 1: 1, 2: 1}) + def test_bool(self): self.assertIs(not {}, True) self.assertTrue({1: 2}) @@ -73,6 +105,26 @@ class DictTest(unittest.TestCase): self.assertRaises(TypeError, d.items, None) self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])") + def test_views_mapping(self): + mappingproxy = type(type.__dict__) + class Dict(dict): + pass + for cls in [dict, Dict]: + d = cls() + m1 = d.keys().mapping + m2 = d.values().mapping + m3 = d.items().mapping + + for m in [m1, m2, m3]: + self.assertIsInstance(m, mappingproxy) + self.assertEqual(m, d) + + d["foo"] = "bar" + + for m in [m1, m2, m3]: + self.assertIsInstance(m, mappingproxy) + self.assertEqual(m, d) + def test_contains(self): d = {} self.assertNotIn('a', d) @@ -668,6 +720,16 @@ class DictTest(unittest.TestCase): self.assertEqual(k1 ^ k2, {(3,3)}) self.assertEqual(k1 ^ k3, {(1,1), (2,2), (4,4)}) + def test_items_symmetric_difference(self): + rr = random.randrange + for _ in range(100): + left = {x:rr(3) for x in range(20) if rr(2)} + right = {x:rr(3) for x in range(20) if rr(2)} + with self.subTest(left=left, right=right): + expected = set(left.items()) ^ set(right.items()) + actual = left.items() ^ right.items() + self.assertEqual(actual, expected) + def test_dictview_mixed_set_operations(self): # Just a few for .keys() self.assertTrue({1:1}.keys() == {1}) @@ -994,7 +1056,7 @@ class DictTest(unittest.TestCase): @support.cpython_only def test_splittable_pop_pending(self): - """pop a pending key in a splitted table should not crash""" + """pop a pending key in a split table should not crash""" a, b = self.make_shared_key_dict(2) a['a'] = 4 @@ -1230,7 +1292,7 @@ class DictTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_equal_operator_modifying_operand(self): - # test fix for seg fault reported in issue 27945 part 3. + # test fix for seg fault reported in bpo-27945 part 3. class X(): def __del__(self): dict_b.clear() @@ -1246,6 +1308,16 @@ class DictTest(unittest.TestCase): dict_b = {X(): X()} self.assertTrue(dict_a == dict_b) + # test fix for seg fault reported in bpo-38588 part 1. + class Y: + def __eq__(self, other): + dict_d.clear() + return True + + dict_c = {0: Y()} + dict_d = {0: set()} + self.assertTrue(dict_c == dict_d) + def test_fromkeys_operator_modifying_dict_operand(self): # test fix for seg fault reported in issue 27945 part 4a. class X(int): @@ -1291,6 +1363,19 @@ class DictTest(unittest.TestCase): d = {0: set()} (0, X()) in d.items() + def test_dict_contain_use_after_free(self): + # bpo-40489 + class S(str): + def __eq__(self, other): + d.clear() + return NotImplemented + + def __hash__(self): + return hash('test') + + d = {S(): 'value'} + self.assertFalse('test' in d) + def test_init_use_after_free(self): class X: def __hash__(self): @@ -1321,6 +1406,31 @@ class DictTest(unittest.TestCase): self.assertEqual(list(r), list('dcba')) self.assertRaises(StopIteration, next, r) + def test_reverse_iterator_for_empty_dict(self): + # bpo-38525: reversed iterator should work properly + + # empty dict is directly used for reference count test + self.assertEqual(list(reversed({})), []) + self.assertEqual(list(reversed({}.items())), []) + self.assertEqual(list(reversed({}.values())), []) + self.assertEqual(list(reversed({}.keys())), []) + + # dict() and {} don't trigger the same code path + self.assertEqual(list(reversed(dict())), []) + self.assertEqual(list(reversed(dict().items())), []) + self.assertEqual(list(reversed(dict().values())), []) + self.assertEqual(list(reversed(dict().keys())), []) + + def test_reverse_iterator_for_shared_shared_dicts(self): + class A: + def __init__(self, x, y): + if x: self.x = x + if y: self.y = y + + self.assertEqual(list(reversed(A(1, 2).__dict__)), ['y', 'x']) + self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x']) + self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y']) + def test_dict_copy_order(self): # bpo-34320 od = collections.OrderedDict([('a', 1), ('b', 2)]) @@ -1351,6 +1461,125 @@ class DictTest(unittest.TestCase): d = CustomReversedDict(pairs) self.assertEqual(pairs[::-1], list(dict(d).items())) + @support.cpython_only + def test_dict_items_result_gc(self): + # bpo-42536: dict.items's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = iter({None: []}.items()) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None, None). Make sure it's re-tracked + # when it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_dict_items_result_gc_reversed(self): + # Same as test_dict_items_result_gc above, but reversed. + it = reversed({None: []}.items()) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + def test_str_nonstr(self): + # cpython uses a different lookup function if the dict only contains + # `str` keys. Make sure the unoptimized path is used when a non-`str` + # key appears. + + class StrSub(str): + pass + + eq_count = 0 + # This class compares equal to the string 'key3' + class Key3: + def __hash__(self): + return hash('key3') + + def __eq__(self, other): + nonlocal eq_count + if isinstance(other, Key3) or isinstance(other, str) and other == 'key3': + eq_count += 1 + return True + return False + + key3_1 = StrSub('key3') + key3_2 = Key3() + key3_3 = Key3() + + dicts = [] + + # Create dicts of the form `{'key1': 42, 'key2': 43, key3: 44}` in a + # bunch of different ways. In all cases, `key3` is not of type `str`. + # `key3_1` is a `str` subclass and `key3_2` is a completely unrelated + # type. + for key3 in (key3_1, key3_2): + # A literal + dicts.append({'key1': 42, 'key2': 43, key3: 44}) + + # key3 inserted via `dict.__setitem__` + d = {'key1': 42, 'key2': 43} + d[key3] = 44 + dicts.append(d) + + # key3 inserted via `dict.setdefault` + d = {'key1': 42, 'key2': 43} + self.assertEqual(d.setdefault(key3, 44), 44) + dicts.append(d) + + # key3 inserted via `dict.update` + d = {'key1': 42, 'key2': 43} + d.update({key3: 44}) + dicts.append(d) + + # key3 inserted via `dict.__ior__` + d = {'key1': 42, 'key2': 43} + d |= {key3: 44} + dicts.append(d) + + # `dict(iterable)` + def make_pairs(): + yield ('key1', 42) + yield ('key2', 43) + yield (key3, 44) + d = dict(make_pairs()) + dicts.append(d) + + # `dict.copy` + d = d.copy() + dicts.append(d) + + # dict comprehension + d = {key: 42 + i for i,key in enumerate(['key1', 'key2', key3])} + dicts.append(d) + + for d in dicts: + with self.subTest(d=d): + self.assertEqual(d.get('key1'), 42) + + # Try to make an object that is of type `str` and is equal to + # `'key1'`, but (at least on cpython) is a different object. + noninterned_key1 = 'ke' + noninterned_key1 += 'y1' + if support.check_impl_detail(cpython=True): + # suppress a SyntaxWarning + interned_key1 = 'key1' + self.assertFalse(noninterned_key1 is interned_key1) + self.assertEqual(d.get(noninterned_key1), 42) + + self.assertEqual(d.get('key3'), 44) + self.assertEqual(d.get(key3_1), 44) + self.assertEqual(d.get(key3_2), 44) + + # `key3_3` itself is definitely not a dict key, so make sure + # that `__eq__` gets called. + # + # Note that this might not hold for `key3_1` and `key3_2` + # because they might be the same object as one of the dict keys, + # in which case implementations are allowed to skip the call to + # `__eq__`. + eq_count = 0 + self.assertEqual(d.get(key3_3), 44) + self.assertGreaterEqual(eq_count, 1) + class CAPITest(unittest.TestCase): diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py index 28046eb5b..4e72e4179 100644 --- a/Lib/test/test_userdict.py +++ b/Lib/test/test_userdict.py @@ -30,8 +30,8 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): self.assertEqual(collections.UserDict(one=1, two=2), d2) # item sequence constructor self.assertEqual(collections.UserDict([('one',1), ('two',2)]), d2) - with self.assertWarnsRegex(DeprecationWarning, "'dict'"): - self.assertEqual(collections.UserDict(dict=[('one',1), ('two',2)]), d2) + self.assertEqual(collections.UserDict(dict=[('one',1), ('two',2)]), + {'dict': [('one', 1), ('two', 2)]}) # both together self.assertEqual(collections.UserDict([('one',1), ('two',2)], two=3, three=5), d3) @@ -149,9 +149,8 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): [('dict', 42)]) self.assertEqual(list(collections.UserDict({}, dict=None).items()), [('dict', None)]) - with self.assertWarnsRegex(DeprecationWarning, "'dict'"): - self.assertEqual(list(collections.UserDict(dict={'a': 42}).items()), - [('a', 42)]) + self.assertEqual(list(collections.UserDict(dict={'a': 42}).items()), + [('dict', {'a': 42})]) self.assertRaises(TypeError, collections.UserDict, 42) self.assertRaises(TypeError, collections.UserDict, (), ()) self.assertRaises(TypeError, collections.UserDict.__init__) From a4f2421aadb8610a60871b1081dbaef05e6f870a Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:40:45 +0900 Subject: [PATCH 08/26] Update test_dictcomps from CPython 3.10.5 --- Lib/test/test_dictcomps.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_dictcomps.py b/Lib/test/test_dictcomps.py index 77e5e2895..233faa90c 100644 --- a/Lib/test/test_dictcomps.py +++ b/Lib/test/test_dictcomps.py @@ -79,7 +79,7 @@ class DictComprehensionTest(unittest.TestCase): compile("{x: y for y, x in ((1, 2), (3, 4))} = 5", "", "exec") - with self.assertRaisesRegex(SyntaxError, "cannot assign"): + with self.assertRaisesRegex(SyntaxError, "illegal expression"): compile("{x: y for y, x in ((1, 2), (3, 4))} += 5", "", "exec") @@ -113,5 +113,22 @@ class DictComprehensionTest(unittest.TestCase): self.assertEqual(actual, expected) self.assertEqual(actual_calls, expected_calls) + def test_assignment_idiom_in_comprehensions(self): + expected = {1: 1, 2: 4, 3: 9, 4: 16} + actual = {j: j*j for i in range(4) for j in [i+1]} + self.assertEqual(actual, expected) + expected = {3: 2, 5: 6, 7: 12, 9: 20} + actual = {j+k: j*k for i in range(4) for j in [i+1] for k in [j+1]} + self.assertEqual(actual, expected) + expected = {3: 2, 5: 6, 7: 12, 9: 20} + actual = {j+k: j*k for i in range(4) for j, k in [(i+1, i+2)]} + self.assertEqual(actual, expected) + + def test_star_expression(self): + expected = {0: 0, 1: 1, 2: 4, 3: 9} + self.assertEqual({i: i*i for i in [*range(4)]}, expected) + self.assertEqual({i: i*i for i in (*range(4),)}, expected) + + if __name__ == "__main__": unittest.main() From 146c9ff3164ee59e5cfd598e33f6c1f63192f4d6 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:43:22 +0900 Subject: [PATCH 09/26] Update test_dictviews from CPython 3.10.5 --- Lib/test/test_dictviews.py | 625 ++++++++++++++++++++----------------- 1 file changed, 335 insertions(+), 290 deletions(-) diff --git a/Lib/test/test_dictviews.py b/Lib/test/test_dictviews.py index de8f962c0..62bcb346a 100644 --- a/Lib/test/test_dictviews.py +++ b/Lib/test/test_dictviews.py @@ -1,290 +1,335 @@ -import collections.abc -import copy -import pickle -import sys -import unittest - -class DictSetTest(unittest.TestCase): - - def test_constructors_not_callable(self): - kt = type({}.keys()) - self.assertRaises(TypeError, kt, {}) - self.assertRaises(TypeError, kt) - it = type({}.items()) - self.assertRaises(TypeError, it, {}) - self.assertRaises(TypeError, it) - vt = type({}.values()) - self.assertRaises(TypeError, vt, {}) - self.assertRaises(TypeError, vt) - - def test_dict_keys(self): - d = {1: 10, "a": "ABC"} - keys = d.keys() - self.assertEqual(len(keys), 2) - self.assertEqual(set(keys), {1, "a"}) - self.assertEqual(keys, {1, "a"}) - self.assertNotEqual(keys, {1, "a", "b"}) - self.assertNotEqual(keys, {1, "b"}) - self.assertNotEqual(keys, {1}) - self.assertNotEqual(keys, 42) - self.assertIn(1, keys) - self.assertIn("a", keys) - self.assertNotIn(10, keys) - self.assertNotIn("Z", keys) - self.assertEqual(d.keys(), d.keys()) - e = {1: 11, "a": "def"} - self.assertEqual(d.keys(), e.keys()) - del e["a"] - self.assertNotEqual(d.keys(), e.keys()) - - def test_dict_items(self): - d = {1: 10, "a": "ABC"} - items = d.items() - self.assertEqual(len(items), 2) - self.assertEqual(set(items), {(1, 10), ("a", "ABC")}) - self.assertEqual(items, {(1, 10), ("a", "ABC")}) - self.assertNotEqual(items, {(1, 10), ("a", "ABC"), "junk"}) - self.assertNotEqual(items, {(1, 10), ("a", "def")}) - self.assertNotEqual(items, {(1, 10)}) - self.assertNotEqual(items, 42) - self.assertIn((1, 10), items) - self.assertIn(("a", "ABC"), items) - self.assertNotIn((1, 11), items) - self.assertNotIn(1, items) - self.assertNotIn((), items) - self.assertNotIn((1,), items) - self.assertNotIn((1, 2, 3), items) - self.assertEqual(d.items(), d.items()) - e = d.copy() - self.assertEqual(d.items(), e.items()) - e["a"] = "def" - self.assertNotEqual(d.items(), e.items()) - - def test_dict_mixed_keys_items(self): - d = {(1, 1): 11, (2, 2): 22} - e = {1: 1, 2: 2} - self.assertEqual(d.keys(), e.items()) - self.assertNotEqual(d.items(), e.keys()) - - def test_dict_values(self): - d = {1: 10, "a": "ABC"} - values = d.values() - self.assertEqual(set(values), {10, "ABC"}) - self.assertEqual(len(values), 2) - - def test_dict_repr(self): - d = {1: 10, "a": "ABC"} - self.assertIsInstance(repr(d), str) - r = repr(d.items()) - self.assertIsInstance(r, str) - self.assertTrue(r == "dict_items([('a', 'ABC'), (1, 10)])" or - r == "dict_items([(1, 10), ('a', 'ABC')])") - r = repr(d.keys()) - self.assertIsInstance(r, str) - self.assertTrue(r == "dict_keys(['a', 1])" or - r == "dict_keys([1, 'a'])") - r = repr(d.values()) - self.assertIsInstance(r, str) - self.assertTrue(r == "dict_values(['ABC', 10])" or - r == "dict_values([10, 'ABC'])") - - def test_keys_set_operations(self): - d1 = {'a': 1, 'b': 2} - d2 = {'b': 3, 'c': 2} - d3 = {'d': 4, 'e': 5} - self.assertEqual(d1.keys() & d1.keys(), {'a', 'b'}) - self.assertEqual(d1.keys() & d2.keys(), {'b'}) - self.assertEqual(d1.keys() & d3.keys(), set()) - self.assertEqual(d1.keys() & set(d1.keys()), {'a', 'b'}) - self.assertEqual(d1.keys() & set(d2.keys()), {'b'}) - self.assertEqual(d1.keys() & set(d3.keys()), set()) - self.assertEqual(d1.keys() & tuple(d1.keys()), {'a', 'b'}) - - self.assertEqual(d1.keys() | d1.keys(), {'a', 'b'}) - self.assertEqual(d1.keys() | d2.keys(), {'a', 'b', 'c'}) - self.assertEqual(d1.keys() | d3.keys(), {'a', 'b', 'd', 'e'}) - self.assertEqual(d1.keys() | set(d1.keys()), {'a', 'b'}) - self.assertEqual(d1.keys() | set(d2.keys()), {'a', 'b', 'c'}) - self.assertEqual(d1.keys() | set(d3.keys()), - {'a', 'b', 'd', 'e'}) - self.assertEqual(d1.keys() | (1, 2), {'a', 'b', 1, 2}) - - self.assertEqual(d1.keys() ^ d1.keys(), set()) - self.assertEqual(d1.keys() ^ d2.keys(), {'a', 'c'}) - self.assertEqual(d1.keys() ^ d3.keys(), {'a', 'b', 'd', 'e'}) - self.assertEqual(d1.keys() ^ set(d1.keys()), set()) - self.assertEqual(d1.keys() ^ set(d2.keys()), {'a', 'c'}) - self.assertEqual(d1.keys() ^ set(d3.keys()), - {'a', 'b', 'd', 'e'}) - self.assertEqual(d1.keys() ^ tuple(d2.keys()), {'a', 'c'}) - - self.assertEqual(d1.keys() - d1.keys(), set()) - self.assertEqual(d1.keys() - d2.keys(), {'a'}) - self.assertEqual(d1.keys() - d3.keys(), {'a', 'b'}) - self.assertEqual(d1.keys() - set(d1.keys()), set()) - self.assertEqual(d1.keys() - set(d2.keys()), {'a'}) - self.assertEqual(d1.keys() - set(d3.keys()), {'a', 'b'}) - self.assertEqual(d1.keys() - (0, 1), {'a', 'b'}) - - self.assertFalse(d1.keys().isdisjoint(d1.keys())) - self.assertFalse(d1.keys().isdisjoint(d2.keys())) - self.assertFalse(d1.keys().isdisjoint(list(d2.keys()))) - self.assertFalse(d1.keys().isdisjoint(set(d2.keys()))) - self.assertTrue(d1.keys().isdisjoint({'x', 'y', 'z'})) - self.assertTrue(d1.keys().isdisjoint(['x', 'y', 'z'])) - self.assertTrue(d1.keys().isdisjoint(set(['x', 'y', 'z']))) - self.assertTrue(d1.keys().isdisjoint(set(['x', 'y']))) - self.assertTrue(d1.keys().isdisjoint(['x', 'y'])) - self.assertTrue(d1.keys().isdisjoint({})) - self.assertTrue(d1.keys().isdisjoint(d3.keys())) - - de = {} - self.assertTrue(de.keys().isdisjoint(set())) - self.assertTrue(de.keys().isdisjoint([])) - self.assertTrue(de.keys().isdisjoint(de.keys())) - self.assertTrue(de.keys().isdisjoint([1])) - - def test_items_set_operations(self): - d1 = {'a': 1, 'b': 2} - d2 = {'a': 2, 'b': 2} - d3 = {'d': 4, 'e': 5} - self.assertEqual( - d1.items() & d1.items(), {('a', 1), ('b', 2)}) - self.assertEqual(d1.items() & d2.items(), {('b', 2)}) - self.assertEqual(d1.items() & d3.items(), set()) - self.assertEqual(d1.items() & set(d1.items()), - {('a', 1), ('b', 2)}) - self.assertEqual(d1.items() & set(d2.items()), {('b', 2)}) - self.assertEqual(d1.items() & set(d3.items()), set()) - - self.assertEqual(d1.items() | d1.items(), - {('a', 1), ('b', 2)}) - self.assertEqual(d1.items() | d2.items(), - {('a', 1), ('a', 2), ('b', 2)}) - self.assertEqual(d1.items() | d3.items(), - {('a', 1), ('b', 2), ('d', 4), ('e', 5)}) - self.assertEqual(d1.items() | set(d1.items()), - {('a', 1), ('b', 2)}) - self.assertEqual(d1.items() | set(d2.items()), - {('a', 1), ('a', 2), ('b', 2)}) - self.assertEqual(d1.items() | set(d3.items()), - {('a', 1), ('b', 2), ('d', 4), ('e', 5)}) - - self.assertEqual(d1.items() ^ d1.items(), set()) - self.assertEqual(d1.items() ^ d2.items(), - {('a', 1), ('a', 2)}) - self.assertEqual(d1.items() ^ d3.items(), - {('a', 1), ('b', 2), ('d', 4), ('e', 5)}) - - self.assertEqual(d1.items() - d1.items(), set()) - self.assertEqual(d1.items() - d2.items(), {('a', 1)}) - self.assertEqual(d1.items() - d3.items(), {('a', 1), ('b', 2)}) - self.assertEqual(d1.items() - set(d1.items()), set()) - self.assertEqual(d1.items() - set(d2.items()), {('a', 1)}) - self.assertEqual(d1.items() - set(d3.items()), {('a', 1), ('b', 2)}) - - self.assertFalse(d1.items().isdisjoint(d1.items())) - self.assertFalse(d1.items().isdisjoint(d2.items())) - self.assertFalse(d1.items().isdisjoint(list(d2.items()))) - self.assertFalse(d1.items().isdisjoint(set(d2.items()))) - self.assertTrue(d1.items().isdisjoint({'x', 'y', 'z'})) - self.assertTrue(d1.items().isdisjoint(['x', 'y', 'z'])) - self.assertTrue(d1.items().isdisjoint(set(['x', 'y', 'z']))) - self.assertTrue(d1.items().isdisjoint(set(['x', 'y']))) - self.assertTrue(d1.items().isdisjoint({})) - self.assertTrue(d1.items().isdisjoint(d3.items())) - - de = {} - self.assertTrue(de.items().isdisjoint(set())) - self.assertTrue(de.items().isdisjoint([])) - self.assertTrue(de.items().isdisjoint(de.items())) - self.assertTrue(de.items().isdisjoint([1])) - - def test_recursive_repr(self): - d = {} - d[42] = d.values() - r = repr(d) - # Cannot perform a stronger test, as the contents of the repr - # are implementation-dependent. All we can say is that we - # want a str result, not an exception of any sort. - self.assertIsInstance(r, str) - d[42] = d.items() - r = repr(d) - # Again. - self.assertIsInstance(r, str) - - @unittest.skipIf(sys.platform == "win32", "thread 'main' has overflowed its stack") - def test_deeply_nested_repr(self): - d = {} - for i in range(sys.getrecursionlimit() + 100): - d = {42: d.values()} - self.assertRaises(RecursionError, repr, d) - - def test_copy(self): - d = {1: 10, "a": "ABC"} - self.assertRaises(TypeError, copy.copy, d.keys()) - self.assertRaises(TypeError, copy.copy, d.values()) - self.assertRaises(TypeError, copy.copy, d.items()) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_compare_error(self): - class Exc(Exception): - pass - - class BadEq: - def __hash__(self): - return 7 - def __eq__(self, other): - raise Exc - - k1, k2 = BadEq(), BadEq() - v1, v2 = BadEq(), BadEq() - d = {k1: v1} - - self.assertIn(k1, d) - self.assertIn(k1, d.keys()) - self.assertIn(v1, d.values()) - self.assertIn((k1, v1), d.items()) - - self.assertRaises(Exc, d.__contains__, k2) - self.assertRaises(Exc, d.keys().__contains__, k2) - self.assertRaises(Exc, d.items().__contains__, (k2, v1)) - self.assertRaises(Exc, d.items().__contains__, (k1, v2)) - with self.assertRaises(Exc): - v2 in d.values() - - def test_pickle(self): - d = {1: 10, "a": "ABC"} - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - self.assertRaises((TypeError, pickle.PicklingError), - pickle.dumps, d.keys(), proto) - self.assertRaises((TypeError, pickle.PicklingError), - pickle.dumps, d.values(), proto) - self.assertRaises((TypeError, pickle.PicklingError), - pickle.dumps, d.items(), proto) - - def test_abc_registry(self): - d = dict(a=1) - - self.assertIsInstance(d.keys(), collections.abc.KeysView) - self.assertIsInstance(d.keys(), collections.abc.MappingView) - self.assertIsInstance(d.keys(), collections.abc.Set) - self.assertIsInstance(d.keys(), collections.abc.Sized) - self.assertIsInstance(d.keys(), collections.abc.Iterable) - self.assertIsInstance(d.keys(), collections.abc.Container) - - self.assertIsInstance(d.values(), collections.abc.ValuesView) - self.assertIsInstance(d.values(), collections.abc.MappingView) - self.assertIsInstance(d.values(), collections.abc.Sized) - - self.assertIsInstance(d.items(), collections.abc.ItemsView) - self.assertIsInstance(d.items(), collections.abc.MappingView) - self.assertIsInstance(d.items(), collections.abc.Set) - self.assertIsInstance(d.items(), collections.abc.Sized) - self.assertIsInstance(d.items(), collections.abc.Iterable) - self.assertIsInstance(d.items(), collections.abc.Container) - - -if __name__ == "__main__": - unittest.main() +import collections.abc +import copy +import pickle +import sys +import unittest + +class DictSetTest(unittest.TestCase): + + def test_constructors_not_callable(self): + kt = type({}.keys()) + self.assertRaises(TypeError, kt, {}) + self.assertRaises(TypeError, kt) + it = type({}.items()) + self.assertRaises(TypeError, it, {}) + self.assertRaises(TypeError, it) + vt = type({}.values()) + self.assertRaises(TypeError, vt, {}) + self.assertRaises(TypeError, vt) + + def test_dict_keys(self): + d = {1: 10, "a": "ABC"} + keys = d.keys() + self.assertEqual(len(keys), 2) + self.assertEqual(set(keys), {1, "a"}) + self.assertEqual(keys, {1, "a"}) + self.assertNotEqual(keys, {1, "a", "b"}) + self.assertNotEqual(keys, {1, "b"}) + self.assertNotEqual(keys, {1}) + self.assertNotEqual(keys, 42) + self.assertIn(1, keys) + self.assertIn("a", keys) + self.assertNotIn(10, keys) + self.assertNotIn("Z", keys) + self.assertEqual(d.keys(), d.keys()) + e = {1: 11, "a": "def"} + self.assertEqual(d.keys(), e.keys()) + del e["a"] + self.assertNotEqual(d.keys(), e.keys()) + + def test_dict_items(self): + d = {1: 10, "a": "ABC"} + items = d.items() + self.assertEqual(len(items), 2) + self.assertEqual(set(items), {(1, 10), ("a", "ABC")}) + self.assertEqual(items, {(1, 10), ("a", "ABC")}) + self.assertNotEqual(items, {(1, 10), ("a", "ABC"), "junk"}) + self.assertNotEqual(items, {(1, 10), ("a", "def")}) + self.assertNotEqual(items, {(1, 10)}) + self.assertNotEqual(items, 42) + self.assertIn((1, 10), items) + self.assertIn(("a", "ABC"), items) + self.assertNotIn((1, 11), items) + self.assertNotIn(1, items) + self.assertNotIn((), items) + self.assertNotIn((1,), items) + self.assertNotIn((1, 2, 3), items) + self.assertEqual(d.items(), d.items()) + e = d.copy() + self.assertEqual(d.items(), e.items()) + e["a"] = "def" + self.assertNotEqual(d.items(), e.items()) + + def test_dict_mixed_keys_items(self): + d = {(1, 1): 11, (2, 2): 22} + e = {1: 1, 2: 2} + self.assertEqual(d.keys(), e.items()) + self.assertNotEqual(d.items(), e.keys()) + + def test_dict_values(self): + d = {1: 10, "a": "ABC"} + values = d.values() + self.assertEqual(set(values), {10, "ABC"}) + self.assertEqual(len(values), 2) + + def test_dict_repr(self): + d = {1: 10, "a": "ABC"} + self.assertIsInstance(repr(d), str) + r = repr(d.items()) + self.assertIsInstance(r, str) + self.assertTrue(r == "dict_items([('a', 'ABC'), (1, 10)])" or + r == "dict_items([(1, 10), ('a', 'ABC')])") + r = repr(d.keys()) + self.assertIsInstance(r, str) + self.assertTrue(r == "dict_keys(['a', 1])" or + r == "dict_keys([1, 'a'])") + r = repr(d.values()) + self.assertIsInstance(r, str) + self.assertTrue(r == "dict_values(['ABC', 10])" or + r == "dict_values([10, 'ABC'])") + + def test_keys_set_operations(self): + d1 = {'a': 1, 'b': 2} + d2 = {'b': 3, 'c': 2} + d3 = {'d': 4, 'e': 5} + d4 = {'d': 4} + + class CustomSet(set): + def intersection(self, other): + return CustomSet(super().intersection(other)) + + self.assertEqual(d1.keys() & d1.keys(), {'a', 'b'}) + self.assertEqual(d1.keys() & d2.keys(), {'b'}) + self.assertEqual(d1.keys() & d3.keys(), set()) + self.assertEqual(d1.keys() & set(d1.keys()), {'a', 'b'}) + self.assertEqual(d1.keys() & set(d2.keys()), {'b'}) + self.assertEqual(d1.keys() & set(d3.keys()), set()) + self.assertEqual(d1.keys() & tuple(d1.keys()), {'a', 'b'}) + self.assertEqual(d3.keys() & d4.keys(), {'d'}) + self.assertEqual(d4.keys() & d3.keys(), {'d'}) + self.assertEqual(d4.keys() & set(d3.keys()), {'d'}) + self.assertIsInstance(d4.keys() & frozenset(d3.keys()), set) + self.assertIsInstance(frozenset(d3.keys()) & d4.keys(), set) + self.assertIs(type(d4.keys() & CustomSet(d3.keys())), set) + self.assertIs(type(d1.keys() & []), set) + self.assertIs(type([] & d1.keys()), set) + + self.assertEqual(d1.keys() | d1.keys(), {'a', 'b'}) + self.assertEqual(d1.keys() | d2.keys(), {'a', 'b', 'c'}) + self.assertEqual(d1.keys() | d3.keys(), {'a', 'b', 'd', 'e'}) + self.assertEqual(d1.keys() | set(d1.keys()), {'a', 'b'}) + self.assertEqual(d1.keys() | set(d2.keys()), {'a', 'b', 'c'}) + self.assertEqual(d1.keys() | set(d3.keys()), + {'a', 'b', 'd', 'e'}) + self.assertEqual(d1.keys() | (1, 2), {'a', 'b', 1, 2}) + + self.assertEqual(d1.keys() ^ d1.keys(), set()) + self.assertEqual(d1.keys() ^ d2.keys(), {'a', 'c'}) + self.assertEqual(d1.keys() ^ d3.keys(), {'a', 'b', 'd', 'e'}) + self.assertEqual(d1.keys() ^ set(d1.keys()), set()) + self.assertEqual(d1.keys() ^ set(d2.keys()), {'a', 'c'}) + self.assertEqual(d1.keys() ^ set(d3.keys()), + {'a', 'b', 'd', 'e'}) + self.assertEqual(d1.keys() ^ tuple(d2.keys()), {'a', 'c'}) + + self.assertEqual(d1.keys() - d1.keys(), set()) + self.assertEqual(d1.keys() - d2.keys(), {'a'}) + self.assertEqual(d1.keys() - d3.keys(), {'a', 'b'}) + self.assertEqual(d1.keys() - set(d1.keys()), set()) + self.assertEqual(d1.keys() - set(d2.keys()), {'a'}) + self.assertEqual(d1.keys() - set(d3.keys()), {'a', 'b'}) + self.assertEqual(d1.keys() - (0, 1), {'a', 'b'}) + + self.assertFalse(d1.keys().isdisjoint(d1.keys())) + self.assertFalse(d1.keys().isdisjoint(d2.keys())) + self.assertFalse(d1.keys().isdisjoint(list(d2.keys()))) + self.assertFalse(d1.keys().isdisjoint(set(d2.keys()))) + self.assertTrue(d1.keys().isdisjoint({'x', 'y', 'z'})) + self.assertTrue(d1.keys().isdisjoint(['x', 'y', 'z'])) + self.assertTrue(d1.keys().isdisjoint(set(['x', 'y', 'z']))) + self.assertTrue(d1.keys().isdisjoint(set(['x', 'y']))) + self.assertTrue(d1.keys().isdisjoint(['x', 'y'])) + self.assertTrue(d1.keys().isdisjoint({})) + self.assertTrue(d1.keys().isdisjoint(d3.keys())) + + de = {} + self.assertTrue(de.keys().isdisjoint(set())) + self.assertTrue(de.keys().isdisjoint([])) + self.assertTrue(de.keys().isdisjoint(de.keys())) + self.assertTrue(de.keys().isdisjoint([1])) + + def test_items_set_operations(self): + d1 = {'a': 1, 'b': 2} + d2 = {'a': 2, 'b': 2} + d3 = {'d': 4, 'e': 5} + self.assertEqual( + d1.items() & d1.items(), {('a', 1), ('b', 2)}) + self.assertEqual(d1.items() & d2.items(), {('b', 2)}) + self.assertEqual(d1.items() & d3.items(), set()) + self.assertEqual(d1.items() & set(d1.items()), + {('a', 1), ('b', 2)}) + self.assertEqual(d1.items() & set(d2.items()), {('b', 2)}) + self.assertEqual(d1.items() & set(d3.items()), set()) + + self.assertEqual(d1.items() | d1.items(), + {('a', 1), ('b', 2)}) + self.assertEqual(d1.items() | d2.items(), + {('a', 1), ('a', 2), ('b', 2)}) + self.assertEqual(d1.items() | d3.items(), + {('a', 1), ('b', 2), ('d', 4), ('e', 5)}) + self.assertEqual(d1.items() | set(d1.items()), + {('a', 1), ('b', 2)}) + self.assertEqual(d1.items() | set(d2.items()), + {('a', 1), ('a', 2), ('b', 2)}) + self.assertEqual(d1.items() | set(d3.items()), + {('a', 1), ('b', 2), ('d', 4), ('e', 5)}) + + self.assertEqual(d1.items() ^ d1.items(), set()) + self.assertEqual(d1.items() ^ d2.items(), + {('a', 1), ('a', 2)}) + self.assertEqual(d1.items() ^ d3.items(), + {('a', 1), ('b', 2), ('d', 4), ('e', 5)}) + + self.assertEqual(d1.items() - d1.items(), set()) + self.assertEqual(d1.items() - d2.items(), {('a', 1)}) + self.assertEqual(d1.items() - d3.items(), {('a', 1), ('b', 2)}) + self.assertEqual(d1.items() - set(d1.items()), set()) + self.assertEqual(d1.items() - set(d2.items()), {('a', 1)}) + self.assertEqual(d1.items() - set(d3.items()), {('a', 1), ('b', 2)}) + + self.assertFalse(d1.items().isdisjoint(d1.items())) + self.assertFalse(d1.items().isdisjoint(d2.items())) + self.assertFalse(d1.items().isdisjoint(list(d2.items()))) + self.assertFalse(d1.items().isdisjoint(set(d2.items()))) + self.assertTrue(d1.items().isdisjoint({'x', 'y', 'z'})) + self.assertTrue(d1.items().isdisjoint(['x', 'y', 'z'])) + self.assertTrue(d1.items().isdisjoint(set(['x', 'y', 'z']))) + self.assertTrue(d1.items().isdisjoint(set(['x', 'y']))) + self.assertTrue(d1.items().isdisjoint({})) + self.assertTrue(d1.items().isdisjoint(d3.items())) + + de = {} + self.assertTrue(de.items().isdisjoint(set())) + self.assertTrue(de.items().isdisjoint([])) + self.assertTrue(de.items().isdisjoint(de.items())) + self.assertTrue(de.items().isdisjoint([1])) + + def test_set_operations_with_iterator(self): + origin = {1: 2, 3: 4} + self.assertEqual(origin.keys() & iter([1, 2]), {1}) + self.assertEqual(origin.keys() | iter([1, 2]), {1, 2, 3}) + self.assertEqual(origin.keys() ^ iter([1, 2]), {2, 3}) + self.assertEqual(origin.keys() - iter([1, 2]), {3}) + + items = origin.items() + self.assertEqual(items & iter([(1, 2)]), {(1, 2)}) + self.assertEqual(items ^ iter([(1, 2)]), {(3, 4)}) + self.assertEqual(items | iter([(1, 2)]), {(1, 2), (3, 4)}) + self.assertEqual(items - iter([(1, 2)]), {(3, 4)}) + + def test_set_operations_with_noniterable(self): + with self.assertRaises(TypeError): + {}.keys() & 1 + with self.assertRaises(TypeError): + {}.keys() | 1 + with self.assertRaises(TypeError): + {}.keys() ^ 1 + with self.assertRaises(TypeError): + {}.keys() - 1 + + with self.assertRaises(TypeError): + {}.items() & 1 + with self.assertRaises(TypeError): + {}.items() | 1 + with self.assertRaises(TypeError): + {}.items() ^ 1 + with self.assertRaises(TypeError): + {}.items() - 1 + + def test_recursive_repr(self): + d = {} + d[42] = d.values() + r = repr(d) + # Cannot perform a stronger test, as the contents of the repr + # are implementation-dependent. All we can say is that we + # want a str result, not an exception of any sort. + self.assertIsInstance(r, str) + d[42] = d.items() + r = repr(d) + # Again. + self.assertIsInstance(r, str) + + def test_deeply_nested_repr(self): + d = {} + for i in range(sys.getrecursionlimit() + 100): + d = {42: d.values()} + self.assertRaises(RecursionError, repr, d) + + def test_copy(self): + d = {1: 10, "a": "ABC"} + self.assertRaises(TypeError, copy.copy, d.keys()) + self.assertRaises(TypeError, copy.copy, d.values()) + self.assertRaises(TypeError, copy.copy, d.items()) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_compare_error(self): + class Exc(Exception): + pass + + class BadEq: + def __hash__(self): + return 7 + def __eq__(self, other): + raise Exc + + k1, k2 = BadEq(), BadEq() + v1, v2 = BadEq(), BadEq() + d = {k1: v1} + + self.assertIn(k1, d) + self.assertIn(k1, d.keys()) + self.assertIn(v1, d.values()) + self.assertIn((k1, v1), d.items()) + + self.assertRaises(Exc, d.__contains__, k2) + self.assertRaises(Exc, d.keys().__contains__, k2) + self.assertRaises(Exc, d.items().__contains__, (k2, v1)) + self.assertRaises(Exc, d.items().__contains__, (k1, v2)) + with self.assertRaises(Exc): + v2 in d.values() + + def test_pickle(self): + d = {1: 10, "a": "ABC"} + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + self.assertRaises((TypeError, pickle.PicklingError), + pickle.dumps, d.keys(), proto) + self.assertRaises((TypeError, pickle.PicklingError), + pickle.dumps, d.values(), proto) + self.assertRaises((TypeError, pickle.PicklingError), + pickle.dumps, d.items(), proto) + + def test_abc_registry(self): + d = dict(a=1) + + self.assertIsInstance(d.keys(), collections.abc.KeysView) + self.assertIsInstance(d.keys(), collections.abc.MappingView) + self.assertIsInstance(d.keys(), collections.abc.Set) + self.assertIsInstance(d.keys(), collections.abc.Sized) + self.assertIsInstance(d.keys(), collections.abc.Iterable) + self.assertIsInstance(d.keys(), collections.abc.Container) + + self.assertIsInstance(d.values(), collections.abc.ValuesView) + self.assertIsInstance(d.values(), collections.abc.MappingView) + self.assertIsInstance(d.values(), collections.abc.Sized) + + self.assertIsInstance(d.items(), collections.abc.ItemsView) + self.assertIsInstance(d.items(), collections.abc.MappingView) + self.assertIsInstance(d.items(), collections.abc.Set) + self.assertIsInstance(d.items(), collections.abc.Sized) + self.assertIsInstance(d.items(), collections.abc.Iterable) + self.assertIsInstance(d.items(), collections.abc.Container) + + +if __name__ == "__main__": + unittest.main() From fda0e0a94ed24ae4ea7dd04163557da93efde8d1 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 02:16:42 +0900 Subject: [PATCH 10/26] adjust failing markers for test_dict --- Lib/test/test_dict.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index c095144d2..1033a6d8f 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -105,6 +105,8 @@ class DictTest(unittest.TestCase): self.assertRaises(TypeError, d.items, None) self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_views_mapping(self): mappingproxy = type(type.__dict__) class Dict(dict): From 71c2772d9ee73b87ad36911ec076428242966ee0 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:18:29 +0900 Subject: [PATCH 11/26] Update test_set from CPython 3.10.5 --- Lib/test/test_set.py | 260 ++++++++++++++++++++++++++++++++----------- 1 file changed, 198 insertions(+), 62 deletions(-) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 4fd8f357b..8dab8d4bf 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1,7 +1,7 @@ import unittest from test import support -from test.support import os_helper, warnings_helper -# import gc +from test.support import warnings_helper +import gc import weakref import operator import copy @@ -318,20 +318,6 @@ class TestJointOps: name = repr(s).partition('(')[0] # strip class name self.assertEqual(repr(s), '%s({%s(...)})' % (name, name)) - def test_cyclical_print(self): - w = ReprWrapper() - s = self.thetype([w]) - w.value = s - fo = open(os_helper.TESTFN, "w") - try: - fo.write(str(s)) - fo.close() - fo = open(os_helper.TESTFN, "r") - self.assertEqual(fo.read(), repr(s)) - finally: - fo.close() - os_helper.unlink(os_helper.TESTFN) - # TODO: RUSTPYTHON @unittest.expectedFailure def test_do_not_rehash_dict_keys(self): @@ -376,17 +362,14 @@ class TestSet(TestJointOps, unittest.TestCase): thetype = set basetype = set - def test_contains(self): - super().test_contains() - def test_init(self): s = self.thetype() s.__init__(self.word) self.assertEqual(s, set(self.word)) s.__init__(self.otherword) self.assertEqual(s, set(self.otherword)) - self.assertRaises(TypeError, s.__init__, s, 2); - self.assertRaises(TypeError, s.__init__, 1); + self.assertRaises(TypeError, s.__init__, s, 2) + self.assertRaises(TypeError, s.__init__, 1) def test_constructor_identity(self): s = self.thetype(range(3)) @@ -618,6 +601,7 @@ class TestSet(TestJointOps, unittest.TestCase): p = weakref.proxy(s) self.assertEqual(str(p), str(s)) s = None + support.gc_collect() # For PyPy or other GCs. self.assertRaises(ReferenceError, str, p) def test_rich_compare(self): @@ -668,17 +652,12 @@ class TestSetSubclass(TestSet): thetype = SetSubclass basetype = set - def test_pickling(self): - super().test_pickling() - - def test_cyclical_repr(self): - super().test_cyclical_repr() - class SetSubclassWithKeywordArgs(set): def __init__(self, iterable=[], newarg=None): set.__init__(self, iterable) class TestSetSubclassWithKeywordArgs(TestSet): + def test_keywords_in_subclass(self): 'SF bug #1486663 -- this used to erroneously raise a TypeError' SetSubclassWithKeywordArgs(newarg=1) @@ -692,15 +671,6 @@ class TestFrozenSet(TestJointOps, unittest.TestCase): s.__init__(self.otherword) self.assertEqual(s, set(self.word)) - def test_singleton_empty_frozenset(self): - f = frozenset() - efs = [frozenset(), frozenset([]), frozenset(()), frozenset(''), - frozenset(), frozenset([]), frozenset(()), frozenset(''), - frozenset(range(0)), frozenset(frozenset()), - frozenset(f), f] - # All of the empty frozensets should have just one id() - self.assertEqual(len(set(map(id, efs))), 1) - def test_constructor_identity(self): s = self.thetype(range(3)) t = self.thetype(s) @@ -774,9 +744,6 @@ class TestFrozenSetSubclass(TestFrozenSet): thetype = FrozenSetSubclass basetype = frozenset - def test_pickling(self): - super().test_pickling() - def test_constructor_identity(self): s = self.thetype(range(3)) t = self.thetype(s) @@ -825,17 +792,6 @@ class TestBasicOps: sorted_repr_values.sort() self.assertEqual(result, sorted_repr_values) - def test_print(self): - try: - fo = open(os_helper.TESTFN, "w") - fo.write(str(self.set)) - fo.close() - fo = open(os_helper.TESTFN, "r") - self.assertEqual(fo.read(), repr(self.set)) - finally: - fo.close() - os_helper.unlink(os_helper.TESTFN) - def test_length(self): self.assertEqual(len(self.set), self.length) @@ -917,6 +873,12 @@ class TestBasicOps: self.assertEqual(self.set, copy, "%s != %s" % (self.set, copy)) + def test_issue_37219(self): + with self.assertRaises(TypeError): + set().difference(123) + with self.assertRaises(TypeError): + set().difference_update(123) + #------------------------------------------------------------------------------ class TestBasicOpsEmpty(TestBasicOps, unittest.TestCase): @@ -1576,36 +1538,24 @@ class TestCopyingSingleton(TestCopying, unittest.TestCase): def setUp(self): self.set = set(["hello"]) - def test_deep_copy(self): - super().test_deep_copy() - #------------------------------------------------------------------------------ class TestCopyingTriple(TestCopying, unittest.TestCase): def setUp(self): self.set = set(["zero", 0, None]) - def test_deep_copy(self): - super().test_deep_copy() - #------------------------------------------------------------------------------ class TestCopyingTuple(TestCopying, unittest.TestCase): def setUp(self): self.set = set([(1, 2)]) - def test_deep_copy(self): - super().test_deep_copy() - #------------------------------------------------------------------------------ class TestCopyingNested(TestCopying, unittest.TestCase): def setUp(self): self.set = set([((1, 2), (3, 4))]) - def test_deep_copy(self): - super().test_deep_copy() - #============================================================================== class TestIdentities(unittest.TestCase): @@ -1828,6 +1778,192 @@ class TestWeirdBugs(unittest.TestCase): s = {0} s.update(other) + +class TestOperationsMutating: + """Regression test for bpo-46615""" + + constructor1 = None + constructor2 = None + + def make_sets_of_bad_objects(self): + class Bad: + def __eq__(self, other): + if not enabled: + return False + if randrange(20) == 0: + set1.clear() + if randrange(20) == 0: + set2.clear() + return bool(randrange(2)) + def __hash__(self): + return randrange(2) + # Don't behave poorly during construction. + enabled = False + set1 = self.constructor1(Bad() for _ in range(randrange(50))) + set2 = self.constructor2(Bad() for _ in range(randrange(50))) + # Now start behaving poorly + enabled = True + return set1, set2 + + def check_set_op_does_not_crash(self, function): + for _ in range(100): + set1, set2 = self.make_sets_of_bad_objects() + try: + function(set1, set2) + except RuntimeError as e: + # Just make sure we don't crash here. + self.assertIn("changed size during iteration", str(e)) + + +class TestBinaryOpsMutating(TestOperationsMutating): + + def test_eq_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a == b) + + def test_ne_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a != b) + + def test_lt_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a < b) + + def test_le_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a <= b) + + def test_gt_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a > b) + + def test_ge_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a >= b) + + def test_and_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a & b) + + def test_or_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a | b) + + def test_sub_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a - b) + + def test_xor_with_mutation(self): + self.check_set_op_does_not_crash(lambda a, b: a ^ b) + + def test_iadd_with_mutation(self): + def f(a, b): + a &= b + self.check_set_op_does_not_crash(f) + + def test_ior_with_mutation(self): + def f(a, b): + a |= b + self.check_set_op_does_not_crash(f) + + def test_isub_with_mutation(self): + def f(a, b): + a -= b + self.check_set_op_does_not_crash(f) + + def test_ixor_with_mutation(self): + def f(a, b): + a ^= b + self.check_set_op_does_not_crash(f) + + def test_iteration_with_mutation(self): + def f1(a, b): + for x in a: + pass + for y in b: + pass + def f2(a, b): + for y in b: + pass + for x in a: + pass + def f3(a, b): + for x, y in zip(a, b): + pass + self.check_set_op_does_not_crash(f1) + self.check_set_op_does_not_crash(f2) + self.check_set_op_does_not_crash(f3) + + +class TestBinaryOpsMutating_Set_Set(TestBinaryOpsMutating, unittest.TestCase): + constructor1 = set + constructor2 = set + +class TestBinaryOpsMutating_Subclass_Subclass(TestBinaryOpsMutating, unittest.TestCase): + constructor1 = SetSubclass + constructor2 = SetSubclass + +class TestBinaryOpsMutating_Set_Subclass(TestBinaryOpsMutating, unittest.TestCase): + constructor1 = set + constructor2 = SetSubclass + +class TestBinaryOpsMutating_Subclass_Set(TestBinaryOpsMutating, unittest.TestCase): + constructor1 = SetSubclass + constructor2 = set + + +class TestMethodsMutating(TestOperationsMutating): + + def test_issubset_with_mutation(self): + self.check_set_op_does_not_crash(set.issubset) + + def test_issuperset_with_mutation(self): + self.check_set_op_does_not_crash(set.issuperset) + + def test_intersection_with_mutation(self): + self.check_set_op_does_not_crash(set.intersection) + + def test_union_with_mutation(self): + self.check_set_op_does_not_crash(set.union) + + def test_difference_with_mutation(self): + self.check_set_op_does_not_crash(set.difference) + + def test_symmetric_difference_with_mutation(self): + self.check_set_op_does_not_crash(set.symmetric_difference) + + def test_isdisjoint_with_mutation(self): + self.check_set_op_does_not_crash(set.isdisjoint) + + def test_difference_update_with_mutation(self): + self.check_set_op_does_not_crash(set.difference_update) + + def test_intersection_update_with_mutation(self): + self.check_set_op_does_not_crash(set.intersection_update) + + def test_symmetric_difference_update_with_mutation(self): + self.check_set_op_does_not_crash(set.symmetric_difference_update) + + def test_update_with_mutation(self): + self.check_set_op_does_not_crash(set.update) + + +class TestMethodsMutating_Set_Set(TestMethodsMutating, unittest.TestCase): + constructor1 = set + constructor2 = set + +class TestMethodsMutating_Subclass_Subclass(TestMethodsMutating, unittest.TestCase): + constructor1 = SetSubclass + constructor2 = SetSubclass + +class TestMethodsMutating_Set_Subclass(TestMethodsMutating, unittest.TestCase): + constructor1 = set + constructor2 = SetSubclass + +class TestMethodsMutating_Subclass_Set(TestMethodsMutating, unittest.TestCase): + constructor1 = SetSubclass + constructor2 = set + +class TestMethodsMutating_Set_Dict(TestMethodsMutating, unittest.TestCase): + constructor1 = set + constructor2 = dict.fromkeys + +class TestMethodsMutating_Set_List(TestMethodsMutating, unittest.TestCase): + constructor1 = set + constructor2 = list + + # Application tests (based on David Eppstein's graph recipes ==================================== def powerset(U): From da54c40566bdbbf7012a5f6ec905aca9bba93c6b Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 02:20:44 +0900 Subject: [PATCH 12/26] adjust failing markers for test_set --- Lib/test/test_set.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 8dab8d4bf..e90f3eb91 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1815,6 +1815,7 @@ class TestOperationsMutating: self.assertIn("changed size during iteration", str(e)) +@unittest.skip("TODO: RUSTPYTHON; segfault") class TestBinaryOpsMutating(TestOperationsMutating): def test_eq_with_mutation(self): @@ -1903,6 +1904,7 @@ class TestBinaryOpsMutating_Subclass_Set(TestBinaryOpsMutating, unittest.TestCas constructor2 = set +@unittest.skip("TODO: RUSTPYTHON; segfault") class TestMethodsMutating(TestOperationsMutating): def test_issubset_with_mutation(self): From 9537149572a90a7fc7dd5d067df944a36e2c9d01 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:22:02 +0900 Subject: [PATCH 13/26] Update collections/test_{collections,defaultdict} from CPython 3.10.5 --- Lib/collections/__init__.py | 188 ++++++++++++++++------------------- Lib/collections/abc.py | 1 + Lib/test/test_collections.py | 116 +++++++++++++-------- Lib/test/test_defaultdict.py | 71 +++++++------ 4 files changed, 198 insertions(+), 178 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index ace8db113..8a2b22083 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -27,7 +27,6 @@ __all__ = [ ] import _collections_abc -import heapq as _heapq import sys as _sys from itertools import chain as _chain @@ -56,22 +55,6 @@ except ImportError: from ._defaultdict import defaultdict -def __getattr__(name): - # For backwards compatibility, continue to make the collections ABCs - # through Python 3.6 available through the collections module. - # Note, no new collections ABCs were added in Python 3.7 - if name in _collections_abc.__all__: - obj = getattr(_collections_abc, name) - import warnings - warnings.warn("Using or importing the ABCs from 'collections' instead " - "of from 'collections.abc' is deprecated since Python 3.3, " - "and in 3.10 it will stop working", - DeprecationWarning, stacklevel=2) - globals()[name] = obj - return obj - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') - - ################################################################################ ### OrderedDict ################################################################################ @@ -111,17 +94,10 @@ class OrderedDict(dict): # Individual links are kept alive by the hard reference in self.__map. # Those hard references disappear when a key is deleted from an OrderedDict. - def __init__(*args, **kwds): + def __init__(self, other=(), /, **kwds): '''Initialize an ordered dictionary. The signature is the same as regular dictionaries. Keyword argument order is preserved. ''' - if not args: - raise TypeError("descriptor '__init__' of 'OrderedDict' object " - "needs an argument") - self, *args = args - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: self.__root except AttributeError: @@ -129,7 +105,7 @@ class OrderedDict(dict): self.__root = root = _proxy(self.__hardroot) root.prev = root.next = root self.__map = {} - self.__update(*args, **kwds) + self.__update(other, **kwds) def __setitem__(self, key, value, dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link): @@ -455,8 +431,8 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non _make.__func__.__doc__ = (f'Make a new {typename} object from a sequence ' 'or iterable') - def _replace(_self, /, **kwds): - result = _self._make(_map(kwds.pop, field_names, _self)) + def _replace(self, /, **kwds): + result = self._make(_map(kwds.pop, field_names, self)) if kwds: raise ValueError(f'Got unexpected field names: {list(kwds)!r}') return result @@ -500,6 +476,7 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non '__repr__': __repr__, '_asdict': _asdict, '__getnewargs__': __getnewargs__, + '__match_args__': field_names, } for index, name in enumerate(field_names): doc = _sys.intern(f'Alias for field number {index}') @@ -589,7 +566,7 @@ class Counter(dict): # http://code.activestate.com/recipes/259174/ # Knuth, TAOCP Vol. II section 4.6.3 - def __init__(*args, **kwds): + def __init__(self, iterable=None, /, **kwds): '''Create a new, empty Counter object. And if given, count elements from an input iterable. Or, initialize the count from another mapping of elements to their counts. @@ -600,20 +577,18 @@ class Counter(dict): >>> c = Counter(a=4, b=2) # a new counter from keyword args ''' - if not args: - raise TypeError("descriptor '__init__' of 'Counter' object " - "needs an argument") - self, *args = args - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - super(Counter, self).__init__() - self.update(*args, **kwds) + super().__init__() + self.update(iterable, **kwds) def __missing__(self, key): 'The count of elements not in the Counter is zero.' # Needed so that self[missing_item] does not raise KeyError return 0 + def total(self): + 'Sum of the counts' + return sum(self.values()) + def most_common(self, n=None): '''List the n most common elements and their counts from the most common to the least. If n is None, then list all element counts. @@ -625,7 +600,10 @@ class Counter(dict): # Emulate Bag.sortedByCount from Smalltalk if n is None: return sorted(self.items(), key=_itemgetter(1), reverse=True) - return _heapq.nlargest(n, self.items(), key=_itemgetter(1)) + + # Lazy import to speedup Python startup time + import heapq + return heapq.nlargest(n, self.items(), key=_itemgetter(1)) def elements(self): '''Iterator over elements repeating each as many times as its count. @@ -663,7 +641,7 @@ class Counter(dict): raise NotImplementedError( 'Counter.fromkeys() is undefined. Use Counter(iterable) instead.') - def update(*args, **kwds): + def update(self, iterable=None, /, **kwds): '''Like dict.update() but add counts instead of replacing them. Source can be an iterable, a dictionary, or another Counter instance. @@ -683,14 +661,6 @@ class Counter(dict): # contexts. Instead, we implement straight-addition. Both the inputs # and outputs are allowed to contain zero and negative counts. - if not args: - raise TypeError("descriptor 'update' of 'Counter' object " - "needs an argument") - self, *args = args - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - iterable = args[0] if args else None - if iterable is not None: if isinstance(iterable, _collections_abc.Mapping): if self: @@ -699,13 +669,13 @@ class Counter(dict): self[elem] = count + self_get(elem, 0) else: # fast path when counter is empty - super(Counter, self).update(iterable) + super().update(iterable) else: _count_elements(self, iterable) if kwds: self.update(kwds) - def subtract(*args, **kwds): + def subtract(self, iterable=None, /, **kwds): '''Like dict.update() but subtracts counts instead of replacing them. Counts can be reduced below zero. Both the inputs and outputs are allowed to contain zero and negative counts. @@ -721,14 +691,6 @@ class Counter(dict): -1 ''' - if not args: - raise TypeError("descriptor 'subtract' of 'Counter' object " - "needs an argument") - self, *args = args - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - iterable = args[0] if args else None - if iterable is not None: self_get = self.get if isinstance(iterable, _collections_abc.Mapping): @@ -752,6 +714,42 @@ class Counter(dict): if elem in self: super().__delitem__(elem) + def __eq__(self, other): + 'True if all counts agree. Missing counts are treated as zero.' + if not isinstance(other, Counter): + return NotImplemented + return all(self[e] == other[e] for c in (self, other) for e in c) + + def __ne__(self, other): + 'True if any counts disagree. Missing counts are treated as zero.' + if not isinstance(other, Counter): + return NotImplemented + return not self == other + + def __le__(self, other): + 'True if all counts in self are a subset of those in other.' + if not isinstance(other, Counter): + return NotImplemented + return all(self[e] <= other[e] for c in (self, other) for e in c) + + def __lt__(self, other): + 'True if all counts in self are a proper subset of those in other.' + if not isinstance(other, Counter): + return NotImplemented + return self <= other and self != other + + def __ge__(self, other): + 'True if all counts in self are a superset of those in other.' + if not isinstance(other, Counter): + return NotImplemented + return all(self[e] >= other[e] for c in (self, other) for e in c) + + def __gt__(self, other): + 'True if all counts in self are a proper superset of those in other.' + if not isinstance(other, Counter): + return NotImplemented + return self >= other and self != other + def __repr__(self): if not self: return f'{self.__class__.__name__}()' @@ -772,12 +770,30 @@ class Counter(dict): # To strip negative and zero counts, add-in an empty counter: # c += Counter() # - # Rich comparison operators for multiset subset and superset tests - # are deliberately omitted due to semantic conflicts with the - # existing inherited dict equality method. Subset and superset - # semantics ignore zero counts and require that p≤q ∧ p≥q → p=q; - # however, that would not be the case for p=Counter(a=1, b=0) - # and q=Counter(a=1) where the dictionaries are not equal. + # Results are ordered according to when an element is first + # encountered in the left operand and then by the order + # encountered in the right operand. + # + # When the multiplicities are all zero or one, multiset operations + # are guaranteed to be equivalent to the corresponding operations + # for regular sets. + # Given counter multisets such as: + # cp = Counter(a=1, b=0, c=1) + # cq = Counter(c=1, d=0, e=1) + # The corresponding regular sets would be: + # sp = {'a', 'c'} + # sq = {'c', 'e'} + # All of the following relations would hold: + # set(cp + cq) == sp | sq + # set(cp - cq) == sp - sq + # set(cp | cq) == sp | sq + # set(cp & cq) == sp & sq + # (cp == cq) == (sp == sq) + # (cp != cq) == (sp != sq) + # (cp <= cq) == (sp <= sq) + # (cp < cq) == (sp < sq) + # (cp >= cq) == (sp >= sq) + # (cp > cq) == (sp > sq) def __add__(self, other): '''Add counts from two counters. @@ -1006,12 +1022,15 @@ class ChainMap(_collections_abc.MutableMapping): __copy__ = copy - def new_child(self, m=None): # like Django's Context.push() + def new_child(self, m=None, **kwargs): # like Django's Context.push() '''New ChainMap with a new map followed by all previous maps. If no map is provided, an empty dict is used. + Keyword arguments update the map or new empty dict. ''' if m is None: - m = {} + m = kwargs + elif kwargs: + m.update(kwargs) return self.__class__(m, *self.maps) @property @@ -1073,34 +1092,16 @@ class ChainMap(_collections_abc.MutableMapping): class UserDict(_collections_abc.MutableMapping): # Start by filling-out the abstract methods - def __init__(*args, **kwargs): - if not args: - raise TypeError("descriptor '__init__' of 'UserDict' object " - "needs an argument") - self, *args = args - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - if args: - dict = args[0] - elif 'dict' in kwargs: - dict = kwargs.pop('dict') - import warnings - warnings.warn("Passing 'dict' as keyword argument is deprecated", - DeprecationWarning, stacklevel=2) - else: - dict = None + def __init__(self, dict=None, /, **kwargs): self.data = {} if dict is not None: self.update(dict) - if len(kwargs): + if kwargs: self.update(kwargs) def __len__(self): return len(self.data) - def __bool__(self): - return bool(self.data) - def __getitem__(self, key): if key in self.data: return self.data[key] @@ -1153,9 +1154,6 @@ class UserDict(_collections_abc.MutableMapping): inst.__dict__["data"] = self.__dict__["data"].copy() return inst - def __sizeof__(self): - return _sys.getsizeof(self.data) - def copy(self): if self.__class__ is UserDict: return UserDict(self.data.copy()) @@ -1222,9 +1220,6 @@ class UserList(_collections_abc.MutableSequence): def __len__(self): return len(self.data) - def __bool__(self): - return bool(self.data) - def __getitem__(self, i): if isinstance(i, slice): return self.__class__(self.data[i]) @@ -1276,9 +1271,6 @@ class UserList(_collections_abc.MutableSequence): inst.__dict__["data"] = self.__dict__["data"][:] return inst - def __sizeof__(self): - return _sys.getsizeof(self.data) - def append(self, item): self.data.append(item) @@ -1381,9 +1373,6 @@ class UserString(_collections_abc.Sequence): char = char.data return char in self.data - def __bool__(self): - return bool(self.data) - def __len__(self): return len(self.data) @@ -1413,9 +1402,6 @@ class UserString(_collections_abc.Sequence): def __rmod__(self, template): return self.__class__(str(template) % self) - def __sizeof__(self): - return _sys.getsizeof(self.data) - # the following methods are defined in alphabetical order: def capitalize(self): return self.__class__(self.data.capitalize()) @@ -1571,4 +1557,4 @@ class UserString(_collections_abc.Sequence): return self.__class__(self.data.upper()) def zfill(self, width): - return self.__class__(self.data.zfill(width)) \ No newline at end of file + return self.__class__(self.data.zfill(width)) diff --git a/Lib/collections/abc.py b/Lib/collections/abc.py index 891600d16..86ca8b8a8 100644 --- a/Lib/collections/abc.py +++ b/Lib/collections/abc.py @@ -1,2 +1,3 @@ from _collections_abc import * from _collections_abc import __all__ +from _collections_abc import _CallableGenericAlias diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index abfcb1514..c98ee55b1 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -7,6 +7,7 @@ import inspect import operator import pickle from random import choice, randrange +from itertools import product, chain, combinations import string import sys from test import support @@ -248,6 +249,10 @@ class TestChainMap(unittest.TestCase): for k, v in dict(a=1, B=20, C=30, z=100).items(): # check get self.assertEqual(d.get(k, 100), v) + c = ChainMap({'a': 1, 'b': 2}) + d = c.new_child(b=20, c=30) + self.assertEqual(d.maps, [{'b': 20, 'c': 30}, {'a': 1, 'b': 2}]) + def test_union_operators(self): cm1 = ChainMap(dict(a=1, b=2), dict(c=3, d=4)) cm2 = ChainMap(dict(a=10, e=5), dict(b=20, d=4)) @@ -663,8 +668,7 @@ class TestNamedTuple(unittest.TestCase): a.w = 5 self.assertEqual(a.__dict__, {'w': 5}) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @support.cpython_only def test_field_descriptor(self): Point = namedtuple('Point', 'x y') p = Point(11, 22) @@ -683,9 +687,14 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(np.y, 2) def test_new_builtins_issue_43102(self): - self.assertEqual( - namedtuple('C', ()).__new__.__globals__['__builtins__'], - {}) + obj = namedtuple('C', ()) + new_func = obj.__new__ + self.assertEqual(new_func.__globals__['__builtins__'], {}) + self.assertEqual(new_func.__builtins__, {}) + + def test_match_args(self): + Point = namedtuple('Point', 'x y') + self.assertEqual(Point.__match_args__, ('x', 'y')) ################################################################################ @@ -1516,7 +1525,7 @@ class TestCollectionABCs(ABCTestCase): items = [5,43,2,1] s = MySet(items) r = s.pop() - self.assertEquals(len(s), len(items) - 1) + self.assertEqual(len(s), len(items) - 1) self.assertNotIn(r, s) self.assertIn(r, items) @@ -1566,10 +1575,6 @@ class TestCollectionABCs(ABCTestCase): def test_issue26915(self): # Container membership test should check identity first - class CustomEqualObject: - def __eq__(self, other): - return False - class CustomSequence(Sequence): def __init__(self, seq): self._seq = seq @@ -1579,7 +1584,7 @@ class TestCollectionABCs(ABCTestCase): return len(self._seq) nan = float('nan') - obj = CustomEqualObject + obj = support.NEVER_EQ seq = CustomSequence([nan, obj, nan]) containers = [ seq, @@ -1599,7 +1604,7 @@ class TestCollectionABCs(ABCTestCase): self.assertSetEqual(set(s1), set(s2)) def test_Set_from_iterable(self): - """Verify _from_iterable overriden to an instance method works.""" + """Verify _from_iterable overridden to an instance method works.""" class SetUsingInstanceFromIterable(MutableSet): def __init__(self, values, created_by): if not created_by: @@ -1994,6 +1999,12 @@ class TestCollectionABCs(ABCTestCase): self.assertEqual(len(mss), len(mss2)) self.assertEqual(list(mss), list(mss2)) + def test_illegal_patma_flags(self): + with self.assertRaises(TypeError): + class Both(Collection): + __abc_tpflags__ = (Sequence.__flags__ | Mapping.__flags__) + + ################################################################################ ### Counter @@ -2087,6 +2098,10 @@ class TestCounter(unittest.TestCase): self.assertRaises(TypeError, Counter, (), ()) self.assertRaises(TypeError, Counter.__init__) + def test_total(self): + c = Counter(a=10, b=5, c=0) + self.assertEqual(c.total(), 15) + def test_order_preservation(self): # Input order dictates items() order self.assertEqual(list(Counter('abracadabra').items()), @@ -2240,31 +2255,6 @@ class TestCounter(unittest.TestCase): set_result = setop(set(p.elements()), set(q.elements())) self.assertEqual(counter_result, dict.fromkeys(set_result, 1)) - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_subset_superset_not_implemented(self): - # Verify that multiset comparison operations are not implemented. - - # These operations were intentionally omitted because multiset - # comparison semantics conflict with existing dict equality semantics. - - # For multisets, we would expect that if p<=q and p>=q are both true, - # then p==q. However, dict equality semantics require that p!=q when - # one of sets contains an element with a zero count and the other - # doesn't. - - p = Counter(a=1, b=0) - q = Counter(a=1, c=0) - self.assertNotEqual(p, q) - with self.assertRaises(TypeError): - p < q - with self.assertRaises(TypeError): - p <= q - with self.assertRaises(TypeError): - p > q - with self.assertRaises(TypeError): - p >= q - def test_inplace_operations(self): elements = 'abcd' for i in range(1000): @@ -2339,23 +2329,61 @@ class TestCounter(unittest.TestCase): self.assertTrue(c.called) self.assertEqual(dict(c), {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r':2 }) + def test_multiset_operations_equivalent_to_set_operations(self): + # When the multiplicities are all zero or one, multiset operations + # are guaranteed to be equivalent to the corresponding operations + # for regular sets. + s = list(product(('a', 'b', 'c'), range(2))) + powerset = chain.from_iterable(combinations(s, r) for r in range(len(s)+1)) + counters = [Counter(dict(groups)) for groups in powerset] + for cp, cq in product(counters, repeat=2): + sp = set(cp.elements()) + sq = set(cq.elements()) + self.assertEqual(set(cp + cq), sp | sq) + self.assertEqual(set(cp - cq), sp - sq) + self.assertEqual(set(cp | cq), sp | sq) + self.assertEqual(set(cp & cq), sp & sq) + self.assertEqual(cp == cq, sp == sq) + self.assertEqual(cp != cq, sp != sq) + self.assertEqual(cp <= cq, sp <= sq) + self.assertEqual(cp >= cq, sp >= sq) + self.assertEqual(cp < cq, sp < sq) + self.assertEqual(cp > cq, sp > sq) + + def test_eq(self): + self.assertEqual(Counter(a=3, b=2, c=0), Counter('ababa')) + self.assertNotEqual(Counter(a=3, b=2), Counter('babab')) + + def test_le(self): + self.assertTrue(Counter(a=3, b=2, c=0) <= Counter('ababa')) + self.assertFalse(Counter(a=3, b=2) <= Counter('babab')) + + def test_lt(self): + self.assertTrue(Counter(a=3, b=1, c=0) < Counter('ababa')) + self.assertFalse(Counter(a=3, b=2, c=0) < Counter('ababa')) + + def test_ge(self): + self.assertTrue(Counter(a=2, b=1, c=0) >= Counter('aab')) + self.assertFalse(Counter(a=3, b=2, c=0) >= Counter('aabd')) + + def test_gt(self): + self.assertTrue(Counter(a=3, b=2, c=0) > Counter('aab')) + self.assertFalse(Counter(a=2, b=1, c=0) > Counter('aab')) + ################################################################################ ### Run tests ################################################################################ def test_main(verbose=None): - # TODO: RUSTPYTHON - # NamedTupleDocs = doctest.DocTestSuite(module=collections) - # test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs, - test_classes = [TestNamedTuple, TestOneTrickPonyABCs, + NamedTupleDocs = doctest.DocTestSuite(module=collections) + test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs, TestCollectionABCs, TestCounter, TestChainMap, TestUserObjects, ] support.run_unittest(*test_classes) - # TODO: RUSTPYTHON - # support.run_doctest(collections, verbose) + support.run_doctest(collections, verbose) if __name__ == "__main__": - test_main(verbose=True) \ No newline at end of file + test_main(verbose=True) diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index b9f1fb9f2..68fc44978 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -72,27 +72,6 @@ class TestDefaultDict(unittest.TestCase): d3[13] self.assertEqual(repr(d3), "defaultdict(%s, {13: 43})" % repr(foo)) - def test_print(self): - d1 = defaultdict() - def foo(): return 42 - d2 = defaultdict(foo, {1: 2}) - # NOTE: We can't use tempfile.[Named]TemporaryFile since this - # code must exercise the tp_print C code, which only gets - # invoked for *real* files. - tfn = tempfile.mktemp() - try: - f = open(tfn, "w+") - try: - print(d1, file=f) - print(d2, file=f) - f.seek(0) - self.assertEqual(f.readline(), repr(d1) + "\n") - self.assertEqual(f.readline(), repr(d2) + "\n") - finally: - f.close() - finally: - os.remove(tfn) - def test_copy(self): d1 = defaultdict() d2 = d1.copy() @@ -160,18 +139,6 @@ class TestDefaultDict(unittest.TestCase): r"sub\(, \{\}\)") - # NOTE: printing a subclass of a builtin type does not call its - # tp_print slot. So this part is essentially the same test as above. - tfn = tempfile.mktemp() - try: - f = open(tfn, "w+") - try: - print(d, file=f) - finally: - f.close() - finally: - os.remove(tfn) - def test_callable_arg(self): self.assertRaises(TypeError, defaultdict, {}) @@ -183,5 +150,43 @@ class TestDefaultDict(unittest.TestCase): o = pickle.loads(s) self.assertEqual(d, o) + def test_union(self): + i = defaultdict(int, {1: 1, 2: 2}) + s = defaultdict(str, {0: "zero", 1: "one"}) + + i_s = i | s + self.assertIs(i_s.default_factory, int) + self.assertDictEqual(i_s, {1: "one", 2: 2, 0: "zero"}) + self.assertEqual(list(i_s), [1, 2, 0]) + + s_i = s | i + self.assertIs(s_i.default_factory, str) + self.assertDictEqual(s_i, {0: "zero", 1: 1, 2: 2}) + self.assertEqual(list(s_i), [0, 1, 2]) + + i_ds = i | dict(s) + self.assertIs(i_ds.default_factory, int) + self.assertDictEqual(i_ds, {1: "one", 2: 2, 0: "zero"}) + self.assertEqual(list(i_ds), [1, 2, 0]) + + ds_i = dict(s) | i + self.assertIs(ds_i.default_factory, int) + self.assertDictEqual(ds_i, {0: "zero", 1: 1, 2: 2}) + self.assertEqual(list(ds_i), [0, 1, 2]) + + with self.assertRaises(TypeError): + i | list(s.items()) + with self.assertRaises(TypeError): + list(s.items()) | i + + # We inherit a fine |= from dict, so just a few sanity checks here: + i |= list(s.items()) + self.assertIs(i.default_factory, int) + self.assertDictEqual(i, {1: "one", 2: 2, 0: "zero"}) + self.assertEqual(list(i), [1, 2, 0]) + + with self.assertRaises(TypeError): + i |= None + if __name__ == "__main__": unittest.main() From 7580e113205eeb87b9de9388331903168f048128 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 02:28:42 +0900 Subject: [PATCH 14/26] adjust failing markers for test_{collections,defaultdict} --- Lib/selectors.py | 292 ++++++++++++++++++----------------- Lib/test/test_collections.py | 10 ++ Lib/test/test_defaultdict.py | 2 + Lib/test/test_typing.py | 6 - 4 files changed, 162 insertions(+), 148 deletions(-) diff --git a/Lib/selectors.py b/Lib/selectors.py index 89680a20a..bb15a1cb1 100644 --- a/Lib/selectors.py +++ b/Lib/selectors.py @@ -6,7 +6,8 @@ This module allows high-level and efficient I/O multiplexing, built upon the from abc import ABCMeta, abstractmethod -from collections import namedtuple, Mapping +from collections import namedtuple +from collections.abc import Mapping import math import select import sys @@ -56,6 +57,7 @@ if sys.version_info >= (3, 5): SelectorKey.data.__doc__ = ('''Optional opaque data associated to this file object. For example, this could be used to store a per-client session ID.''') + class _SelectorMapping(Mapping): """Mapping of file objects to selector keys.""" @@ -251,7 +253,6 @@ class _BaseSelectorImpl(BaseSelector): return key def modify(self, fileobj, events, data=None): - # TODO: Subclasses can probably optimize this even further. try: key = self._fd_to_key[self._fileobj_lookup(fileobj)] except KeyError: @@ -338,92 +339,115 @@ class SelectSelector(_BaseSelectorImpl): return ready +class _PollLikeSelector(_BaseSelectorImpl): + """Base class shared between poll, epoll and devpoll selectors.""" + _selector_cls = None + _EVENT_READ = None + _EVENT_WRITE = None + + def __init__(self): + super().__init__() + self._selector = self._selector_cls() + + def register(self, fileobj, events, data=None): + key = super().register(fileobj, events, data) + poller_events = 0 + if events & EVENT_READ: + poller_events |= self._EVENT_READ + if events & EVENT_WRITE: + poller_events |= self._EVENT_WRITE + try: + self._selector.register(key.fd, poller_events) + except: + super().unregister(fileobj) + raise + return key + + def unregister(self, fileobj): + key = super().unregister(fileobj) + try: + self._selector.unregister(key.fd) + except OSError: + # This can happen if the FD was closed since it + # was registered. + pass + return key + + def modify(self, fileobj, events, data=None): + try: + key = self._fd_to_key[self._fileobj_lookup(fileobj)] + except KeyError: + raise KeyError(f"{fileobj!r} is not registered") from None + + changed = False + if events != key.events: + selector_events = 0 + if events & EVENT_READ: + selector_events |= self._EVENT_READ + if events & EVENT_WRITE: + selector_events |= self._EVENT_WRITE + try: + self._selector.modify(key.fd, selector_events) + except: + super().unregister(fileobj) + raise + changed = True + if data != key.data: + changed = True + + if changed: + key = key._replace(events=events, data=data) + self._fd_to_key[key.fd] = key + return key + + def select(self, timeout=None): + # This is shared between poll() and epoll(). + # epoll() has a different signature and handling of timeout parameter. + if timeout is None: + timeout = None + elif timeout <= 0: + timeout = 0 + else: + # poll() has a resolution of 1 millisecond, round away from + # zero to wait *at least* timeout seconds. + timeout = math.ceil(timeout * 1e3) + ready = [] + try: + fd_event_list = self._selector.poll(timeout) + except InterruptedError: + return ready + for fd, event in fd_event_list: + events = 0 + if event & ~self._EVENT_READ: + events |= EVENT_WRITE + if event & ~self._EVENT_WRITE: + events |= EVENT_READ + + key = self._key_from_fd(fd) + if key: + ready.append((key, events & key.events)) + return ready + + if hasattr(select, 'poll'): - class PollSelector(_BaseSelectorImpl): + class PollSelector(_PollLikeSelector): """Poll-based selector.""" - - def __init__(self): - super().__init__() - self._poll = select.poll() - - def register(self, fileobj, events, data=None): - key = super().register(fileobj, events, data) - poll_events = 0 - if events & EVENT_READ: - poll_events |= select.POLLIN - if events & EVENT_WRITE: - poll_events |= select.POLLOUT - self._poll.register(key.fd, poll_events) - return key - - def unregister(self, fileobj): - key = super().unregister(fileobj) - self._poll.unregister(key.fd) - return key - - def select(self, timeout=None): - if timeout is None: - timeout = None - elif timeout <= 0: - timeout = 0 - else: - # poll() has a resolution of 1 millisecond, round away from - # zero to wait *at least* timeout seconds. - timeout = math.ceil(timeout * 1e3) - ready = [] - try: - fd_event_list = self._poll.poll(timeout) - except InterruptedError: - return ready - for fd, event in fd_event_list: - events = 0 - if event & ~select.POLLIN: - events |= EVENT_WRITE - if event & ~select.POLLOUT: - events |= EVENT_READ - - key = self._key_from_fd(fd) - if key: - ready.append((key, events & key.events)) - return ready + _selector_cls = select.poll + _EVENT_READ = select.POLLIN + _EVENT_WRITE = select.POLLOUT if hasattr(select, 'epoll'): - class EpollSelector(_BaseSelectorImpl): + class EpollSelector(_PollLikeSelector): """Epoll-based selector.""" - - def __init__(self): - super().__init__() - self._epoll = select.epoll() + _selector_cls = select.epoll + _EVENT_READ = select.EPOLLIN + _EVENT_WRITE = select.EPOLLOUT def fileno(self): - return self._epoll.fileno() - - def register(self, fileobj, events, data=None): - key = super().register(fileobj, events, data) - epoll_events = 0 - if events & EVENT_READ: - epoll_events |= select.EPOLLIN - if events & EVENT_WRITE: - epoll_events |= select.EPOLLOUT - try: - self._epoll.register(key.fd, epoll_events) - except BaseException: - super().unregister(fileobj) - raise - return key - - def unregister(self, fileobj): - key = super().unregister(fileobj) - try: - self._epoll.unregister(key.fd) - except OSError: - # This can happen if the FD was closed since it - # was registered. - pass - return key + return self._selector.fileno() def select(self, timeout=None): if timeout is None: @@ -442,7 +466,7 @@ if hasattr(select, 'epoll'): ready = [] try: - fd_event_list = self._epoll.poll(timeout, max_ev) + fd_event_list = self._selector.poll(timeout, max_ev) except InterruptedError: return ready for fd, event in fd_event_list: @@ -458,65 +482,23 @@ if hasattr(select, 'epoll'): return ready def close(self): - self._epoll.close() + self._selector.close() super().close() if hasattr(select, 'devpoll'): - class DevpollSelector(_BaseSelectorImpl): + class DevpollSelector(_PollLikeSelector): """Solaris /dev/poll selector.""" - - def __init__(self): - super().__init__() - self._devpoll = select.devpoll() + _selector_cls = select.devpoll + _EVENT_READ = select.POLLIN + _EVENT_WRITE = select.POLLOUT def fileno(self): - return self._devpoll.fileno() - - def register(self, fileobj, events, data=None): - key = super().register(fileobj, events, data) - poll_events = 0 - if events & EVENT_READ: - poll_events |= select.POLLIN - if events & EVENT_WRITE: - poll_events |= select.POLLOUT - self._devpoll.register(key.fd, poll_events) - return key - - def unregister(self, fileobj): - key = super().unregister(fileobj) - self._devpoll.unregister(key.fd) - return key - - def select(self, timeout=None): - if timeout is None: - timeout = None - elif timeout <= 0: - timeout = 0 - else: - # devpoll() has a resolution of 1 millisecond, round away from - # zero to wait *at least* timeout seconds. - timeout = math.ceil(timeout * 1e3) - ready = [] - try: - fd_event_list = self._devpoll.poll(timeout) - except InterruptedError: - return ready - for fd, event in fd_event_list: - events = 0 - if event & ~select.POLLIN: - events |= EVENT_WRITE - if event & ~select.POLLOUT: - events |= EVENT_READ - - key = self._key_from_fd(fd) - if key: - ready.append((key, events & key.events)) - return ready + return self._selector.fileno() def close(self): - self._devpoll.close() + self._selector.close() super().close() @@ -527,10 +509,10 @@ if hasattr(select, 'kqueue'): def __init__(self): super().__init__() - self._kqueue = select.kqueue() + self._selector = select.kqueue() def fileno(self): - return self._kqueue.fileno() + return self._selector.fileno() def register(self, fileobj, events, data=None): key = super().register(fileobj, events, data) @@ -538,12 +520,12 @@ if hasattr(select, 'kqueue'): if events & EVENT_READ: kev = select.kevent(key.fd, select.KQ_FILTER_READ, select.KQ_EV_ADD) - self._kqueue.control([kev], 0, 0) + self._selector.control([kev], 0, 0) if events & EVENT_WRITE: kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, select.KQ_EV_ADD) - self._kqueue.control([kev], 0, 0) - except BaseException: + self._selector.control([kev], 0, 0) + except: super().unregister(fileobj) raise return key @@ -554,7 +536,7 @@ if hasattr(select, 'kqueue'): kev = select.kevent(key.fd, select.KQ_FILTER_READ, select.KQ_EV_DELETE) try: - self._kqueue.control([kev], 0, 0) + self._selector.control([kev], 0, 0) except OSError: # This can happen if the FD was closed since it # was registered. @@ -563,7 +545,7 @@ if hasattr(select, 'kqueue'): kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, select.KQ_EV_DELETE) try: - self._kqueue.control([kev], 0, 0) + self._selector.control([kev], 0, 0) except OSError: # See comment above. pass @@ -571,10 +553,13 @@ if hasattr(select, 'kqueue'): def select(self, timeout=None): timeout = None if timeout is None else max(timeout, 0) - max_ev = len(self._fd_to_key) + # If max_ev is 0, kqueue will ignore the timeout. For consistent + # behavior with the other selector classes, we prevent that here + # (using max). See https://bugs.python.org/issue29255 + max_ev = max(len(self._fd_to_key), 1) ready = [] try: - kev_list = self._kqueue.control(None, max_ev, timeout) + kev_list = self._selector.control(None, max_ev, timeout) except InterruptedError: return ready for kev in kev_list: @@ -592,20 +577,43 @@ if hasattr(select, 'kqueue'): return ready def close(self): - self._kqueue.close() + self._selector.close() super().close() +def _can_use(method): + """Check if we can use the selector depending upon the + operating system. """ + # Implementation based upon https://github.com/sethmlarson/selectors2/blob/master/selectors2.py + selector = getattr(select, method, None) + if selector is None: + # select module does not implement method + return False + # check if the OS and Kernel actually support the method. Call may fail with + # OSError: [Errno 38] Function not implemented + try: + selector_obj = selector() + if method == 'poll': + # check that poll actually works + selector_obj.poll(0) + else: + # close epoll, kqueue, and devpoll fd + selector_obj.close() + return True + except OSError: + return False + + # Choose the best implementation, roughly: # epoll|kqueue|devpoll > poll > select. # select() also can't accept a FD > FD_SETSIZE (usually around 1024) -if 'KqueueSelector' in globals(): +if _can_use('kqueue'): DefaultSelector = KqueueSelector -elif 'EpollSelector' in globals(): +elif _can_use('epoll'): DefaultSelector = EpollSelector -elif 'DevpollSelector' in globals(): +elif _can_use('devpoll'): DefaultSelector = DevpollSelector -elif 'PollSelector' in globals(): +elif _can_use('poll'): DefaultSelector = PollSelector else: DefaultSelector = SelectSelector diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index c98ee55b1..4a080d87d 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -52,12 +52,18 @@ class TestUserObjects(unittest.TestCase): self.assertEqual(obj.data, obj_copy.data) self.assertIs(obj.test, obj_copy.test) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_str_protocol(self): self._superset_test(UserString, str) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_list_protocol(self): self._superset_test(UserList, list) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_dict_protocol(self): self._superset_test(UserDict, dict) @@ -686,6 +692,8 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(np.x, 1) self.assertEqual(np.y, 2) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_new_builtins_issue_43102(self): obj = namedtuple('C', ()) new_func = obj.__new__ @@ -1999,6 +2007,8 @@ class TestCollectionABCs(ABCTestCase): self.assertEqual(len(mss), len(mss2)) self.assertEqual(list(mss), list(mss2)) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_illegal_patma_flags(self): with self.assertRaises(TypeError): class Both(Collection): diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index 68fc44978..65376a37e 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -150,6 +150,8 @@ class TestDefaultDict(unittest.TestCase): o = pickle.loads(s) self.assertEqual(d, o) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_union(self): i = defaultdict(int, {1: 1, 2: 2}) s = defaultdict(str, {0: "zero", 1: "one"}) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 86825bce2..bebf40d21 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -691,11 +691,6 @@ class TypingCallableTests(BaseCallableTests, BaseTestCase): class CollectionsCallableTests(BaseCallableTests, BaseTestCase): Callable = collections.abc.Callable - # TODO: RUSTPYTHON, pickle.PicklingError: Can't pickle : it's not found as collections.abc._CallableGenericAlias - @unittest.expectedFailure - def test_pickle(self): # TODO: RUSTPYTHON, remove when this passes - super().test_pickle() # TODO: RUSTPYTHON, remove when this passes - # TODO: RUSTPYTHON, AssertionError: 'collections.abc.Callable[__main__.ParamSpec, typing.TypeVar]' != 'collections.abc.Callable[~P, ~T]' @unittest.expectedFailure def test_paramspec(self): # TODO: RUSTPYTHON, remove when this passes @@ -5423,7 +5418,6 @@ class AllTests(BaseTestCase): self.assertIn('SupportsComplex', a) def test_all_exported_names(self): - actual_all = set(typing.__all__) computed_all = { k for k, v in vars(typing).items() From c8ae1806a41e235fec7455f252b4ff1d026fc70f Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:37:11 +0900 Subject: [PATCH 15/26] Update test_deque from CPython 3.10.5 --- Lib/test/test_deque.py | 58 ++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 1d5b4b01c..0cf3a3663 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -1,7 +1,6 @@ from collections import deque import unittest from test import support, seq_tests -from test.support import os_helper import gc import weakref import copy @@ -67,28 +66,9 @@ class TestBasic(unittest.TestCase): self.assertEqual(list(d), [7, 8, 9]) d = deque(range(200), maxlen=10) d.append(d) - os_helper.unlink(os_helper.TESTFN) - fo = open(os_helper.TESTFN, "w") - try: - fo.write(str(d)) - fo.close() - fo = open(os_helper.TESTFN, "r") - self.assertEqual(fo.read(), repr(d)) - finally: - fo.close() - os_helper.unlink(os_helper.TESTFN) - + self.assertEqual(repr(d)[-30:], ', 198, 199, [...]], maxlen=10)') d = deque(range(10), maxlen=None) self.assertEqual(repr(d), 'deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])') - fo = open(os_helper.TESTFN, "w") - try: - fo.write(str(d)) - fo.close() - fo = open(os_helper.TESTFN, "r") - self.assertEqual(fo.read(), repr(d)) - finally: - fo.close() - os_helper.unlink(os_helper.TESTFN) def test_maxlen_zero(self): it = iter(range(100)) @@ -149,7 +129,8 @@ class TestBasic(unittest.TestCase): self.assertEqual(d.count(None), 16) def test_comparisons(self): - d = deque('xabc'); d.popleft() + d = deque('xabc') + d.popleft() for e in [d, deque('abc'), deque('ab'), deque(), list(d)]: self.assertEqual(d==e, type(d)==type(e) and list(d)==list(e)) self.assertEqual(d!=e, not(type(d)==type(e) and list(d)==list(e))) @@ -184,6 +165,18 @@ class TestBasic(unittest.TestCase): with self.assertRaises(RuntimeError): n in d + def test_contains_count_stop_crashes(self): + class A: + def __eq__(self, other): + d.clear() + return NotImplemented + d = deque([A(), A()]) + with self.assertRaises(RuntimeError): + _ = 3 in d + d = deque([A(), A()]) + with self.assertRaises(RuntimeError): + _ = d.count(3) + def test_extend(self): d = deque('a') self.assertRaises(TypeError, d.extend, 1) @@ -534,25 +527,11 @@ class TestBasic(unittest.TestCase): e = eval(repr(d)) self.assertEqual(list(d), list(e)) d.append(d) - self.assertIn('...', repr(d)) - - def test_print(self): - d = deque(range(200)) - d.append(d) - try: - os_helper.unlink(os_helper.TESTFN) - fo = open(os_helper.TESTFN, "w") - print(d, file=fo, end='') - fo.close() - fo = open(os_helper.TESTFN, "r") - self.assertEqual(fo.read(), repr(d)) - finally: - fo.close() - os_helper.unlink(os_helper.TESTFN) + self.assertEqual(repr(d)[-20:], '7, 198, 199, [...]])') def test_init(self): - self.assertRaises(TypeError, deque, 'abc', 2, 3); - self.assertRaises(TypeError, deque, 1); + self.assertRaises(TypeError, deque, 'abc', 2, 3) + self.assertRaises(TypeError, deque, 1) def test_hash(self): self.assertRaises(TypeError, hash, deque('abc')) @@ -894,6 +873,7 @@ class TestSubclass(unittest.TestCase): p = weakref.proxy(d) self.assertEqual(str(p), str(d)) d = None + support.gc_collect() # For PyPy or other GCs. self.assertRaises(ReferenceError, str, p) def test_strange_subclass(self): From 45e6fb14c2ff5b8d4ec8d957aca950949ec50a11 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 02:39:56 +0900 Subject: [PATCH 16/26] adjust failing markers for test_deque --- Lib/test/test_deque.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 0cf3a3663..34729e734 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -929,6 +929,21 @@ class TestSequence(seq_tests.CommonTest): # For now, bypass tests that require slicing self.skipTest("Exhausted deque iterator doesn't free a deque") + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_contains_fake(self): # XXX: RUSTPYTHON; the method also need to be removed when done + super().test_contains_fake() + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_count(self): # XXX: RUSTPYTHON; the method also need to be removed when done + super().test_count() + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_index(self): # XXX: RUSTPYTHON; the method also need to be removed when done + super().test_index() + #============================================================================== libreftest = """ From 8a6fc058934f099d5bb84351f981c933971eeccb Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:30:05 +0900 Subject: [PATCH 17/26] Update test_contains from CPython 3.10.5 --- Lib/test/test_contains.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_contains.py b/Lib/test/test_contains.py index d91036e1c..c53331157 100644 --- a/Lib/test/test_contains.py +++ b/Lib/test/test_contains.py @@ -1,5 +1,6 @@ from collections import deque import unittest +from test.support import NEVER_EQ class base_set: @@ -70,13 +71,7 @@ class TestContains(unittest.TestCase): # containment and equality tests involving elements that are # not necessarily equal to themselves - class MyNonReflexive(object): - def __eq__(self, other): - return False - def __hash__(self): - return 28 - - values = float('nan'), 1, None, 'abc', MyNonReflexive() + values = float('nan'), 1, None, 'abc', NEVER_EQ constructors = list, tuple, dict.fromkeys, set, frozenset, deque for constructor in constructors: container = constructor(values) From 36f5134bca66633ca99c133bc82677a7246edb31 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:33:54 +0900 Subject: [PATCH 18/26] Update test_bytes from CPython 3.10.5 --- Lib/test/test_bytes.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index bdc9168be..727b7645e 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -16,9 +16,11 @@ import textwrap import unittest import test.support +from test.support import import_helper +from test.support import warnings_helper import test.string_tests import test.list_tests -from test.support import bigaddrspacetest, MAX_Py_ssize_t, import_helper, warnings_helper +from test.support import bigaddrspacetest, MAX_Py_ssize_t from test.support.script_helper import assert_python_failure @@ -1177,6 +1179,28 @@ class BytesTest(BaseBytesTest, unittest.TestCase): self.assertEqual(bytes(ba), b'ab') self.assertRaises(TypeError, bytes, bb) + def test_repeat_id_preserving(self): + a = b'123abc1@' + b = b'456zyx-+' + self.assertEqual(id(a), id(a)) + self.assertNotEqual(id(a), id(b)) + self.assertNotEqual(id(a), id(a * -4)) + self.assertNotEqual(id(a), id(a * 0)) + self.assertEqual(id(a), id(a * 1)) + self.assertEqual(id(a), id(1 * a)) + self.assertNotEqual(id(a), id(a * 2)) + + class SubBytes(bytes): + pass + + s = SubBytes(b'qwerty()') + self.assertEqual(id(s), id(s)) + self.assertNotEqual(id(s), id(s * -4)) + self.assertNotEqual(id(s), id(s * 0)) + self.assertNotEqual(id(s), id(s * 1)) + self.assertNotEqual(id(s), id(1 * s)) + self.assertNotEqual(id(s), id(s * 2)) + class ByteArrayTest(BaseBytesTest, unittest.TestCase): type2test = bytearray @@ -1799,7 +1823,7 @@ class AssortedBytesTest(unittest.TestCase): "BytesWarning is needed for this test: use -bb option") def test_compare(self): def bytes_warning(): - return test.warnings_helper.check_warningswarnings(('', BytesWarning)) + return warnings_helper.check_warnings(('', BytesWarning)) with bytes_warning(): b'' == '' with bytes_warning(): From c7a5b960be1e86d6204997a2c57d73da70f3afa2 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:45:44 +0900 Subject: [PATCH 19/26] Update test_memoryview from CPython 3.10.5 --- Lib/test/test_memoryview.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index e128b3190..98d2a7c9f 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -6,15 +6,16 @@ import unittest import test.support -from test.support import import_helper import sys -# import gc # XXX: RUSTPYTHON +import gc import weakref import array import io import copy import pickle +from test.support import import_helper + class AbstractMemoryTests: source_bytes = b"abcdef" @@ -418,9 +419,11 @@ class BaseArrayMemoryTests(AbstractMemoryTests): itemsize = array.array('i').itemsize format = 'i' + @unittest.skip('XXX test should be adapted for non-byte buffers') def test_getbuffer(self): pass + @unittest.skip('XXX NotImplementedError: tolist() only supports byte views') def test_tolist(self): pass From b4c0a7611c556e2e202adf76adbaf407c9118ac2 Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 02:47:09 +0900 Subject: [PATCH 20/26] Update queue/test_queue from CPython 3.10.5 --- Lib/queue.py | 2 +- Lib/test/test_queue.py | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Lib/queue.py b/Lib/queue.py index 10dbcbc18..55f500884 100644 --- a/Lib/queue.py +++ b/Lib/queue.py @@ -298,7 +298,7 @@ class _PySimpleQueue: def put_nowait(self, item): '''Put an item into the queue without blocking. - This is exactly equivalent to `put(item)` and is only provided + This is exactly equivalent to `put(item, block=False)` and is only provided for compatibility with the Queue class. ''' return self.put(item, block=False) diff --git a/Lib/test/test_queue.py b/Lib/test/test_queue.py index 1a1f3a963..cfa6003a8 100644 --- a/Lib/test/test_queue.py +++ b/Lib/test/test_queue.py @@ -6,7 +6,7 @@ import threading import time import unittest import weakref -from test import support +from test.support import gc_collect from test.support import import_helper from test.support import threading_helper @@ -420,11 +420,12 @@ class BaseSimpleQueueTest: def setUp(self): self.q = self.type2test() - def feed(self, q, seq, rnd): + def feed(self, q, seq, rnd, sentinel): while True: try: val = seq.pop() except IndexError: + q.put(sentinel) return q.put(val) if rnd.random() > 0.5: @@ -463,11 +464,10 @@ class BaseSimpleQueueTest: return results.append(val) - def run_threads(self, n_feeders, n_consumers, q, inputs, - feed_func, consume_func): + def run_threads(self, n_threads, q, inputs, feed_func, consume_func): results = [] sentinel = None - seq = inputs + [sentinel] * n_consumers + seq = inputs.copy() seq.reverse() rnd = random.Random(42) @@ -481,11 +481,11 @@ class BaseSimpleQueueTest: return wrapper feeders = [threading.Thread(target=log_exceptions(feed_func), - args=(q, seq, rnd)) - for i in range(n_feeders)] + args=(q, seq, rnd, sentinel)) + for i in range(n_threads)] consumers = [threading.Thread(target=log_exceptions(consume_func), args=(q, results, sentinel)) - for i in range(n_consumers)] + for i in range(n_threads)] with threading_helper.start_threads(feeders + consumers): pass @@ -543,7 +543,7 @@ class BaseSimpleQueueTest: # Test a pair of concurrent put() and get() q = self.q inputs = list(range(100)) - results = self.run_threads(1, 1, q, inputs, self.feed, self.consume) + results = self.run_threads(1, q, inputs, self.feed, self.consume) # One producer, one consumer => results appended in well-defined order self.assertEqual(results, inputs) @@ -553,7 +553,7 @@ class BaseSimpleQueueTest: N = 50 q = self.q inputs = list(range(10000)) - results = self.run_threads(N, N, q, inputs, self.feed, self.consume) + results = self.run_threads(N, q, inputs, self.feed, self.consume) # Multiple consumers without synchronization append the # results in random order @@ -564,7 +564,7 @@ class BaseSimpleQueueTest: N = 50 q = self.q inputs = list(range(10000)) - results = self.run_threads(N, N, q, inputs, + results = self.run_threads(N, q, inputs, self.feed, self.consume_nonblock) self.assertEqual(sorted(results), inputs) @@ -574,7 +574,7 @@ class BaseSimpleQueueTest: N = 50 q = self.q inputs = list(range(1000)) - results = self.run_threads(N, N, q, inputs, + results = self.run_threads(N, q, inputs, self.feed, self.consume_timeout) self.assertEqual(sorted(results), inputs) @@ -591,6 +591,7 @@ class BaseSimpleQueueTest: q.put(C()) for i in range(N): wr = weakref.ref(q.get()) + gc_collect() # For PyPy or other GCs. self.assertIsNone(wr()) From ff7b96f91cabb3481bac75d936e0f092a4e4d94a Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 02:50:33 +0900 Subject: [PATCH 21/26] Update test_unicode* from CPython 3.10.5 --- Lib/test/test_unicode.py | 232 ++++++++++++++++++------ Lib/test/test_unicode_file_functions.py | 20 +- Lib/test/test_unicode_identifiers.py | 8 +- 3 files changed, 192 insertions(+), 68 deletions(-) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index bf7feb66f..8dd25fcca 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -11,11 +11,14 @@ import itertools import operator import struct import sys +import textwrap import unicodedata import unittest import warnings +from test.support import import_helper +from test.support import warnings_helper from test import support, string_tests -from test.support import import_helper, warnings_helper +from test.support.script_helper import assert_python_failure # Error handling (bad decoder return) def search_function(encoding): @@ -33,7 +36,6 @@ def search_function(encoding): return (encode2, decode2, None, None) else: return None -codecs.register(search_function) def duplicate_string(text): """ @@ -55,6 +57,10 @@ class UnicodeTest(string_tests.CommonTest, type2test = str + def setUp(self): + codecs.register(search_function) + self.addCleanup(codecs.unregister, search_function) + def checkequalnofix(self, result, object, methodname, *args): method = getattr(object, methodname) realresult = method(*args) @@ -505,6 +511,28 @@ class UnicodeTest(string_tests.CommonTest, text = 'abc def' self.assertIs(text.replace(pattern, pattern), text) + def test_repeat_id_preserving(self): + a = '123abc1@' + b = '456zyx-+' + self.assertEqual(id(a), id(a)) + self.assertNotEqual(id(a), id(b)) + self.assertNotEqual(id(a), id(a * -4)) + self.assertNotEqual(id(a), id(a * 0)) + self.assertEqual(id(a), id(a * 1)) + self.assertEqual(id(a), id(1 * a)) + self.assertNotEqual(id(a), id(a * 2)) + + class SubStr(str): + pass + + s = SubStr('qwerty()') + self.assertEqual(id(s), id(s)) + self.assertNotEqual(id(s), id(s * -4)) + self.assertNotEqual(id(s), id(s * 0)) + self.assertNotEqual(id(s), id(s * 1)) + self.assertNotEqual(id(s), id(1 * s)) + self.assertNotEqual(id(s), id(s * 2)) + def test_bytes_comparison(self): with warnings_helper.check_warnings(): warnings.simplefilter('ignore', BytesWarning) @@ -728,6 +756,16 @@ class UnicodeTest(string_tests.CommonTest, self.assertFalse("©".isidentifier()) self.assertFalse("0".isidentifier()) + @support.cpython_only + @support.requires_legacy_unicode_capi + def test_isidentifier_legacy(self): + import _testcapi + u = '𝖀𝖓𝖎𝖈𝖔𝖉𝖊' + self.assertTrue(u.isidentifier()) + with warnings_helper.check_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + self.assertTrue(_testcapi.unicode_legacy_string(u).isidentifier()) + # TODO: RUSTPYTHON @unittest.expectedFailure def test_isprintable(self): @@ -1103,6 +1141,12 @@ class UnicodeTest(string_tests.CommonTest, self.assertEqual('{0:^8s}'.format('result'), ' result ') self.assertEqual('{0:^9s}'.format('result'), ' result ') self.assertEqual('{0:^10s}'.format('result'), ' result ') + self.assertEqual('{0:8s}'.format('result'), 'result ') + self.assertEqual('{0:0s}'.format('result'), 'result') + self.assertEqual('{0:08s}'.format('result'), 'result00') + self.assertEqual('{0:<08s}'.format('result'), 'result00') + self.assertEqual('{0:>08s}'.format('result'), '00result') + self.assertEqual('{0:^08s}'.format('result'), '0result0') self.assertEqual('{0:10000}'.format('a'), 'a' + ' ' * 9999) self.assertEqual('{0:10000}'.format(''), ' ' * 10000) self.assertEqual('{0:10000000}'.format(''), ' ' * 10000000) @@ -1230,8 +1274,11 @@ class UnicodeTest(string_tests.CommonTest, 0, 1, 2, 3, 4, 5, 6, 7) # string format spec errors - self.assertRaises(ValueError, "{0:-s}".format, '') - self.assertRaises(ValueError, format, "", "-") + sign_msg = "Sign not allowed in string format specifier" + self.assertRaisesRegex(ValueError, sign_msg, "{0:-s}".format, '') + self.assertRaisesRegex(ValueError, sign_msg, format, "", "-") + space_msg = "Space not allowed in string format specifier" + self.assertRaisesRegex(ValueError, space_msg, "{: }".format, '') self.assertRaises(ValueError, "{0:=s}".format, '') # Alternate formatting is not supported @@ -1789,7 +1836,7 @@ class UnicodeTest(string_tests.CommonTest, # Issue #8271: during the decoding of an invalid UTF-8 byte sequence, # only the start byte and the continuation byte(s) are now considered # invalid, instead of the number of bytes specified by the start byte. - # See http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf (page 95, + # See https://www.unicode.org/versions/Unicode5.2.0/ch03.pdf (page 95, # table 3-8, Row 2) for more information about the algorithm used. FFFD = '\ufffd' sequences = [ @@ -2247,22 +2294,6 @@ class UnicodeTest(string_tests.CommonTest, self.assertEqual(("abc" "def" "ghi"), "abcdefghi") self.assertEqual(("abc" "def" "ghi"), "abcdefghi") - def test_printing(self): - class BitBucket: - def write(self, text): - pass - - out = BitBucket() - print('abc', file=out) - print('abc', 'def', file=out) - print('abc', 'def', file=out) - print('abc', 'def', file=out) - print('abc\n', file=out) - print('abc\n', end=' ', file=out) - print('abc\n', end=' ', file=out) - print('def\n', file=out) - print('def\n', file=out) - def test_ucs4(self): x = '\U00100000' y = x.encode("raw-unicode-escape").decode("raw-unicode-escape") @@ -2400,6 +2431,7 @@ class UnicodeTest(string_tests.CommonTest, self.assertEqual(len(args), 1) @support.cpython_only + @support.requires_legacy_unicode_capi def test_resize(self): from _testcapi import getargs_u for length in range(1, 100, 7): @@ -2407,12 +2439,14 @@ class UnicodeTest(string_tests.CommonTest, text = 'a' * length + 'b' # fill wstr internal field - abc = getargs_u(text) + with self.assertWarns(DeprecationWarning): + abc = getargs_u(text) self.assertEqual(abc, text) # resize text: wstr field must be cleared and then recomputed text += 'c' - abcdef = getargs_u(text) + with self.assertWarns(DeprecationWarning): + abcdef = getargs_u(text) self.assertNotEqual(abc, abcdef) self.assertEqual(abcdef, text) @@ -2496,6 +2530,66 @@ class UnicodeTest(string_tests.CommonTest, support.check_free_after_iterating(self, iter, str) support.check_free_after_iterating(self, reversed, str) + def test_check_encoding_errors(self): + # bpo-37388: str(bytes) and str.decode() must check encoding and errors + # arguments in dev mode + encodings = ('ascii', 'utf8', 'latin1') + invalid = 'Boom, Shaka Laka, Boom!' + code = textwrap.dedent(f''' + import sys + encodings = {encodings!r} + + for data in (b'', b'short string'): + try: + str(data, encoding={invalid!r}) + except LookupError: + pass + else: + sys.exit(21) + + try: + str(data, errors={invalid!r}) + except LookupError: + pass + else: + sys.exit(22) + + for encoding in encodings: + try: + str(data, encoding, errors={invalid!r}) + except LookupError: + pass + else: + sys.exit(22) + + for data in ('', 'short string'): + try: + data.encode(encoding={invalid!r}) + except LookupError: + pass + else: + sys.exit(23) + + try: + data.encode(errors={invalid!r}) + except LookupError: + pass + else: + sys.exit(24) + + for encoding in encodings: + try: + data.encode(encoding, errors={invalid!r}) + except LookupError: + pass + else: + sys.exit(24) + + sys.exit(10) + ''') + proc = assert_python_failure('-X', 'dev', '-c', code) + self.assertEqual(proc.rc, 10, proc) + class CAPITest(unittest.TestCase): @@ -2503,11 +2597,13 @@ class CAPITest(unittest.TestCase): def test_from_format(self): import_helper.import_module('ctypes') from ctypes import ( + c_char_p, pythonapi, py_object, sizeof, c_int, c_long, c_longlong, c_ssize_t, c_uint, c_ulong, c_ulonglong, c_size_t, c_void_p) name = "PyUnicode_FromFormat" _PyUnicode_FromFormat = getattr(pythonapi, name) + _PyUnicode_FromFormat.argtypes = (c_char_p,) _PyUnicode_FromFormat.restype = py_object def PyUnicode_FromFormat(format, *args): @@ -2807,15 +2903,43 @@ class CAPITest(unittest.TestCase): for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600', 'a\ud800b\udfffc', '\ud834\udd1e']: l = len(s) - self.assertEqual(unicode_asucs4(s, l, 1), s+'\0') - self.assertEqual(unicode_asucs4(s, l, 0), s+'\uffff') - self.assertEqual(unicode_asucs4(s, l+1, 1), s+'\0\uffff') - self.assertEqual(unicode_asucs4(s, l+1, 0), s+'\0\uffff') - self.assertRaises(SystemError, unicode_asucs4, s, l-1, 1) - self.assertRaises(SystemError, unicode_asucs4, s, l-2, 0) + self.assertEqual(unicode_asucs4(s, l, True), s+'\0') + self.assertEqual(unicode_asucs4(s, l, False), s+'\uffff') + self.assertEqual(unicode_asucs4(s, l+1, True), s+'\0\uffff') + self.assertEqual(unicode_asucs4(s, l+1, False), s+'\0\uffff') + self.assertRaises(SystemError, unicode_asucs4, s, l-1, True) + self.assertRaises(SystemError, unicode_asucs4, s, l-2, False) s = '\0'.join([s, s]) - self.assertEqual(unicode_asucs4(s, len(s), 1), s+'\0') - self.assertEqual(unicode_asucs4(s, len(s), 0), s+'\uffff') + self.assertEqual(unicode_asucs4(s, len(s), True), s+'\0') + self.assertEqual(unicode_asucs4(s, len(s), False), s+'\uffff') + + # Test PyUnicode_AsUTF8() + @support.cpython_only + def test_asutf8(self): + from _testcapi import unicode_asutf8 + + bmp = '\u0100' + bmp2 = '\uffff' + nonbmp = chr(0x10ffff) + + self.assertEqual(unicode_asutf8(bmp), b'\xc4\x80') + self.assertEqual(unicode_asutf8(bmp2), b'\xef\xbf\xbf') + self.assertEqual(unicode_asutf8(nonbmp), b'\xf4\x8f\xbf\xbf') + self.assertRaises(UnicodeEncodeError, unicode_asutf8, 'a\ud800b\udfffc') + + # Test PyUnicode_AsUTF8AndSize() + @support.cpython_only + def test_asutf8andsize(self): + from _testcapi import unicode_asutf8andsize + + bmp = '\u0100' + bmp2 = '\uffff' + nonbmp = chr(0x10ffff) + + self.assertEqual(unicode_asutf8andsize(bmp), (b'\xc4\x80', 2)) + self.assertEqual(unicode_asutf8andsize(bmp2), (b'\xef\xbf\xbf', 3)) + self.assertEqual(unicode_asutf8andsize(nonbmp), (b'\xf4\x8f\xbf\xbf', 4)) + self.assertRaises(UnicodeEncodeError, unicode_asutf8andsize, 'a\ud800b\udfffc') # Test PyUnicode_FindChar() @support.cpython_only @@ -2884,32 +3008,38 @@ class CAPITest(unittest.TestCase): self.assertRaises(SystemError, unicode_copycharacters, s, 0, b'', 0, 0) @support.cpython_only + @support.requires_legacy_unicode_capi def test_encode_decimal(self): from _testcapi import unicode_encodedecimal - self.assertEqual(unicode_encodedecimal('123'), - b'123') - self.assertEqual(unicode_encodedecimal('\u0663.\u0661\u0664'), - b'3.14') - self.assertEqual(unicode_encodedecimal("\N{EM SPACE}3.14\N{EN SPACE}"), - b' 3.14 ') - self.assertRaises(UnicodeEncodeError, - unicode_encodedecimal, "123\u20ac", "strict") - self.assertRaisesRegex( - ValueError, - "^'decimal' codec can't encode character", - unicode_encodedecimal, "123\u20ac", "replace") + with warnings_helper.check_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + self.assertEqual(unicode_encodedecimal('123'), + b'123') + self.assertEqual(unicode_encodedecimal('\u0663.\u0661\u0664'), + b'3.14') + self.assertEqual(unicode_encodedecimal( + "\N{EM SPACE}3.14\N{EN SPACE}"), b' 3.14 ') + self.assertRaises(UnicodeEncodeError, + unicode_encodedecimal, "123\u20ac", "strict") + self.assertRaisesRegex( + ValueError, + "^'decimal' codec can't encode character", + unicode_encodedecimal, "123\u20ac", "replace") @support.cpython_only + @support.requires_legacy_unicode_capi def test_transform_decimal(self): from _testcapi import unicode_transformdecimaltoascii as transform_decimal - self.assertEqual(transform_decimal('123'), - '123') - self.assertEqual(transform_decimal('\u0663.\u0661\u0664'), - '3.14') - self.assertEqual(transform_decimal("\N{EM SPACE}3.14\N{EN SPACE}"), - "\N{EM SPACE}3.14\N{EN SPACE}") - self.assertEqual(transform_decimal('123\u20ac'), - '123\u20ac') + with warnings_helper.check_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + self.assertEqual(transform_decimal('123'), + '123') + self.assertEqual(transform_decimal('\u0663.\u0661\u0664'), + '3.14') + self.assertEqual(transform_decimal("\N{EM SPACE}3.14\N{EN SPACE}"), + "\N{EM SPACE}3.14\N{EN SPACE}") + self.assertEqual(transform_decimal('123\u20ac'), + '123\u20ac') @support.cpython_only def test_pep393_utf8_caching_bug(self): diff --git a/Lib/test/test_unicode_file_functions.py b/Lib/test/test_unicode_file_functions.py index c4bb3e186..169aaaf14 100644 --- a/Lib/test/test_unicode_file_functions.py +++ b/Lib/test/test_unicode_file_functions.py @@ -5,9 +5,9 @@ import sys import unittest import warnings from unicodedata import normalize -from test import support from test.support import os_helper + filenames = [ '1_abc', '2_ascii', @@ -151,7 +151,8 @@ class UnicodeFileTests(unittest.TestCase): sf0 = set(self.files) with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) - f1 = os.listdir(os_helper.TESTFN.encode(sys.getfilesystemencoding())) + f1 = os.listdir(os_helper.TESTFN.encode( + sys.getfilesystemencoding())) f2 = os.listdir(os_helper.TESTFN) sf2 = set(os.path.join(os_helper.TESTFN, f) for f in f2) self.assertEqual(sf0, sf2, "%a != %a" % (sf0, sf2)) @@ -163,7 +164,8 @@ class UnicodeFileTests(unittest.TestCase): os.rename("tmp", name) def test_directory(self): - dirname = os.path.join(os_helper.TESTFN, 'Gr\xfc\xdf-\u66e8\u66e9\u66eb') + dirname = os.path.join(os_helper.TESTFN, + 'Gr\xfc\xdf-\u66e8\u66e9\u66eb') filename = '\xdf-\u66e8\u66e9\u66eb' with os_helper.temp_cwd(dirname): with open(filename, 'wb') as f: @@ -188,15 +190,5 @@ class UnicodeNFKDFileTests(UnicodeFileTests): normal_form = 'NFKD' -def test_main(): - support.run_unittest( - UnicodeFileTests, - UnicodeNFCFileTests, - UnicodeNFDFileTests, - UnicodeNFKCFileTests, - UnicodeNFKDFileTests, - ) - - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_unicode_identifiers.py b/Lib/test/test_unicode_identifiers.py index 7bd5e5da5..9e611caf6 100644 --- a/Lib/test/test_unicode_identifiers.py +++ b/Lib/test/test_unicode_identifiers.py @@ -26,9 +26,11 @@ class PEP3131Test(unittest.TestCase): def test_invalid(self): try: from test import badsyntax_3131 - except SyntaxError as s: - self.assertEqual(str(s), - "invalid character in identifier (badsyntax_3131.py, line 2)") + except SyntaxError as err: + self.assertEqual(str(err), + "invalid character '€' (U+20AC) (badsyntax_3131.py, line 2)") + self.assertEqual(err.lineno, 2) + self.assertEqual(err.offset, 1) else: self.fail("expected exception didn't occur") From 2f488cb153634b3649145a1131360a988a60818f Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 02:52:47 +0900 Subject: [PATCH 22/26] adjust failing tests from test_unicode* --- Lib/test/test_unicode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 8dd25fcca..a4c3c5e55 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -2530,6 +2530,8 @@ class UnicodeTest(string_tests.CommonTest, support.check_free_after_iterating(self, iter, str) support.check_free_after_iterating(self, reversed, str) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_check_encoding_errors(self): # bpo-37388: str(bytes) and str.decode() must check encoding and errors # arguments in dev mode From 33690be35b68f8b4a9afce4ce35e8d92af0be5ab Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 03:20:23 +0900 Subject: [PATCH 23/26] Update tests from CPython 3.10.5 --- Lib/test/test_bool.py | 36 +- Lib/test/test_builtin.py | 380 +- Lib/test/test_class.py | 2 +- Lib/test/test_decimal.py | 11501 +++++++++++++++++++------------------ Lib/test/test_float.py | 48 +- Lib/test/test_range.py | 87 +- Lib/test/test_richcmp.py | 3 - 7 files changed, 6198 insertions(+), 5859 deletions(-) diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 9ed5c55c4..f413b7aaa 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -1,7 +1,6 @@ # Test properties of bool promised by PEP 285 import unittest -from test import support from test.support import os_helper import os @@ -21,20 +20,11 @@ class BoolTest(unittest.TestCase): self.assertRaises(TypeError, int.__new__, bool, 0) - def test_print(self): - try: - with open(os_helper.TESTFN, "w") as fo: - print(False, True, file=fo) - with open(os_helper.TESTFN, "r") as fi: - self.assertEqual(fi.read(), 'False True\n') - finally: - os.remove(os_helper.TESTFN) - def test_repr(self): self.assertEqual(repr(False), 'False') self.assertEqual(repr(True), 'True') - self.assertEqual(eval(repr(False)), False) - self.assertEqual(eval(repr(True)), True) + self.assertIs(eval(repr(False)), False) + self.assertIs(eval(repr(True)), True) def test_str(self): self.assertEqual(str(False), 'False') @@ -246,7 +236,7 @@ class BoolTest(unittest.TestCase): def test_fileclosed(self): try: - with open(os_helper.TESTFN, "w") as f: + with open(os_helper.TESTFN, "w", encoding="utf-8") as f: self.assertIs(f.closed, False) self.assertIs(f.closed, True) finally: @@ -365,8 +355,22 @@ class BoolTest(unittest.TestCase): self.assertIs(type(False.real), int) self.assertIs(type(False.imag), int) -def test_main(): - support.run_unittest(BoolTest) + def test_bool_called_at_least_once(self): + class X: + def __init__(self): + self.count = 0 + def __bool__(self): + self.count += 1 + return True + + def f(x): + if x or True: + pass + + x = X() + f(x) + self.assertGreaterEqual(x.count, 1) + if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 847c97fc9..a233fc3dd 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1,10 +1,12 @@ # Python test set -- built-in functions import ast +import asyncio import builtins import collections import decimal import fractions +import gc import io import locale import os @@ -17,13 +19,18 @@ import traceback import types import unittest import warnings -from functools import partial from contextlib import ExitStack +from functools import partial +from inspect import CO_COROUTINE +from itertools import product +from textwrap import dedent +from types import AsyncGeneratorType, FunctionType from operator import neg -from test.support import swap_attr -from test.support.warnings_helper import check_warnings +from test import support +from test.support import (swap_attr, maybe_get_event_loop_policy) +from test.support.os_helper import (EnvironmentVarGuard, TESTFN, unlink) from test.support.script_helper import assert_python_ok -from test.support.os_helper import EnvironmentVarGuard, TESTFN, unlink +from test.support.warnings_helper import check_warnings from unittest.mock import MagicMock, patch try: import pty, signal @@ -156,6 +163,11 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, __import__, 1, 2, 3, 4) self.assertRaises(ValueError, __import__, '') self.assertRaises(TypeError, __import__, 'sys', name='sys') + # Relative import outside of a package with no __package__ or __spec__ (bpo-37409). + with self.assertWarns(ImportWarning): + self.assertRaises(ImportError, __import__, '', + {'__package__': None, '__spec__': None, '__name__': '__main__'}, + locals={}, fromlist=('foo',), level=1) # embedded null character self.assertRaises(ModuleNotFoundError, __import__, 'string\x00') @@ -320,8 +332,8 @@ class BuiltinTest(unittest.TestCase): bom = b'\xef\xbb\xbf' compile(bom + b'print(1)\n', '', 'exec') compile(source='pass', filename='?', mode='exec') - compile(dont_inherit=0, filename='tmp', source='0', mode='eval') - compile('pass', '?', dont_inherit=1, mode='exec') + compile(dont_inherit=False, filename='tmp', source='0', mode='eval') + compile('pass', '?', dont_inherit=True, mode='exec') compile(memoryview(b"text"), "name", "exec") self.assertRaises(TypeError, compile) self.assertRaises(ValueError, compile, 'print(42)\n', '', 'badmode') @@ -364,6 +376,139 @@ class BuiltinTest(unittest.TestCase): rv = ns['f']() self.assertEqual(rv, tuple(expected)) + def test_compile_top_level_await_no_coro(self): + """Make sure top level non-await codes get the correct coroutine flags""" + modes = ('single', 'exec') + code_samples = [ + '''def f():pass\n''', + '''[x for x in l]''', + '''{x for x in l}''', + '''(x for x in l)''', + '''{x:x for x in l}''', + ] + for mode, code_sample in product(modes, code_samples): + source = dedent(code_sample) + co = compile(source, + '?', + mode, + flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT) + + self.assertNotEqual(co.co_flags & CO_COROUTINE, CO_COROUTINE, + msg=f"source={source} mode={mode}") + + + def test_compile_top_level_await(self): + """Test whether code some top level await can be compiled. + + Make sure it compiles only with the PyCF_ALLOW_TOP_LEVEL_AWAIT flag + set, and make sure the generated code object has the CO_COROUTINE flag + set in order to execute it with `await eval(.....)` instead of exec, + or via a FunctionType. + """ + + # helper function just to check we can run top=level async-for + async def arange(n): + for i in range(n): + yield i + + modes = ('single', 'exec') + code_samples = [ + '''a = await asyncio.sleep(0, result=1)''', + '''async for i in arange(1): + a = 1''', + '''async with asyncio.Lock() as l: + a = 1''', + '''a = [x async for x in arange(2)][1]''', + '''a = 1 in {x async for x in arange(2)}''', + '''a = {x:1 async for x in arange(1)}[0]''', + '''a = [x async for x in arange(2) async for x in arange(2)][1]''', + '''a = [x async for x in (x async for x in arange(5))][1]''', + '''a, = [1 for x in {x async for x in arange(1)}]''', + '''a = [await asyncio.sleep(0, x) async for x in arange(2)][1]''' + ] + policy = maybe_get_event_loop_policy() + try: + for mode, code_sample in product(modes, code_samples): + source = dedent(code_sample) + with self.assertRaises( + SyntaxError, msg=f"source={source} mode={mode}"): + compile(source, '?', mode) + + co = compile(source, + '?', + mode, + flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT) + + self.assertEqual(co.co_flags & CO_COROUTINE, CO_COROUTINE, + msg=f"source={source} mode={mode}") + + # test we can create and advance a function type + globals_ = {'asyncio': asyncio, 'a': 0, 'arange': arange} + async_f = FunctionType(co, globals_) + asyncio.run(async_f()) + self.assertEqual(globals_['a'], 1) + + # test we can await-eval, + globals_ = {'asyncio': asyncio, 'a': 0, 'arange': arange} + asyncio.run(eval(co, globals_)) + self.assertEqual(globals_['a'], 1) + finally: + asyncio.set_event_loop_policy(policy) + + def test_compile_top_level_await_invalid_cases(self): + # helper function just to check we can run top=level async-for + async def arange(n): + for i in range(n): + yield i + + modes = ('single', 'exec') + code_samples = [ + '''def f(): await arange(10)\n''', + '''def f(): [x async for x in arange(10)]\n''', + '''def f(): [await x async for x in arange(10)]\n''', + '''def f(): + async for i in arange(1): + a = 1 + ''', + '''def f(): + async with asyncio.Lock() as l: + a = 1 + ''' + ] + policy = maybe_get_event_loop_policy() + try: + for mode, code_sample in product(modes, code_samples): + source = dedent(code_sample) + with self.assertRaises( + SyntaxError, msg=f"source={source} mode={mode}"): + compile(source, '?', mode) + + with self.assertRaises( + SyntaxError, msg=f"source={source} mode={mode}"): + co = compile(source, + '?', + mode, + flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT) + finally: + asyncio.set_event_loop_policy(policy) + + + def test_compile_async_generator(self): + """ + With the PyCF_ALLOW_TOP_LEVEL_AWAIT flag added in 3.8, we want to + make sure AsyncGenerators are still properly not marked with the + CO_COROUTINE flag. + """ + code = dedent("""async def ticker(): + for i in range(10): + yield i + await asyncio.sleep(0)""") + + co = compile(code, '?', 'exec', flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT) + glob = {} + exec(co, glob) + self.assertEqual(type(glob['ticker']()), AsyncGeneratorType) + def test_delattr(self): sys.spam = 1 delattr(sys, 'spam') @@ -442,8 +587,8 @@ class BuiltinTest(unittest.TestCase): # dir(traceback) try: raise IndexError - except: - self.assertEqual(len(dir(sys.exc_info()[2])), 4) + except IndexError as e: + self.assertEqual(len(dir(e.__traceback__)), 4) # test that object has a __dir__() self.assertEqual(sorted([].__dir__()), dir([])) @@ -664,66 +809,6 @@ class BuiltinTest(unittest.TestCase): f2 = filter(filter_char, "abcdeabcde") self.check_iter_pickle(f1, list(f2), proto) - def test_zip_pickle_strict(self): - a = (1, 2, 3) - b = (4, 5, 6) - t = [(1, 4), (2, 5), (3, 6)] - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z1 = zip(a, b, strict=True) - self.check_iter_pickle(z1, t, proto) - - def test_zip_pickle_strict_fail(self): - a = (1, 2, 3) - b = (4, 5, 6, 7) - t = [(1, 4), (2, 5), (3, 6)] - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z1 = zip(a, b, strict=True) - z2 = pickle.loads(pickle.dumps(z1, proto)) - self.assertEqual(self.iter_error(z1, ValueError), t) - self.assertEqual(self.iter_error(z2, ValueError), t) - - def test_zip_pickle_stability(self): - # Pickles of zip((1, 2, 3), (4, 5, 6)) dumped from 3.9: - pickles = [ - b'citertools\nizip\np0\n(c__builtin__\niter\np1\n((I1\nI2\nI3\ntp2\ntp3\nRp4\nI0\nbg1\n((I4\nI5\nI6\ntp5\ntp6\nRp7\nI0\nbtp8\nRp9\n.', - b'citertools\nizip\nq\x00(c__builtin__\niter\nq\x01((K\x01K\x02K\x03tq\x02tq\x03Rq\x04K\x00bh\x01((K\x04K\x05K\x06tq\x05tq\x06Rq\x07K\x00btq\x08Rq\t.', - b'\x80\x02citertools\nizip\nq\x00c__builtin__\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05K\x06\x87q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t.', - b'\x80\x03cbuiltins\nzip\nq\x00cbuiltins\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05K\x06\x87q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t.', - b'\x80\x04\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05K\x06\x87\x94\x85\x94R\x94K\x00b\x86\x94R\x94.', - b'\x80\x05\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05K\x06\x87\x94\x85\x94R\x94K\x00b\x86\x94R\x94.', - ] - for protocol, dump in enumerate(pickles): - z1 = zip((1, 2, 3), (4, 5, 6)) - z2 = zip((1, 2, 3), (4, 5, 6), strict=False) - z3 = pickle.loads(dump) - l3 = list(z3) - self.assertEqual(type(z3), zip) - self.assertEqual(pickle.dumps(z1, protocol), dump) - self.assertEqual(pickle.dumps(z2, protocol), dump) - self.assertEqual(list(z1), l3) - self.assertEqual(list(z2), l3) - - def test_zip_pickle_strict_stability(self): - # Pickles of zip((1, 2, 3), (4, 5), strict=True) dumped from 3.10: - pickles = [ - b'citertools\nizip\np0\n(c__builtin__\niter\np1\n((I1\nI2\nI3\ntp2\ntp3\nRp4\nI0\nbg1\n((I4\nI5\ntp5\ntp6\nRp7\nI0\nbtp8\nRp9\nI01\nb.', - b'citertools\nizip\nq\x00(c__builtin__\niter\nq\x01((K\x01K\x02K\x03tq\x02tq\x03Rq\x04K\x00bh\x01((K\x04K\x05tq\x05tq\x06Rq\x07K\x00btq\x08Rq\tI01\nb.', - b'\x80\x02citertools\nizip\nq\x00c__builtin__\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05\x86q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t\x88b.', - b'\x80\x03cbuiltins\nzip\nq\x00cbuiltins\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05\x86q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t\x88b.', - b'\x80\x04\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05\x86\x94\x85\x94R\x94K\x00b\x86\x94R\x94\x88b.', - b'\x80\x05\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05\x86\x94\x85\x94R\x94K\x00b\x86\x94R\x94\x88b.', - ] - a = (1, 2, 3) - b = (4, 5) - t = [(1, 4), (2, 5)] - for protocol, dump in enumerate(pickles): - z1 = zip(a, b, strict=True) - z2 = pickle.loads(dump) - self.assertEqual(pickle.dumps(z1, protocol), dump) - self.assertEqual(type(z2), zip) - self.assertEqual(self.iter_error(z1, ValueError), t) - self.assertEqual(self.iter_error(z2, ValueError), t) - def test_getattr(self): self.assertTrue(getattr(sys, 'stdout') is sys.stdout) self.assertRaises(TypeError, getattr, sys, 1) @@ -758,6 +843,7 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(hash('spam'), hash(b'spam')) hash((0,1,2,3)) def f(): pass + hash(f) self.assertRaises(TypeError, hash, []) self.assertRaises(TypeError, hash, {}) # Bug 1536021: Allow hash to return long objects @@ -941,7 +1027,12 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(max(1, 2.0, 3), 3) self.assertEqual(max(1.0, 2, 3), 3) - self.assertRaises(TypeError, max) + with self.assertRaisesRegex( + TypeError, + 'max expected at least 1 argument, got 0' + ): + max() + self.assertRaises(TypeError, max, 42) self.assertRaises(ValueError, max, ()) class BadSeq: @@ -995,7 +1086,12 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(min(1, 2.0, 3), 1) self.assertEqual(min(1.0, 2, 3), 1.0) - self.assertRaises(TypeError, min) + with self.assertRaisesRegex( + TypeError, + 'min expected at least 1 argument, got 0' + ): + min() + self.assertRaises(TypeError, min, 42) self.assertRaises(ValueError, min, ()) class BadSeq: @@ -1073,7 +1169,7 @@ class BuiltinTest(unittest.TestCase): def write_testfile(self): # NB the first 4 lines are also used to test input, below - fp = open(TESTFN, 'w') + fp = open(TESTFN, 'w', encoding="utf-8") self.addCleanup(unlink, TESTFN) with fp: fp.write('1+1\n') @@ -1085,7 +1181,7 @@ class BuiltinTest(unittest.TestCase): def test_open(self): self.write_testfile() - fp = open(TESTFN, 'r') + fp = open(TESTFN, encoding="utf-8") with fp: self.assertEqual(fp.readline(4), '1+1\n') self.assertEqual(fp.readline(), 'The quick brown fox jumps over the lazy dog.\n') @@ -1113,16 +1209,17 @@ class BuiltinTest(unittest.TestCase): self.write_testfile() current_locale_encoding = locale.getpreferredencoding(False) - fp = open(TESTFN, 'w') + with warnings.catch_warnings(): + warnings.simplefilter("ignore", EncodingWarning) + fp = open(TESTFN, 'w') with fp: self.assertEqual(fp.encoding, current_locale_encoding) finally: os.environ.clear() os.environ.update(old_environ) - @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON Windows') def test_open_non_inheritable(self): - fileobj = open(__file__) + fileobj = open(__file__, encoding="utf-8") with fileobj: self.assertFalse(os.get_inheritable(fileobj.fileno())) @@ -1217,7 +1314,7 @@ class BuiltinTest(unittest.TestCase): def test_input(self): self.write_testfile() - fp = open(TESTFN, 'r') + fp = open(TESTFN, encoding="utf-8") savestdin = sys.stdin savestdout = sys.stdout # Eats the echo try: @@ -1387,6 +1484,24 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(sum(range(10), 1000), 1045) self.assertEqual(sum(range(10), start=1000), 1045) + self.assertEqual(sum(range(10), 2**31-5), 2**31+40) + self.assertEqual(sum(range(10), 2**63-5), 2**63+40) + + self.assertEqual(sum(i % 2 != 0 for i in range(10)), 5) + self.assertEqual(sum((i % 2 != 0 for i in range(10)), 2**31-3), + 2**31+2) + self.assertEqual(sum((i % 2 != 0 for i in range(10)), 2**63-3), + 2**63+2) + self.assertIs(sum([], False), False) + + self.assertEqual(sum(i / 2 for i in range(10)), 22.5) + self.assertEqual(sum((i / 2 for i in range(10)), 1000), 1022.5) + self.assertEqual(sum((i / 2 for i in range(10)), 1000.25), 1022.75) + self.assertEqual(sum([0.5, 1]), 1.5) + self.assertEqual(sum([1, 0.5]), 1.5) + self.assertEqual(repr(sum([-0.0])), '0.0') + self.assertEqual(repr(sum([-0.0], -0.0)), '-0.0') + self.assertEqual(repr(sum([], -0.0)), '-0.0') self.assertRaises(TypeError, sum) self.assertRaises(TypeError, sum, 42) @@ -1398,6 +1513,9 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, sum, [[1], [2], [3]]) self.assertRaises(TypeError, sum, [{2:3}]) self.assertRaises(TypeError, sum, [{2:3}]*2, {2:3}) + self.assertRaises(TypeError, sum, [], '') + self.assertRaises(TypeError, sum, [], b'') + self.assertRaises(TypeError, sum, [], bytearray()) class BadSeq: def __getitem__(self, index): @@ -1499,6 +1617,36 @@ class BuiltinTest(unittest.TestCase): z1 = zip(a, b) self.check_iter_pickle(z1, t, proto) + def test_zip_pickle_strict(self): + a = (1, 2, 3) + b = (4, 5, 6) + t = [(1, 4), (2, 5), (3, 6)] + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z1 = zip(a, b, strict=True) + self.check_iter_pickle(z1, t, proto) + + def test_zip_pickle_strict_fail(self): + a = (1, 2, 3) + b = (4, 5, 6, 7) + t = [(1, 4), (2, 5), (3, 6)] + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z1 = zip(a, b, strict=True) + z2 = pickle.loads(pickle.dumps(z1, proto)) + self.assertEqual(self.iter_error(z1, ValueError), t) + self.assertEqual(self.iter_error(z2, ValueError), t) + + def test_zip_bad_iterable(self): + exception = TypeError() + + class BadIterable: + def __iter__(self): + raise exception + + with self.assertRaises(TypeError) as cm: + zip(BadIterable()) + + self.assertIs(cm.exception, exception) + def test_zip_strict(self): self.assertEqual(tuple(zip((1, 2, 3), 'abc', strict=True)), ((1, 'a'), (2, 'b'), (3, 'c'))) @@ -1581,6 +1729,18 @@ class BuiltinTest(unittest.TestCase): l8 = self.iter_error(zip(Iter(3), "AB", strict=True), ValueError) self.assertEqual(l8, [(2, "A"), (1, "B")]) + @support.cpython_only + def test_zip_result_gc(self): + # bpo-42536: zip's tuple-reuse speed trick breaks the GC's assumptions + # about what can be untracked. Make sure we re-track result tuples + # whenever we reuse them. + it = zip([[]]) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None,). Make sure it's re-tracked when + # it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + def test_format(self): # Test the basic machinery of the format() builtin. Don't test # the specifics of the various formatters @@ -1696,6 +1856,11 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(ValueError, x.translate, b"1", 1) self.assertRaises(TypeError, x.translate, b"1"*256, 1) + def test_bytearray_extend_error(self): + array = bytearray() + bad_iter = map(int, "X") + self.assertRaises(ValueError, array.extend, bad_iter) + def test_construct_singletons(self): for const in None, Ellipsis, NotImplemented: tp = type(const) @@ -1703,6 +1868,20 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, tp, 1, 2) self.assertRaises(TypeError, tp, a=1, b=2) + def test_warning_notimplemented(self): + # Issue #35712: NotImplemented is a sentinel value that should never + # be evaluated in a boolean context (virtually all such use cases + # are a result of accidental misuse implementing rich comparison + # operations in terms of one another). + # For the time being, it will continue to evaluate as a true value, but + # issue a deprecation warning (with the eventual intent to make it + # a TypeError). + self.assertWarns(DeprecationWarning, bool, NotImplemented) + with self.assertWarns(DeprecationWarning): + self.assertTrue(NotImplemented) + with self.assertWarns(DeprecationWarning): + self.assertFalse(not NotImplemented) + @unittest.skip("TODO: RUSTPYTHON, AttributeError: module 'sys' has no attribute '__breakpointhook__'") class TestBreakpoint(unittest.TestCase): @@ -1815,7 +1994,21 @@ class PtyTests(unittest.TestCase): """Tests that use a pseudo terminal to guarantee stdin and stdout are terminals in the test environment""" + @staticmethod + def handle_sighup(signum, frame): + # bpo-40140: if the process is the session leader, os.close(fd) + # of "pid, fd = pty.fork()" can raise SIGHUP signal: + # just ignore the signal. + pass + def run_child(self, child, terminal_input): + old_sighup = signal.signal(signal.SIGHUP, self.handle_sighup) + try: + return self._run_child(child, terminal_input) + finally: + signal.signal(signal.SIGHUP, old_sighup) + + def _run_child(self, child, terminal_input): r, w = os.pipe() # Pipe test results from child back to parent try: pid, fd = pty.fork() @@ -1824,6 +2017,7 @@ class PtyTests(unittest.TestCase): os.close(w) self.skipTest("pty.fork() raised {}".format(e)) raise + if pid == 0: # Child try: @@ -1837,11 +2031,13 @@ class PtyTests(unittest.TestCase): finally: # We don't want to return to unittest... os._exit(0) + # Parent os.close(w) os.write(fd, terminal_input) + # Get results from the pipe - with open(r, "r") as rpipe: + with open(r, encoding="utf-8") as rpipe: lines = [] while True: line = rpipe.readline().strip() @@ -1849,6 +2045,7 @@ class PtyTests(unittest.TestCase): # The other end was closed => the child exited break lines.append(line) + # Check the result was got and corresponds to the user's terminal input if len(lines) != 2: # Something went wrong, try to get at stderr @@ -1866,10 +2063,12 @@ class PtyTests(unittest.TestCase): child_output = child_output.decode("ascii", "ignore") self.fail("got %d lines in pipe but expected 2, child output was:\n%s" % (len(lines), child_output)) + + # bpo-40155: Close the PTY before waiting for the child process + # completion, otherwise the child process hangs on AIX. os.close(fd) - # Wait until the child process completes - os.waitpid(pid, 0) + support.wait_process(pid, exitcode=0) return lines @@ -1904,12 +2103,24 @@ class PtyTests(unittest.TestCase): # is different and invokes GNU readline if available). self.check_input_tty("prompt", b"quux") + def skip_if_readline(self): + # bpo-13886: When the readline module is loaded, PyOS_Readline() uses + # the readline implementation. In some cases, the Python readline + # callback rlhandler() is called by readline with a string without + # non-ASCII characters. Skip tests on non-ASCII characters if the + # readline module is loaded, since test_builtin is not intented to test + # the readline module, but the builtins module. + if 'readline' in sys.modules: + self.skipTest("the readline module is loaded") + def test_input_tty_non_ascii(self): - # Check stdin/stdout encoding is used when invoking GNU readline + self.skip_if_readline() + # Check stdin/stdout encoding is used when invoking PyOS_Readline() self.check_input_tty("prompté", b"quux\xe9", "utf-8") def test_input_tty_non_ascii_unicode_errors(self): - # Check stdin/stdout error handler is used when invoking GNU readline + self.skip_if_readline() + # Check stdin/stdout error handler is used when invoking PyOS_Readline() self.check_input_tty("prompté", b"quux\xe9", "ascii") def test_input_no_stdout_fileno(self): @@ -1941,7 +2152,7 @@ class TestSorted(unittest.TestCase): self.assertEqual(data, sorted(copy, key=lambda x: -x)) self.assertNotEqual(data, copy) random.shuffle(copy) - self.assertEqual(data, sorted(copy, reverse=1)) + self.assertEqual(data, sorted(copy, reverse=True)) self.assertNotEqual(data, copy) def test_bad_arguments(self): @@ -2178,9 +2389,8 @@ class TestType(unittest.TestCase): def load_tests(loader, tests, pattern): - # XXX RustPython - # from doctest import DocTestSuite - # tests.addTest(DocTestSuite(builtins)) + from doctest import DocTestSuite + tests.addTest(DocTestSuite(builtins)) return tests if __name__ == "__main__": diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 77bbbed2f..293c7ba14 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -534,7 +534,7 @@ class ClassTests(unittest.TestCase): # In debug mode, printed XXX undetected error and # raises AttributeError I() - except AttributeError as x: + except AttributeError: pass else: self.fail("attribute error for I.__init__ got masked") diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index e3251d1e1..19b4ea872 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -1,5720 +1,5781 @@ -# 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_main() -with the corresponding argument. -""" - -import math -import os, sys -import operator -import warnings -import pickle, copy -import unittest -import numbers -import locale -from test.support import (run_unittest, run_doctest, is_resource_enabled, - requires_IEEE_754, requires_docstrings) -from test.support import (TestFailed, run_with_locale, - cpython_only) -from test.support.import_helper import import_fresh_module -import random -import inspect -import threading - - -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 - - -# 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_main() 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(unittest.TestCase): - """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) 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 - -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): - '''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") - - @cpython_only - def test_from_legacy_strings(self): - import _testcapi - Decimal = self.decimal.Decimal - context = self.decimal.Context() - - s = _testcapi.unicode_legacy_string('9.999999') - self.assertEqual(str(Decimal(s)), '9.999999') - self.assertEqual(str(context.create_decimal(s)), '9.999999') - - 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.assertTrue(isinstance(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 - - # TODO: RUSTPYTHON - @unittest.expectedFailure - 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 CExplicitConstructionTest(ExplicitConstructionTest): - decimal = C -class PyExplicitConstructionTest(ExplicitConstructionTest): - decimal = P - -class ImplicitConstructionTest(unittest.TestCase): - '''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') - -class CImplicitConstructionTest(ImplicitConstructionTest): - decimal = C -class PyImplicitConstructionTest(ImplicitConstructionTest): - decimal = P - -class FormatTest(unittest.TestCase): - '''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'), - ('.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 - (' 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'), - - # 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') - - 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') - - @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') - - # TODO: RUSTPYTHON - @unittest.expectedFailure - 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) - -class CFormatTest(FormatTest): - decimal = C -class PyFormatTest(FormatTest): - decimal = P - -class ArithmeticOperatorsTest(unittest.TestCase): - '''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 - 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') - -class CArithmeticOperatorsTest(ArithmeticOperatorsTest): - decimal = C -class PyArithmeticOperatorsTest(ArithmeticOperatorsTest): - 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]) - -class ThreadingTest(unittest.TestCase): - '''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() - - th1 = threading.Thread(target=thfunc1, args=(self,)) - th2 = threading.Thread(target=thfunc2, args=(self,)) - - 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 - - -class CThreadingTest(ThreadingTest): - decimal = C - -class PyThreadingTest(ThreadingTest): - decimal = P - -class UsabilityTest(unittest.TestCase): - '''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), hashit(int(value))) - - #the same hash that to an int - self.assertEqual(hashit(Decimal(23)), hashit(23)) - self.assertRaises(TypeError, hash, Decimal('sNaN')) - self.assertTrue(hashit(Decimal('Inf'))) - self.assertTrue(hashit(Decimal('-Inf'))) - - # 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(f), hashit(d)) - - 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_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', -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) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - 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))) - -class CUsabilityTest(UsabilityTest): - decimal = C -class PyUsabilityTest(UsabilityTest): - decimal = P - -class PythonAPItests(unittest.TestCase): - - def test_abc(self): - Decimal = self.decimal.Decimal - - self.assertTrue(issubclass(Decimal, numbers.Number)) - self.assertFalse(issubclass(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')) - - 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.assertTrue(issubclass(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')) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - 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.assertTrue(issubclass(DecimalException, ArithmeticError)) - - self.assertTrue(issubclass(InvalidOperation, DecimalException)) - self.assertTrue(issubclass(FloatOperation, DecimalException)) - self.assertTrue(issubclass(FloatOperation, TypeError)) - self.assertTrue(issubclass(DivisionByZero, DecimalException)) - self.assertTrue(issubclass(DivisionByZero, ZeroDivisionError)) - self.assertTrue(issubclass(Overflow, Rounded)) - self.assertTrue(issubclass(Overflow, Inexact)) - self.assertTrue(issubclass(Overflow, DecimalException)) - self.assertTrue(issubclass(Underflow, Inexact)) - self.assertTrue(issubclass(Underflow, Rounded)) - self.assertTrue(issubclass(Underflow, Subnormal)) - self.assertTrue(issubclass(Underflow, DecimalException)) - - self.assertTrue(issubclass(Subnormal, DecimalException)) - self.assertTrue(issubclass(Inexact, DecimalException)) - self.assertTrue(issubclass(Rounded, DecimalException)) - self.assertTrue(issubclass(Clamped, DecimalException)) - - self.assertTrue(issubclass(decimal.ConversionSyntax, InvalidOperation)) - self.assertTrue(issubclass(decimal.DivisionImpossible, InvalidOperation)) - self.assertTrue(issubclass(decimal.DivisionUndefined, InvalidOperation)) - self.assertTrue(issubclass(decimal.DivisionUndefined, ZeroDivisionError)) - self.assertTrue(issubclass(decimal.InvalidContext, InvalidOperation)) - -class CPythonAPItests(PythonAPItests): - decimal = C -class PyPythonAPItests(PythonAPItests): - decimal = P - -class ContextAPItests(unittest.TestCase): - - 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]) - - @cpython_only - def test_from_legacy_strings(self): - import _testcapi - c = self.decimal.Context() - - for rnd in RoundingModes: - c.rounding = _testcapi.unicode_legacy_string(rnd) - self.assertEqual(c.rounding, rnd) - - s = _testcapi.unicode_legacy_string('') - self.assertRaises(TypeError, setattr, c, 'rounding', s) - - s = _testcapi.unicode_legacy_string('ROUND_\x00UP') - self.assertRaises(TypeError, setattr, c, 'rounding', s) - - 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') - -class CContextAPItests(ContextAPItests): - decimal = C -class PyContextAPItests(ContextAPItests): - decimal = P - -class ContextWithStatement(unittest.TestCase): - # 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_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 CContextWithStatement(ContextWithStatement): - decimal = C -class PyContextWithStatement(ContextWithStatement): - decimal = P - -class ContextFlags(unittest.TestCase): - - 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]) - -class CContextFlags(ContextFlags): - decimal = C -class PyContextFlags(ContextFlags): - decimal = P - -class SpecialContexts(unittest.TestCase): - """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 CSpecialContexts(SpecialContexts): - decimal = C -class PySpecialContexts(SpecialContexts): - decimal = P - -class ContextInputValidation(unittest.TestCase): - - 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 CContextInputValidation(ContextInputValidation): - decimal = C -class PyContextInputValidation(ContextInputValidation): - decimal = P - -class ContextSubclassing(unittest.TestCase): - - 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]) - -class CContextSubclassing(ContextSubclassing): - decimal = C -class PyContextSubclassing(ContextSubclassing): - decimal = P - -@skip_if_extra_functionality -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.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.__version__, P.__version__) - - self.assertEqual(dir(C), dir(P)) - - 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(unittest.TestCase): - - 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) - # 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) - -class CCoverage(Coverage): - decimal = C -class PyCoverage(Coverage): - decimal = P - -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') - c.prec = 201 - x = Decimal(2**578) ** Decimal("-0.5") - - 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_ieee_context(self): - # issue 8786: Add support for IEEE 754 contexts to decimal module. - IEEEContext = C.IEEEContext - DECIMAL32 = C.DECIMAL32 - DECIMAL64 = C.DECIMAL64 - DECIMAL128 = C.DECIMAL128 - - def assert_rest(self, context): - self.assertEqual(context.clamp, 1) - assert_signals(self, context, 'traps', []) - assert_signals(self, context, 'flags', []) - - c = IEEEContext(DECIMAL32) - self.assertEqual(c.prec, 7) - self.assertEqual(c.Emax, 96) - self.assertEqual(c.Emin, -95) - assert_rest(self, c) - - c = IEEEContext(DECIMAL64) - self.assertEqual(c.prec, 16) - self.assertEqual(c.Emax, 384) - self.assertEqual(c.Emin, -383) - assert_rest(self, c) - - c = IEEEContext(DECIMAL128) - self.assertEqual(c.prec, 34) - self.assertEqual(c.Emax, 6144) - self.assertEqual(c.Emin, -6143) - assert_rest(self, c) - - # Invalid values - self.assertRaises(OverflowError, IEEEContext, 2**63) - self.assertRaises(ValueError, IEEEContext, -1) - self.assertRaises(ValueError, IEEEContext, 1024) - - @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 - ) - - # IEEEContext - self.assertEqual(C.DECIMAL32, 32) - self.assertEqual(C.DECIMAL64, 64) - self.assertEqual(C.DECIMAL128, 128) - self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, 512) - - # 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 - 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, - 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.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)) - -@requires_docstrings -@unittest.skipUnless(C, "test requires C version") -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 as err: - 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 as err: - raise TestFailed("invalid signature for %s: %s %s" % (p_func, args, kwds)) - - doit('Decimal') - 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] - - # 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(CIBMTestCases, 'test_' + head, tester) - setattr(PyIBMTestCases, 'test_' + head, tester) - del filename, head, tail, tester - - - 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'].") - - -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_main(arith=False, verbose=True) - elif args: - test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug) - else: - test_main(arith=True, verbose=True) +# 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_main() +with the corresponding argument. +""" + +import math +import os, sys +import operator +import warnings +import pickle, copy +import unittest +import numbers +import locale +from test.support import (run_unittest, run_doctest, is_resource_enabled, + requires_IEEE_754, requires_docstrings, + requires_legacy_unicode_capi, check_sanitizer) +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 warnings_helper +import random +import inspect +import threading + + +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 + + +# 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_main() 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(unittest.TestCase): + """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 + +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): + '''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") + + @cpython_only + @requires_legacy_unicode_capi + @warnings_helper.ignore_warnings(category=DeprecationWarning) + def test_from_legacy_strings(self): + import _testcapi + Decimal = self.decimal.Decimal + context = self.decimal.Context() + + s = _testcapi.unicode_legacy_string('9.999999') + self.assertEqual(str(Decimal(s)), '9.999999') + self.assertEqual(str(context.create_decimal(s)), '9.999999') + + 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.assertTrue(isinstance(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 + + # TODO: RUSTPYTHON + @unittest.expectedFailure + 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 CExplicitConstructionTest(ExplicitConstructionTest): + decimal = C +class PyExplicitConstructionTest(ExplicitConstructionTest): + decimal = P + +class ImplicitConstructionTest(unittest.TestCase): + '''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') + +class CImplicitConstructionTest(ImplicitConstructionTest): + decimal = C +class PyImplicitConstructionTest(ImplicitConstructionTest): + decimal = P + +class FormatTest(unittest.TestCase): + '''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'), + ('.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 + (' 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'), + + # 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') + + 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') + + @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') + + # TODO: RUSTPYTHON + @unittest.expectedFailure + 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) + +class CFormatTest(FormatTest): + decimal = C +class PyFormatTest(FormatTest): + decimal = P + +class ArithmeticOperatorsTest(unittest.TestCase): + '''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 + 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') + +class CArithmeticOperatorsTest(ArithmeticOperatorsTest): + decimal = C +class PyArithmeticOperatorsTest(ArithmeticOperatorsTest): + 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]) + +class ThreadingTest(unittest.TestCase): + '''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() + + th1 = threading.Thread(target=thfunc1, args=(self,)) + th2 = threading.Thread(target=thfunc2, args=(self,)) + + 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 + + +class CThreadingTest(ThreadingTest): + decimal = C + +class PyThreadingTest(ThreadingTest): + decimal = P + +class UsabilityTest(unittest.TestCase): + '''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', -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) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + 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))) + +class CUsabilityTest(UsabilityTest): + decimal = C +class PyUsabilityTest(UsabilityTest): + decimal = P + +class PythonAPItests(unittest.TestCase): + + def test_abc(self): + Decimal = self.decimal.Decimal + + self.assertTrue(issubclass(Decimal, numbers.Number)) + self.assertFalse(issubclass(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')) + + 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.assertTrue(issubclass(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')) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + 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.assertTrue(issubclass(DecimalException, ArithmeticError)) + + self.assertTrue(issubclass(InvalidOperation, DecimalException)) + self.assertTrue(issubclass(FloatOperation, DecimalException)) + self.assertTrue(issubclass(FloatOperation, TypeError)) + self.assertTrue(issubclass(DivisionByZero, DecimalException)) + self.assertTrue(issubclass(DivisionByZero, ZeroDivisionError)) + self.assertTrue(issubclass(Overflow, Rounded)) + self.assertTrue(issubclass(Overflow, Inexact)) + self.assertTrue(issubclass(Overflow, DecimalException)) + self.assertTrue(issubclass(Underflow, Inexact)) + self.assertTrue(issubclass(Underflow, Rounded)) + self.assertTrue(issubclass(Underflow, Subnormal)) + self.assertTrue(issubclass(Underflow, DecimalException)) + + self.assertTrue(issubclass(Subnormal, DecimalException)) + self.assertTrue(issubclass(Inexact, DecimalException)) + self.assertTrue(issubclass(Rounded, DecimalException)) + self.assertTrue(issubclass(Clamped, DecimalException)) + + self.assertTrue(issubclass(decimal.ConversionSyntax, InvalidOperation)) + self.assertTrue(issubclass(decimal.DivisionImpossible, InvalidOperation)) + self.assertTrue(issubclass(decimal.DivisionUndefined, InvalidOperation)) + self.assertTrue(issubclass(decimal.DivisionUndefined, ZeroDivisionError)) + self.assertTrue(issubclass(decimal.InvalidContext, InvalidOperation)) + +class CPythonAPItests(PythonAPItests): + decimal = C +class PyPythonAPItests(PythonAPItests): + decimal = P + +class ContextAPItests(unittest.TestCase): + + 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]) + + @cpython_only + @requires_legacy_unicode_capi + @warnings_helper.ignore_warnings(category=DeprecationWarning) + def test_from_legacy_strings(self): + import _testcapi + c = self.decimal.Context() + + for rnd in RoundingModes: + c.rounding = _testcapi.unicode_legacy_string(rnd) + self.assertEqual(c.rounding, rnd) + + s = _testcapi.unicode_legacy_string('') + self.assertRaises(TypeError, setattr, c, 'rounding', s) + + s = _testcapi.unicode_legacy_string('ROUND_\x00UP') + self.assertRaises(TypeError, setattr, c, 'rounding', s) + + 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') + +class CContextAPItests(ContextAPItests): + decimal = C +class PyContextAPItests(ContextAPItests): + decimal = P + +class ContextWithStatement(unittest.TestCase): + # 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_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 CContextWithStatement(ContextWithStatement): + decimal = C +class PyContextWithStatement(ContextWithStatement): + decimal = P + +class ContextFlags(unittest.TestCase): + + 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]) + +class CContextFlags(ContextFlags): + decimal = C +class PyContextFlags(ContextFlags): + decimal = P + +class SpecialContexts(unittest.TestCase): + """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 CSpecialContexts(SpecialContexts): + decimal = C +class PySpecialContexts(SpecialContexts): + decimal = P + +class ContextInputValidation(unittest.TestCase): + + 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 CContextInputValidation(ContextInputValidation): + decimal = C +class PyContextInputValidation(ContextInputValidation): + decimal = P + +class ContextSubclassing(unittest.TestCase): + + 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]) + +class CContextSubclassing(ContextSubclassing): + decimal = C +class PyContextSubclassing(ContextSubclassing): + decimal = P + +@skip_if_extra_functionality +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.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.__version__, P.__version__) + + self.assertEqual(dir(C), dir(P)) + + 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(unittest.TestCase): + + 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) + # 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) + +class CCoverage(Coverage): + decimal = C +class PyCoverage(Coverage): + decimal = P + +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') + c.prec = 201 + x = Decimal(2**578) ** Decimal("-0.5") + + 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_ieee_context(self): + # issue 8786: Add support for IEEE 754 contexts to decimal module. + IEEEContext = C.IEEEContext + DECIMAL32 = C.DECIMAL32 + DECIMAL64 = C.DECIMAL64 + DECIMAL128 = C.DECIMAL128 + + def assert_rest(self, context): + self.assertEqual(context.clamp, 1) + assert_signals(self, context, 'traps', []) + assert_signals(self, context, 'flags', []) + + c = IEEEContext(DECIMAL32) + self.assertEqual(c.prec, 7) + self.assertEqual(c.Emax, 96) + self.assertEqual(c.Emin, -95) + assert_rest(self, c) + + c = IEEEContext(DECIMAL64) + self.assertEqual(c.prec, 16) + self.assertEqual(c.Emax, 384) + self.assertEqual(c.Emin, -383) + assert_rest(self, c) + + c = IEEEContext(DECIMAL128) + self.assertEqual(c.prec, 34) + self.assertEqual(c.Emax, 6144) + self.assertEqual(c.Emin, -6143) + assert_rest(self, c) + + # Invalid values + self.assertRaises(OverflowError, IEEEContext, 2**63) + self.assertRaises(ValueError, IEEEContext, -1) + self.assertRaises(ValueError, IEEEContext, 1024) + + @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 + ) + + # IEEEContext + self.assertEqual(C.DECIMAL32, 32) + self.assertEqual(C.DECIMAL64, 64) + self.assertEqual(C.DECIMAL128, 128) + self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, 512) + + # 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.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)) + + # Issue 41540: + @unittest.skipIf(sys.platform.startswith("aix"), + "AIX: default ulimit: test is flaky because of extreme over-allocation") + @unittest.skipIf(check_sanitizer(address=True, memory=True), + "ASAN/MSAN sanitizer defaults to crashing " + "instead of returning NULL for malloc failure.") + def test_maxcontext_exact_arith(self): + + # Make sure that exact operations do not raise MemoryError due + # to huge intermediate values when the context precision is very + # large. + + # The following functions fill the available precision and are + # therefore not suitable for large precisions (by design of the + # specification). + MaxContextSkip = ['logical_invert', 'next_minus', 'next_plus', + 'logical_and', 'logical_or', 'logical_xor', + 'next_toward', 'rotate', 'shift'] + + Decimal = C.Decimal + Context = C.Context + localcontext = C.localcontext + + # Here only some functions that are likely candidates for triggering a + # MemoryError are tested. deccheck.py has an exhaustive test. + maxcontext = Context(prec=C.MAX_PREC, Emin=C.MIN_EMIN, Emax=C.MAX_EMAX) + with localcontext(maxcontext): + self.assertEqual(Decimal(0).exp(), 1) + self.assertEqual(Decimal(1).ln(), 0) + self.assertEqual(Decimal(1).log10(), 0) + self.assertEqual(Decimal(10**2).log10(), 2) + self.assertEqual(Decimal(10**223).log10(), 223) + self.assertEqual(Decimal(10**19).logb(), 19) + self.assertEqual(Decimal(4).sqrt(), 2) + self.assertEqual(Decimal("40E9").sqrt(), Decimal('2.0E+5')) + self.assertEqual(divmod(Decimal(10), 3), (3, 1)) + self.assertEqual(Decimal(10) // 3, 3) + self.assertEqual(Decimal(4) / 2, 2) + self.assertEqual(Decimal(400) ** -1, Decimal('0.0025')) + + +@requires_docstrings +@unittest.skipUnless(C, "test requires C version") +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') + + +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] + + # 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(CIBMTestCases, 'test_' + head, tester) + setattr(PyIBMTestCases, 'test_' + head, tester) + del filename, head, tail, tester + + + 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'].") + + +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_main(arith=False, verbose=True) + elif args: + test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug) + else: + test_main(arith=True, verbose=True) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index bc3d5bdc7..8b61f4feb 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -66,6 +66,9 @@ class GeneralFloatCases(unittest.TestCase): # See bpo-34087 self.assertRaises(ValueError, float, '\u3053\u3093\u306b\u3061\u306f') + def test_noargs(self): + self.assertEqual(float(), 0.0) + # TODO: RUSTPYTHON @unittest.expectedFailure def test_underscores(self): @@ -571,6 +574,25 @@ class GeneralFloatCases(unittest.TestCase): #self.assertTrue(0.0 < pow_op(2.0, -1047) < 1e-315) #self.assertTrue(0.0 > pow_op(-2.0, -1047) > -1e-315) + def test_hash(self): + for x in range(-30, 30): + self.assertEqual(hash(float(x)), hash(x)) + self.assertEqual(hash(float(sys.float_info.max)), + hash(int(sys.float_info.max))) + self.assertEqual(hash(float('inf')), sys.hash_info.inf) + self.assertEqual(hash(float('-inf')), -sys.hash_info.inf) + + def test_hash_nan(self): + value = float('nan') + self.assertEqual(hash(value), object.__hash__(value)) + class H: + def __hash__(self): + return 42 + class F(float, H): + pass + value = F('nan') + self.assertEqual(hash(value), object.__hash__(value)) + @requires_setformat class FormatFunctionsTestCase(unittest.TestCase): @@ -740,7 +762,7 @@ class FormatTestCase(unittest.TestCase): @unittest.expectedFailure @support.requires_IEEE_754 def test_format_testfile(self): - with open(format_testfile) as testfile: + with open(format_testfile, encoding="utf-8") as testfile: for line in testfile: if line.startswith('--'): continue @@ -782,7 +804,7 @@ class FormatTestCase(unittest.TestCase): class ReprTestCase(unittest.TestCase): def test_repr(self): with open(os.path.join(os.path.split(__file__)[0], - 'floating_points.txt')) as floats_file: + 'floating_points.txt'), encoding="utf-8") as floats_file: for line in floats_file: line = line.strip() if not line or line.startswith('#'): @@ -1186,10 +1208,10 @@ class HexFloatTestCase(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_from_hex(self): - MIN = self.MIN; - MAX = self.MAX; - TINY = self.TINY; - EPS = self.EPS; + MIN = self.MIN + MAX = self.MAX + TINY = self.TINY + EPS = self.EPS # two spellings of infinity, with optional signs; case-insensitive self.identical(fromHex('inf'), INF) @@ -1470,6 +1492,20 @@ class HexFloatTestCase(unittest.TestCase): self.identical(fromHex('0X1.0000000000001fp0'), 1.0+2*EPS) self.identical(fromHex('0x1.00000000000020p0'), 1.0+2*EPS) + # Regression test for a corner-case bug reported in b.p.o. 44954 + self.identical(fromHex('0x.8p-1074'), 0.0) + self.identical(fromHex('0x.80p-1074'), 0.0) + self.identical(fromHex('0x.81p-1074'), TINY) + self.identical(fromHex('0x8p-1078'), 0.0) + self.identical(fromHex('0x8.0p-1078'), 0.0) + self.identical(fromHex('0x8.1p-1078'), TINY) + self.identical(fromHex('0x80p-1082'), 0.0) + self.identical(fromHex('0x81p-1082'), TINY) + self.identical(fromHex('.8p-1074'), 0.0) + self.identical(fromHex('8p-1078'), 0.0) + self.identical(fromHex('-.8p-1074'), -0.0) + self.identical(fromHex('+8p-1078'), 0.0) + def test_roundtrip(self): def roundtrip(x): return fromHex(toHex(x)) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 94c96a941..851ad5b7c 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -4,6 +4,7 @@ import unittest import sys import pickle import itertools +from test.support import ALWAYS_EQ # pure Python implementations (3 args only), for comparison def pyrange(start, stop, step): @@ -90,6 +91,19 @@ class RangeTest(unittest.TestCase): r = range(-sys.maxsize, sys.maxsize, 2) self.assertEqual(len(r), sys.maxsize) + def test_range_constructor_error_messages(self): + with self.assertRaisesRegex( + TypeError, + "range expected at least 1 argument, got 0" + ): + range() + + with self.assertRaisesRegex( + TypeError, + "range expected at most 3 arguments, got 6" + ): + range(1, 2, 3, 4, 5, 6) + def test_large_operands(self): x = range(10**20, 10**20+10, 3) self.assertEqual(len(x), 4) @@ -289,11 +303,7 @@ class RangeTest(unittest.TestCase): self.assertRaises(ValueError, range(1, 2**100, 2).index, 2**87) self.assertEqual(range(1, 2**100, 2).index(2**87+1), 2**86) - class AlwaysEqual(object): - def __eq__(self, other): - return True - always_equal = AlwaysEqual() - self.assertEqual(range(10).index(always_equal), 0) + self.assertEqual(range(10).index(ALWAYS_EQ), 0) def test_user_index_method(self): bignum = 2*sys.maxsize @@ -344,11 +354,7 @@ class RangeTest(unittest.TestCase): self.assertEqual(range(1, 2**100, 2).count(2**87), 0) self.assertEqual(range(1, 2**100, 2).count(2**87+1), 1) - class AlwaysEqual(object): - def __eq__(self, other): - return True - always_equal = AlwaysEqual() - self.assertEqual(range(10).count(always_equal), 10) + self.assertEqual(range(10).count(ALWAYS_EQ), 10) self.assertEqual(len(range(sys.maxsize, sys.maxsize+10)), 10) @@ -368,26 +374,47 @@ class RangeTest(unittest.TestCase): list(r)) def test_iterator_pickling(self): - testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), - (13, 21, 3), (-2, 2, 2), (2**65, 2**65+2)] + testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), (13, 21, 3), + (-2, 2, 2)] + for M in 2**31, 2**63: + testcases += [ + (M-3, M-1), (4*M, 4*M+2), + (M-2, M-1, 2), (-M+1, -M, -2), + (1, 2, M-1), (-1, -2, -M), + (1, M-1, M-1), (-1, -M, -M), + ] for proto in range(pickle.HIGHEST_PROTOCOL + 1): for t in testcases: - it = itorg = iter(range(*t)) - data = list(range(*t)) + with self.subTest(proto=proto, t=t): + it = itorg = iter(range(*t)) + data = list(range(*t)) + d = pickle.dumps(it, proto) + it = pickle.loads(d) + self.assertEqual(type(itorg), type(it)) + self.assertEqual(list(it), data) + + it = pickle.loads(d) + try: + next(it) + except StopIteration: + continue + d = pickle.dumps(it, proto) + it = pickle.loads(d) + self.assertEqual(list(it), data[1:]) + + def test_iterator_pickling_overflowing_index(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(proto=proto): + it = iter(range(2**32 + 2)) + _, _, idx = it.__reduce__() + self.assertEqual(idx, 0) + it.__setstate__(2**32 + 1) # undocumented way to set r->index + _, _, idx = it.__reduce__() + self.assertEqual(idx, 2**32 + 1) d = pickle.dumps(it, proto) it = pickle.loads(d) - self.assertEqual(type(itorg), type(it)) - self.assertEqual(list(it), data) - - it = pickle.loads(d) - try: - next(it) - except StopIteration: - continue - d = pickle.dumps(it, proto) - it = pickle.loads(d) - self.assertEqual(list(it), data[1:]) + self.assertEqual(next(it), 2**32 + 1) def test_exhausted_iterator_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -429,9 +456,7 @@ class RangeTest(unittest.TestCase): self.assertIn(True, range(3)) self.assertIn(1+0j, range(3)) - class C1: - def __eq__(self, other): return True - self.assertIn(C1(), range(3)) + self.assertIn(ALWAYS_EQ, range(3)) # Objects are never coerced into other types for comparison. class C2: @@ -644,11 +669,17 @@ class RangeTest(unittest.TestCase): self.assert_attrs(range(0, 10, 3), 0, 10, 3) self.assert_attrs(range(10, 0, -1), 10, 0, -1) self.assert_attrs(range(10, 0, -3), 10, 0, -3) + self.assert_attrs(range(True), 0, 1, 1) + self.assert_attrs(range(False, True), 0, 1, 1) + self.assert_attrs(range(False, True, True), 0, 1, 1) def assert_attrs(self, rangeobj, start, stop, step): self.assertEqual(rangeobj.start, start) self.assertEqual(rangeobj.stop, stop) self.assertEqual(rangeobj.step, step) + self.assertIs(type(rangeobj.start), int) + self.assertIs(type(rangeobj.stop), int) + self.assertIs(type(rangeobj.step), int) with self.assertRaises(AttributeError): rangeobj.start = 0 diff --git a/Lib/test/test_richcmp.py b/Lib/test/test_richcmp.py index 32273ddae..58729a9fe 100644 --- a/Lib/test/test_richcmp.py +++ b/Lib/test/test_richcmp.py @@ -220,9 +220,6 @@ class MiscTest(unittest.TestCase): for func in (do, operator.not_): self.assertRaises(Exc, func, Bad()) - # TODO: RUSTPYTHON - import sys - @unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON, thread 'main' has overflowed its stack") @support.no_tracing def test_recursion(self): # Check that comparison for recursive objects fails gracefully From 3d27f163e4d2cba1802cc0b0920220809c79c342 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 03:42:21 +0900 Subject: [PATCH 24/26] adjust failing markers for test_{builtin,decimal,float,range} --- Lib/test/test_builtin.py | 17 +++++++++++++++++ Lib/test/test_decimal.py | 2 ++ Lib/test/test_float.py | 2 ++ Lib/test/test_range.py | 6 ++++++ 4 files changed, 27 insertions(+) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index a233fc3dd..92a60af70 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -153,6 +153,8 @@ class BuiltinTest(unittest.TestCase): it = pickle.loads(d) self.assertEqual(list(it), seq[1:]) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_import(self): __import__('sys') __import__('time') @@ -376,6 +378,8 @@ class BuiltinTest(unittest.TestCase): rv = ns['f']() self.assertEqual(rv, tuple(expected)) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_compile_top_level_await_no_coro(self): """Make sure top level non-await codes get the correct coroutine flags""" modes = ('single', 'exec') @@ -397,6 +401,8 @@ class BuiltinTest(unittest.TestCase): msg=f"source={source} mode={mode}") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_compile_top_level_await(self): """Test whether code some top level await can be compiled. @@ -455,6 +461,8 @@ class BuiltinTest(unittest.TestCase): finally: asyncio.set_event_loop_policy(policy) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_compile_top_level_await_invalid_cases(self): # helper function just to check we can run top=level async-for async def arange(n): @@ -493,6 +501,8 @@ class BuiltinTest(unittest.TestCase): asyncio.set_event_loop_policy(policy) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_compile_async_generator(self): """ With the PyCF_ALLOW_TOP_LEVEL_AWAIT flag added in 3.8, we want to @@ -1017,6 +1027,8 @@ class BuiltinTest(unittest.TestCase): m2 = map(map_char, "Is this the real life?") self.check_iter_pickle(m1, list(m2), proto) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_max(self): self.assertEqual(max('123123'), '3') self.assertEqual(max(1, 2, 3), 3) @@ -1076,6 +1088,8 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(max(data, key=f), sorted(reversed(data), key=f)[-1]) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_min(self): self.assertEqual(min('123123'), '1') self.assertEqual(min(1, 2, 3), 1) @@ -1218,6 +1232,7 @@ class BuiltinTest(unittest.TestCase): os.environ.clear() os.environ.update(old_environ) + @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON Windows') def test_open_non_inheritable(self): fileobj = open(__file__, encoding="utf-8") with fileobj: @@ -1868,6 +1883,8 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, tp, 1, 2) self.assertRaises(TypeError, tp, a=1, b=2) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_warning_notimplemented(self): # Issue #35712: NotImplemented is a sentinel value that should never # be evaluated in a boolean context (virtually all such use cases diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 19b4ea872..47e10bf2a 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -1853,6 +1853,8 @@ class UsabilityTest(unittest.TestCase): x = 1100 ** 1248 self.assertEqual(hashit(Decimal(x)), hashit(x)) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_hash_method_nan(self): Decimal = self.decimal.Decimal self.assertRaises(TypeError, hash, Decimal('sNaN')) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 8b61f4feb..73e5cab2b 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -582,6 +582,8 @@ class GeneralFloatCases(unittest.TestCase): self.assertEqual(hash(float('inf')), sys.hash_info.inf) self.assertEqual(hash(float('-inf')), -sys.hash_info.inf) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_hash_nan(self): value = float('nan') self.assertEqual(hash(value), object.__hash__(value)) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 851ad5b7c..8fcd67dac 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -91,6 +91,8 @@ class RangeTest(unittest.TestCase): r = range(-sys.maxsize, sys.maxsize, 2) self.assertEqual(len(r), sys.maxsize) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_range_constructor_error_messages(self): with self.assertRaisesRegex( TypeError, @@ -373,6 +375,8 @@ class RangeTest(unittest.TestCase): self.assertEqual(list(pickle.loads(pickle.dumps(r, proto))), list(r)) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_iterator_pickling(self): testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), (13, 21, 3), (-2, 2, 2)] @@ -660,6 +664,8 @@ class RangeTest(unittest.TestCase): range(0) >= range(0) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_attributes(self): # test the start, stop and step attributes of range objects self.assert_attrs(range(0), 0, 0, 1) From f3acd688a4feb421dbff8a6f6bccd174b1915d3f Mon Sep 17 00:00:00 2001 From: CPython developers <> Date: Mon, 18 Jul 2022 03:58:05 +0900 Subject: [PATCH 25/26] update {test_}xmlrpc from CPython 3.10.5 --- Lib/test/test_xmlrpc.py | 85 ++++++++++++++++++++++++++++++----------- Lib/xmlrpc/client.py | 38 ++++++++++++------ Lib/xmlrpc/server.py | 4 +- 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 110f52bcb..6b60c25e3 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -18,6 +18,7 @@ from test import support from test.support import os_helper from test.support import socket_helper from test.support import threading_helper +from test.support import ALWAYS_EQ, LARGEST, SMALLEST try: import gzip @@ -559,14 +560,10 @@ class DateTimeTestCase(unittest.TestCase): # some other types dbytes = dstr.encode('ascii') dtuple = now.timetuple() - with self.assertRaises(TypeError): - dtime == 1970 - with self.assertRaises(TypeError): - dtime != dbytes - with self.assertRaises(TypeError): - dtime == bytearray(dbytes) - with self.assertRaises(TypeError): - dtime != dtuple + self.assertFalse(dtime == 1970) + self.assertTrue(dtime != dbytes) + self.assertFalse(dtime == bytearray(dbytes)) + self.assertTrue(dtime != dtuple) with self.assertRaises(TypeError): dtime < float(1970) with self.assertRaises(TypeError): @@ -576,9 +573,21 @@ class DateTimeTestCase(unittest.TestCase): with self.assertRaises(TypeError): dtime >= dtuple + self.assertTrue(dtime == ALWAYS_EQ) + self.assertFalse(dtime != ALWAYS_EQ) + self.assertTrue(dtime < LARGEST) + self.assertFalse(dtime > LARGEST) + self.assertTrue(dtime <= LARGEST) + self.assertFalse(dtime >= LARGEST) + self.assertFalse(dtime < SMALLEST) + self.assertTrue(dtime > SMALLEST) + self.assertFalse(dtime <= SMALLEST) + self.assertTrue(dtime >= SMALLEST) + + class BinaryTestCase(unittest.TestCase): - # XXX What should str(Binary(b"\xff")) return? I'm chosing "\xff" + # XXX What should str(Binary(b"\xff")) return? I'm choosing "\xff" # for now (i.e. interpreting the binary data as Latin-1-encoded # text). But this feels very unsatisfactory. Perhaps we should # only define repr(), and return r"Binary(b'\xff')" instead? @@ -665,7 +674,7 @@ def http_server(evt, numrequests, requestHandler=None, encoding=None): serv.handle_request() numrequests -= 1 - except socket.timeout: + except TimeoutError: pass finally: serv.socket.close() @@ -715,11 +724,16 @@ def http_multi_server(evt, numrequests, requestHandler=None): #on AF_INET only. URL = "http://%s:%d"%(ADDR, PORT) serv.server_activate() - paths = ["/foo", "/foo/bar"] + paths = [ + "/foo", "/foo/bar", + "/foo?k=v", "/foo#frag", "/foo?k=v#frag", + "", "/", "/RPC2", "?k=v", "#frag", + ] for path in paths: d = serv.add_dispatcher(path, xmlrpc.server.SimpleXMLRPCDispatcher()) d.register_introspection_functions() d.register_multicall_functions() + d.register_function(lambda p=path: p, 'test') serv.get_dispatcher(paths[0]).register_function(pow) serv.get_dispatcher(paths[1]).register_function(lambda x,y: x+y, 'add') serv.add_dispatcher("/is/broken", BrokenDispatcher()) @@ -730,7 +744,7 @@ def http_multi_server(evt, numrequests, requestHandler=None): serv.handle_request() numrequests -= 1 - except socket.timeout: + except TimeoutError: pass finally: serv.socket.close() @@ -1073,6 +1087,39 @@ class MultiPathServerTestCase(BaseServerTestCase): p = xmlrpclib.ServerProxy(URL+"/is/broken") self.assertRaises(xmlrpclib.Fault, p.add, 6, 8) + def test_invalid_path(self): + p = xmlrpclib.ServerProxy(URL+"/invalid") + self.assertRaises(xmlrpclib.Fault, p.add, 6, 8) + + def test_path_query_fragment(self): + p = xmlrpclib.ServerProxy(URL+"/foo?k=v#frag") + self.assertEqual(p.test(), "/foo?k=v#frag") + + def test_path_fragment(self): + p = xmlrpclib.ServerProxy(URL+"/foo#frag") + self.assertEqual(p.test(), "/foo#frag") + + def test_path_query(self): + p = xmlrpclib.ServerProxy(URL+"/foo?k=v") + self.assertEqual(p.test(), "/foo?k=v") + + def test_empty_path(self): + p = xmlrpclib.ServerProxy(URL) + self.assertEqual(p.test(), "/RPC2") + + def test_root_path(self): + p = xmlrpclib.ServerProxy(URL + "/") + self.assertEqual(p.test(), "/") + + def test_empty_path_query(self): + p = xmlrpclib.ServerProxy(URL + "?k=v") + self.assertEqual(p.test(), "?k=v") + + def test_empty_path_fragment(self): + p = xmlrpclib.ServerProxy(URL + "#frag") + self.assertEqual(p.test(), "#frag") + + #A test case that verifies that a server using the HTTP/1.1 keep-alive mechanism #does indeed serve subsequent requests on the same connection class BaseKeepaliveServerTestCase(BaseServerTestCase): @@ -1544,16 +1591,10 @@ class UseBuiltinTypesTestCase(unittest.TestCase): self.assertTrue(server.use_builtin_types) -@threading_helper.reap_threads -def test_main(): - support.run_unittest(XMLRPCTestCase, HelperTestCase, DateTimeTestCase, - BinaryTestCase, FaultTestCase, UseBuiltinTypesTestCase, - SimpleServerTestCase, SimpleServerEncodingTestCase, - KeepaliveServerTestCase1, KeepaliveServerTestCase2, - GzipServerTestCase, GzipUtilTestCase, HeadersServerTestCase, - MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase, - CGIHandlerTestCase, SimpleXMLRPCDispatcherTestCase) +def setUpModule(): + thread_info = threading_helper.threading_setup() + unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info) if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index f01b5ae78..121e44023 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -265,16 +265,22 @@ boolean = Boolean = bool # Issue #13305: different format codes across platforms _day0 = datetime(1, 1, 1) -if _day0.strftime('%Y') == '0001': # Mac OS X +def _try(fmt): + try: + return _day0.strftime(fmt) == '0001' + except ValueError: + return False +if _try('%Y'): # Mac OS X def _iso8601_format(value): return value.strftime("%Y%m%dT%H:%M:%S") -elif _day0.strftime('%4Y') == '0001': # Linux +elif _try('%4Y'): # Linux def _iso8601_format(value): return value.strftime("%4Y%m%dT%H:%M:%S") else: def _iso8601_format(value): return value.strftime("%Y%m%dT%H:%M:%S").zfill(17) del _day0 +del _try def _strftime(value): @@ -314,31 +320,38 @@ class DateTime: s = self.timetuple() o = other.timetuple() else: - otype = (hasattr(other, "__class__") - and other.__class__.__name__ - or type(other)) - raise TypeError("Can't compare %s and %s" % - (self.__class__.__name__, otype)) + s = self + o = NotImplemented return s, o def __lt__(self, other): s, o = self.make_comparable(other) + if o is NotImplemented: + return NotImplemented return s < o def __le__(self, other): s, o = self.make_comparable(other) + if o is NotImplemented: + return NotImplemented return s <= o def __gt__(self, other): s, o = self.make_comparable(other) + if o is NotImplemented: + return NotImplemented return s > o def __ge__(self, other): s, o = self.make_comparable(other) + if o is NotImplemented: + return NotImplemented return s >= o def __eq__(self, other): s, o = self.make_comparable(other) + if o is NotImplemented: + return NotImplemented return s == o def timetuple(self): @@ -436,7 +449,7 @@ class ExpatParser: target.xml(encoding, None) def feed(self, data): - self._parser.Parse(data, 0) + self._parser.Parse(data, False) def close(self): try: @@ -1415,15 +1428,16 @@ class ServerProxy: # establish a "logical" server connection # get the url - type, uri = urllib.parse._splittype(uri) - if type not in ("http", "https"): + p = urllib.parse.urlsplit(uri) + if p.scheme not in ("http", "https"): raise OSError("unsupported XML-RPC protocol") - self.__host, self.__handler = urllib.parse._splithost(uri) + self.__host = p.netloc + self.__handler = urllib.parse.urlunsplit(["", "", *p[2:]]) if not self.__handler: self.__handler = "/RPC2" if transport is None: - if type == "https": + if p.scheme == "https": handler = SafeTransport extra_kwargs = {"context": context} else: diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py index 32aba4df4..69a260f5b 100644 --- a/Lib/xmlrpc/server.py +++ b/Lib/xmlrpc/server.py @@ -732,7 +732,7 @@ class ServerHTMLDoc(pydoc.HTMLDoc): # hyperlinking of arbitrary strings being used as method # names. Only methods with names consisting of word characters # and '.'s are hyperlinked. - pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' + pattern = re.compile(r'\b((http|https|ftp)://\S+[\w/]|' r'RFC[- ]?(\d+)|' r'PEP[- ]?(\d+)|' r'(self\.)?((?:\w|\.)+))\b') @@ -750,7 +750,7 @@ class ServerHTMLDoc(pydoc.HTMLDoc): url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) results.append('%s' % (url, escape(all))) elif pep: - url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) + url = 'https://www.python.org/dev/peps/pep-%04d/' % int(pep) results.append('%s' % (url, escape(all))) elif text[end:end+1] == '(': results.append(self.namelink(name, methods, funcs, classes)) From 6729417624dff9e5f7ca52cfcaf5710533882a91 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 18 Jul 2022 04:02:16 +0900 Subject: [PATCH 26/26] adjust failing markers for test_xmlrpc --- Lib/test/test_xmlrpc.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 6b60c25e3..25a4be0e0 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -1087,34 +1087,50 @@ class MultiPathServerTestCase(BaseServerTestCase): p = xmlrpclib.ServerProxy(URL+"/is/broken") self.assertRaises(xmlrpclib.Fault, p.add, 6, 8) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_invalid_path(self): p = xmlrpclib.ServerProxy(URL+"/invalid") self.assertRaises(xmlrpclib.Fault, p.add, 6, 8) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_path_query_fragment(self): p = xmlrpclib.ServerProxy(URL+"/foo?k=v#frag") self.assertEqual(p.test(), "/foo?k=v#frag") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_path_fragment(self): p = xmlrpclib.ServerProxy(URL+"/foo#frag") self.assertEqual(p.test(), "/foo#frag") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_path_query(self): p = xmlrpclib.ServerProxy(URL+"/foo?k=v") self.assertEqual(p.test(), "/foo?k=v") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_empty_path(self): p = xmlrpclib.ServerProxy(URL) self.assertEqual(p.test(), "/RPC2") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_root_path(self): p = xmlrpclib.ServerProxy(URL + "/") self.assertEqual(p.test(), "/") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_empty_path_query(self): p = xmlrpclib.ServerProxy(URL + "?k=v") self.assertEqual(p.test(), "?k=v") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_empty_path_fragment(self): p = xmlrpclib.ServerProxy(URL + "#frag") self.assertEqual(p.test(), "#frag")