mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	up the decimal module. Performance gains of the new C implementation are between 12x and 80x, depending on the application.
		
			
				
	
	
		
			344 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			344 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#
 | 
						|
# Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
 | 
						|
#
 | 
						|
# Redistribution and use in source and binary forms, with or without
 | 
						|
# modification, are permitted provided that the following conditions
 | 
						|
# are met:
 | 
						|
#
 | 
						|
# 1. Redistributions of source code must retain the above copyright
 | 
						|
#    notice, this list of conditions and the following disclaimer.
 | 
						|
#
 | 
						|
# 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
#    notice, this list of conditions and the following disclaimer in the
 | 
						|
#    documentation and/or other materials provided with the distribution.
 | 
						|
#
 | 
						|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
 | 
						|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
						|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
						|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
						|
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
						|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
						|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
						|
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
						|
# SUCH DAMAGE.
 | 
						|
#
 | 
						|
 | 
						|
 | 
						|
# Generate PEP-3101 format strings.
 | 
						|
 | 
						|
 | 
						|
import os, sys, locale, random
 | 
						|
import platform, subprocess
 | 
						|
from test.support import import_fresh_module
 | 
						|
from distutils.spawn import find_executable
 | 
						|
 | 
						|
C = import_fresh_module('decimal', fresh=['_decimal'])
 | 
						|
P = import_fresh_module('decimal', blocked=['_decimal'])
 | 
						|
 | 
						|
 | 
						|
windows_lang_strings = [
 | 
						|
  "chinese", "chinese-simplified", "chinese-traditional", "czech", "danish",
 | 
						|
  "dutch", "belgian", "english", "australian", "canadian", "english-nz",
 | 
						|
  "english-uk", "english-us", "finnish", "french", "french-belgian",
 | 
						|
  "french-canadian", "french-swiss", "german", "german-austrian",
 | 
						|
  "german-swiss", "greek", "hungarian", "icelandic", "italian", "italian-swiss",
 | 
						|
  "japanese", "korean", "norwegian", "norwegian-bokmal", "norwegian-nynorsk",
 | 
						|
  "polish", "portuguese", "portuguese-brazil", "russian", "slovak", "spanish",
 | 
						|
  "spanish-mexican", "spanish-modern", "swedish", "turkish",
 | 
						|
]
 | 
						|
 | 
						|
preferred_encoding = {
 | 
						|
  'cs_CZ': 'ISO8859-2',
 | 
						|
  'cs_CZ.iso88592': 'ISO8859-2',
 | 
						|
  'czech': 'ISO8859-2',
 | 
						|
  'eesti': 'ISO8859-1',
 | 
						|
  'estonian': 'ISO8859-1',
 | 
						|
  'et_EE': 'ISO8859-15',
 | 
						|
  'et_EE.ISO-8859-15': 'ISO8859-15',
 | 
						|
  'et_EE.iso885915': 'ISO8859-15',
 | 
						|
  'et_EE.iso88591': 'ISO8859-1',
 | 
						|
  'fi_FI.iso88591': 'ISO8859-1',
 | 
						|
  'fi_FI': 'ISO8859-15',
 | 
						|
  'fi_FI@euro': 'ISO8859-15',
 | 
						|
  'fi_FI.iso885915@euro': 'ISO8859-15',
 | 
						|
  'finnish': 'ISO8859-1',
 | 
						|
  'lv_LV': 'ISO8859-13',
 | 
						|
  'lv_LV.iso885913': 'ISO8859-13',
 | 
						|
  'nb_NO': 'ISO8859-1',
 | 
						|
  'nb_NO.iso88591': 'ISO8859-1',
 | 
						|
  'bokmal': 'ISO8859-1',
 | 
						|
  'nn_NO': 'ISO8859-1',
 | 
						|
  'nn_NO.iso88591': 'ISO8859-1',
 | 
						|
  'no_NO': 'ISO8859-1',
 | 
						|
  'norwegian': 'ISO8859-1',
 | 
						|
  'nynorsk': 'ISO8859-1',
 | 
						|
  'ru_RU': 'ISO8859-5',
 | 
						|
  'ru_RU.iso88595': 'ISO8859-5',
 | 
						|
  'russian': 'ISO8859-5',
 | 
						|
  'ru_RU.KOI8-R': 'KOI8-R',
 | 
						|
  'ru_RU.koi8r': 'KOI8-R',
 | 
						|
  'ru_RU.CP1251': 'CP1251',
 | 
						|
  'ru_RU.cp1251': 'CP1251',
 | 
						|
  'sk_SK': 'ISO8859-2',
 | 
						|
  'sk_SK.iso88592': 'ISO8859-2',
 | 
						|
  'slovak': 'ISO8859-2',
 | 
						|
  'sv_FI': 'ISO8859-1',
 | 
						|
  'sv_FI.iso88591': 'ISO8859-1',
 | 
						|
  'sv_FI@euro': 'ISO8859-15',
 | 
						|
  'sv_FI.iso885915@euro': 'ISO8859-15',
 | 
						|
  'uk_UA': 'KOI8-U',
 | 
						|
  'uk_UA.koi8u': 'KOI8-U'
 | 
						|
}
 | 
						|
 | 
						|
integers = [
 | 
						|
  "",
 | 
						|
  "1",
 | 
						|
  "12",
 | 
						|
  "123",
 | 
						|
  "1234",
 | 
						|
  "12345",
 | 
						|
  "123456",
 | 
						|
  "1234567",
 | 
						|
  "12345678",
 | 
						|
  "123456789",
 | 
						|
  "1234567890",
 | 
						|
  "12345678901",
 | 
						|
  "123456789012",
 | 
						|
  "1234567890123",
 | 
						|
  "12345678901234",
 | 
						|
  "123456789012345",
 | 
						|
  "1234567890123456",
 | 
						|
  "12345678901234567",
 | 
						|
  "123456789012345678",
 | 
						|
  "1234567890123456789",
 | 
						|
  "12345678901234567890",
 | 
						|
  "123456789012345678901",
 | 
						|
  "1234567890123456789012",
 | 
						|
]
 | 
						|
 | 
						|
numbers = [
 | 
						|
  "0", "-0", "+0",
 | 
						|
  "0.0", "-0.0", "+0.0",
 | 
						|
  "0e0", "-0e0", "+0e0",
 | 
						|
  ".0", "-.0",
 | 
						|
  ".1", "-.1",
 | 
						|
  "1.1", "-1.1",
 | 
						|
  "1e1", "-1e1"
 | 
						|
]
 | 
						|
 | 
						|
# Get the list of available locales.
 | 
						|
if platform.system() == 'Windows':
 | 
						|
    locale_list = windows_lang_strings
 | 
						|
else:
 | 
						|
    locale_list = ['C']
 | 
						|
    if os.path.isfile("/var/lib/locales/supported.d/local"):
 | 
						|
        # On Ubuntu, `locale -a` gives the wrong case for some locales,
 | 
						|
        # so we get the correct names directly:
 | 
						|
        with open("/var/lib/locales/supported.d/local") as f:
 | 
						|
            locale_list = [loc.split()[0] for loc in f.readlines() \
 | 
						|
                           if not loc.startswith('#')]
 | 
						|
    elif find_executable('locale'):
 | 
						|
        locale_list = subprocess.Popen(["locale", "-a"],
 | 
						|
                          stdout=subprocess.PIPE).communicate()[0]
 | 
						|
        try:
 | 
						|
            locale_list = locale_list.decode()
 | 
						|
        except UnicodeDecodeError:
 | 
						|
            # Some distributions insist on using latin-1 characters
 | 
						|
            # in their locale names.
 | 
						|
            locale_list = locale_list.decode('latin-1')
 | 
						|
        locale_list = locale_list.split('\n')
 | 
						|
try:
 | 
						|
    locale_list.remove('')
 | 
						|
except ValueError:
 | 
						|
    pass
 | 
						|
 | 
						|
# Debian
 | 
						|
if os.path.isfile("/etc/locale.alias"):
 | 
						|
    with open("/etc/locale.alias") as f:
 | 
						|
        while 1:
 | 
						|
            try:
 | 
						|
                line = f.readline()
 | 
						|
            except UnicodeDecodeError:
 | 
						|
                continue
 | 
						|
            if line == "":
 | 
						|
                break
 | 
						|
            if line.startswith('#'):
 | 
						|
                continue
 | 
						|
            x = line.split()
 | 
						|
            if len(x) == 2:
 | 
						|
                if x[0] in locale_list:
 | 
						|
                    locale_list.remove(x[0])
 | 
						|
 | 
						|
# FreeBSD
 | 
						|
if platform.system() == 'FreeBSD':
 | 
						|
    # http://www.freebsd.org/cgi/query-pr.cgi?pr=142173
 | 
						|
    # en_GB.US-ASCII has 163 as the currency symbol.
 | 
						|
    for loc in ['it_CH.ISO8859-1', 'it_CH.ISO8859-15', 'it_CH.UTF-8',
 | 
						|
                'it_IT.ISO8859-1', 'it_IT.ISO8859-15', 'it_IT.UTF-8',
 | 
						|
                'sl_SI.ISO8859-2', 'sl_SI.UTF-8',
 | 
						|
                'en_GB.US-ASCII']:
 | 
						|
        try:
 | 
						|
            locale_list.remove(loc)
 | 
						|
        except ValueError:
 | 
						|
            pass
 | 
						|
 | 
						|
# Print a testcase in the format of the IBM tests (for runtest.c):
 | 
						|
def get_preferred_encoding():
 | 
						|
    loc = locale.setlocale(locale.LC_CTYPE)
 | 
						|
    if loc in preferred_encoding:
 | 
						|
        return preferred_encoding[loc]
 | 
						|
    else:
 | 
						|
        return locale.getpreferredencoding()
 | 
						|
 | 
						|
def printit(testno, s, fmt, encoding=None):
 | 
						|
    if not encoding:
 | 
						|
        encoding = get_preferred_encoding()
 | 
						|
    try:
 | 
						|
        result = format(P.Decimal(s), fmt)
 | 
						|
        fmt = str(fmt.encode(encoding))[2:-1]
 | 
						|
        result = str(result.encode(encoding))[2:-1]
 | 
						|
        if "'" in result:
 | 
						|
            sys.stdout.write("xfmt%d  format  %s  '%s'  ->  \"%s\"\n"
 | 
						|
                             % (testno, s, fmt, result))
 | 
						|
        else:
 | 
						|
            sys.stdout.write("xfmt%d  format  %s  '%s'  ->  '%s'\n"
 | 
						|
                             % (testno, s, fmt, result))
 | 
						|
    except Exception as err:
 | 
						|
        sys.stderr.write("%s  %s  %s\n" % (err, s, fmt))
 | 
						|
 | 
						|
 | 
						|
# Check if an integer can be converted to a valid fill character.
 | 
						|
def check_fillchar(i):
 | 
						|
    try:
 | 
						|
        c = chr(i)
 | 
						|
        c.encode('utf-8').decode()
 | 
						|
        format(P.Decimal(0), c + '<19g')
 | 
						|
        if c in ("'", '"', '\\'):
 | 
						|
            return None
 | 
						|
        return c
 | 
						|
    except:
 | 
						|
        return None
 | 
						|
 | 
						|
# Generate all unicode characters that are accepted as
 | 
						|
# fill characters by decimal.py.
 | 
						|
def all_fillchars():
 | 
						|
    for i in range(32, 0x110002):
 | 
						|
        c = check_fillchar(i)
 | 
						|
        if c: yield c
 | 
						|
 | 
						|
# Return random fill character.
 | 
						|
def rand_fillchar():
 | 
						|
    while 1:
 | 
						|
        i = random.randrange(32, 0x110002)
 | 
						|
        c = check_fillchar(i)
 | 
						|
        if c: return c
 | 
						|
 | 
						|
# Generate random format strings
 | 
						|
# [[fill]align][sign][#][0][width][.precision][type]
 | 
						|
def rand_format(fill, typespec='EeGgFfn%'):
 | 
						|
    active = sorted(random.sample(range(7), random.randrange(8)))
 | 
						|
    have_align = 0
 | 
						|
    s = ''
 | 
						|
    for elem in active:
 | 
						|
        if elem == 0: # fill+align
 | 
						|
            s += fill
 | 
						|
            s += random.choice('<>=^')
 | 
						|
            have_align = 1
 | 
						|
        elif elem == 1: # sign
 | 
						|
            s += random.choice('+- ')
 | 
						|
        elif elem == 2 and not have_align: # zeropad
 | 
						|
            s += '0'
 | 
						|
        elif elem == 3: # width
 | 
						|
            s += str(random.randrange(1, 100))
 | 
						|
        elif elem == 4: # thousands separator
 | 
						|
            s += ','
 | 
						|
        elif elem == 5: # prec
 | 
						|
            s += '.'
 | 
						|
            s += str(random.randrange(100))
 | 
						|
        elif elem == 6:
 | 
						|
            if 4 in active: c = typespec.replace('n', '')
 | 
						|
            else: c = typespec
 | 
						|
            s += random.choice(c)
 | 
						|
    return s
 | 
						|
 | 
						|
# Partially brute force all possible format strings containing a thousands
 | 
						|
# separator. Fall back to random where the runtime would become excessive.
 | 
						|
# [[fill]align][sign][#][0][width][,][.precision][type]
 | 
						|
def all_format_sep():
 | 
						|
    for align in ('', '<', '>', '=', '^'):
 | 
						|
        for fill in ('', 'x'):
 | 
						|
            if align == '': fill = ''
 | 
						|
            for sign in ('', '+', '-', ' '):
 | 
						|
                for zeropad in ('', '0'):
 | 
						|
                    if align != '': zeropad = ''
 | 
						|
                    for width in ['']+[str(y) for y in range(1, 15)]+['101']:
 | 
						|
                        for prec in ['']+['.'+str(y) for y in range(15)]:
 | 
						|
                            # for type in ('', 'E', 'e', 'G', 'g', 'F', 'f', '%'):
 | 
						|
                            type = random.choice(('', 'E', 'e', 'G', 'g', 'F', 'f', '%'))
 | 
						|
                            yield ''.join((fill, align, sign, zeropad, width, ',', prec, type))
 | 
						|
 | 
						|
# Partially brute force all possible format strings with an 'n' specifier.
 | 
						|
# [[fill]align][sign][#][0][width][,][.precision][type]
 | 
						|
def all_format_loc():
 | 
						|
    for align in ('', '<', '>', '=', '^'):
 | 
						|
        for fill in ('', 'x'):
 | 
						|
            if align == '': fill = ''
 | 
						|
            for sign in ('', '+', '-', ' '):
 | 
						|
                for zeropad in ('', '0'):
 | 
						|
                    if align != '': zeropad = ''
 | 
						|
                    for width in ['']+[str(y) for y in range(1, 20)]+['101']:
 | 
						|
                        for prec in ['']+['.'+str(y) for y in range(1, 20)]:
 | 
						|
                            yield ''.join((fill, align, sign, zeropad, width, prec, 'n'))
 | 
						|
 | 
						|
# Generate random format strings with a unicode fill character
 | 
						|
# [[fill]align][sign][#][0][width][,][.precision][type]
 | 
						|
def randfill(fill):
 | 
						|
    active = sorted(random.sample(range(5), random.randrange(6)))
 | 
						|
    s = ''
 | 
						|
    s += str(fill)
 | 
						|
    s += random.choice('<>=^')
 | 
						|
    for elem in active:
 | 
						|
        if elem == 0: # sign
 | 
						|
            s += random.choice('+- ')
 | 
						|
        elif elem == 1: # width
 | 
						|
            s += str(random.randrange(1, 100))
 | 
						|
        elif elem == 2: # thousands separator
 | 
						|
            s += ','
 | 
						|
        elif elem == 3: # prec
 | 
						|
            s += '.'
 | 
						|
            s += str(random.randrange(100))
 | 
						|
        elif elem == 4:
 | 
						|
            if 2 in active: c = 'EeGgFf%'
 | 
						|
            else: c = 'EeGgFfn%'
 | 
						|
            s += random.choice(c)
 | 
						|
    return s
 | 
						|
 | 
						|
# Generate random format strings with random locale setting
 | 
						|
# [[fill]align][sign][#][0][width][,][.precision][type]
 | 
						|
def rand_locale():
 | 
						|
    try:
 | 
						|
        loc = random.choice(locale_list)
 | 
						|
        locale.setlocale(locale.LC_ALL, loc)
 | 
						|
    except locale.Error as err:
 | 
						|
        pass
 | 
						|
    active = sorted(random.sample(range(5), random.randrange(6)))
 | 
						|
    s = ''
 | 
						|
    have_align = 0
 | 
						|
    for elem in active:
 | 
						|
        if elem == 0: # fill+align
 | 
						|
            s += chr(random.randrange(32, 128))
 | 
						|
            s += random.choice('<>=^')
 | 
						|
            have_align = 1
 | 
						|
        elif elem == 1: # sign
 | 
						|
            s += random.choice('+- ')
 | 
						|
        elif elem == 2 and not have_align: # zeropad
 | 
						|
            s += '0'
 | 
						|
        elif elem == 3: # width
 | 
						|
            s += str(random.randrange(1, 100))
 | 
						|
        elif elem == 4: # prec
 | 
						|
            s += '.'
 | 
						|
            s += str(random.randrange(100))
 | 
						|
    s += 'n'
 | 
						|
    return s
 |