1562 lines · 71.3 KB · Python
RawDownload
""" Locale support.     The module provides low-level access to the C lib's locale APIs    and adds high level number formatting APIs as well as a locale    aliasing engine to complement these.     The aliasing engine includes support for many commonly used locale    names and maps them to values suitable for passing to the C lib's    setlocale() function. It also includes default encodings for all    supported locale names. """ import sys, encodings, encodings.aliases # Try importing the _locale module.## If this fails, fall back on a basic 'C' locale emulation. # Yuck:  LC_MESSAGES is non-standard:  can't tell whether it exists before# trying the import.  So __all__ is also fiddled at the end of the file.__all__ = ["setlocale","Error","localeconv","strcoll","strxfrm",           "format","str","atof","atoi","LC_CTYPE","LC_COLLATE",           "LC_TIME","LC_MONETARY","LC_NUMERIC", "LC_ALL","CHAR_MAX"] try:     from _locale import * except ImportError:     # Locale emulation     CHAR_MAX = 127    LC_ALL = 6    LC_COLLATE = 3    LC_CTYPE = 0    LC_MESSAGES = 5    LC_MONETARY = 4    LC_NUMERIC = 1    LC_TIME = 2    Error = ValueError     def localeconv():        """ localeconv() -> dict.            Returns numeric and monetary locale-specific parameters.        """        # 'C' locale default values        return {'grouping': [127],                'currency_symbol': '',                'n_sign_posn': 127,                'p_cs_precedes': 127,                'n_cs_precedes': 127,                'mon_grouping': [],                'n_sep_by_space': 127,                'decimal_point': '.',                'negative_sign': '',                'positive_sign': '',                'p_sep_by_space': 127,                'int_curr_symbol': '',                'p_sign_posn': 127,                'thousands_sep': '',                'mon_thousands_sep': '',                'frac_digits': 127,                'mon_decimal_point': '',                'int_frac_digits': 127}     def setlocale(category, value=None):        """ setlocale(integer,string=None) -> string.            Activates/queries locale processing.        """        if value not in (None, '', 'C'):            raise Error, '_locale emulation only supports "C" locale'        return 'C'     def strcoll(a,b):        """ strcoll(string,string) -> int.            Compares two strings according to the locale.        """        return cmp(a,b)     def strxfrm(s):        """ strxfrm(string) -> string.            Returns a string that behaves for cmp locale-aware.        """        return s ### Number formatting APIs # Author: Martin von Loewis# improved by Georg Brandl #perform the grouping from right to leftdef _group(s, monetary=False):    conv = localeconv()    thousands_sep = conv[monetary and 'mon_thousands_sep' or 'thousands_sep']    grouping = conv[monetary and 'mon_grouping' or 'grouping']    if not grouping:        return (s, 0)    result = ""    seps = 0    spaces = ""    if s[-1] == ' ':        sp = s.find(' ')        spaces = s[sp:]        s = s[:sp]    while s and grouping:        # if grouping is -1, we are done        if grouping[0] == CHAR_MAX:            break        # 0: re-use last group ad infinitum        elif grouping[0] != 0:            #process last group            group = grouping[0]            grouping = grouping[1:]        if result:            result = s[-group:] + thousands_sep + result            seps += 1        else:            result = s[-group:]        s = s[:-group]        if s and s[-1] not in "0123456789":            # the leading string is only spaces and signs            return s + result + spaces, seps    if not result:        return s + spaces, seps    if s:        result = s + thousands_sep + result        seps += 1    return result + spaces, seps def format(percent, value, grouping=False, monetary=False, *additional):    """Returns the locale-aware substitution of a %? specifier    (percent).     additional is for format strings which contain one or more    '*' modifiers."""    # this is only for one-percent-specifier strings and this should be checked    if percent[0] != '%':        raise ValueError("format() must be given exactly one %char "                         "format specifier")    if additional:        formatted = percent % ((value,) + additional)    else:        formatted = percent % value    # floats and decimal ints need special action!    if percent[-1] in 'eEfFgG':        seps = 0        parts = formatted.split('.')        if grouping:            parts[0], seps = _group(parts[0], monetary=monetary)        decimal_point = localeconv()[monetary and 'mon_decimal_point'                                              or 'decimal_point']        formatted = decimal_point.join(parts)        while seps:            sp = formatted.find(' ')            if sp == -1: break            formatted = formatted[:sp] + formatted[sp+1:]            seps -= 1    elif percent[-1] in 'diu':        if grouping:            formatted = _group(formatted, monetary=monetary)[0]    return formatted import re, operator_percent_re = re.compile(r'%(?:\((?P<key>.*?)\))?'                         r'(?P<modifiers>[-#0-9 +*.hlL]*?)[eEfFgGdiouxXcrs%]') def format_string(f, val, grouping=False):    """Formats a string in the same way that the % formatting would use,    but takes the current locale into account.    Grouping is applied if the third parameter is true."""    percents = list(_percent_re.finditer(f))    new_f = _percent_re.sub('%s', f)     if isinstance(val, tuple):        new_val = list(val)        i = 0        for perc in percents:            starcount = perc.group('modifiers').count('*')            new_val[i] = format(perc.group(), new_val[i], grouping, False, *new_val[i+1:i+1+starcount])            del new_val[i+1:i+1+starcount]            i += (1 + starcount)        val = tuple(new_val)    elif operator.isMappingType(val):        for perc in percents:            key = perc.group("key")            val[key] = format(perc.group(), val[key], grouping)    else:        # val is a single value        val = format(percents[0].group(), val, grouping)     return new_f % val def currency(val, symbol=True, grouping=False, international=False):    """Formats val according to the currency settings    in the current locale."""    conv = localeconv()     # check for illegal values    digits = conv[international and 'int_frac_digits' or 'frac_digits']    if digits == 127:        raise ValueError("Currency formatting is not possible using "                         "the 'C' locale.")     s = format('%%.%if' % digits, abs(val), grouping, monetary=True)    # '<' and '>' are markers if the sign must be inserted between symbol and value    s = '<' + s + '>'     if symbol:        smb = conv[international and 'int_curr_symbol' or 'currency_symbol']        precedes = conv[val<0 and 'n_cs_precedes' or 'p_cs_precedes']        separated = conv[val<0 and 'n_sep_by_space' or 'p_sep_by_space']         if precedes:            s = smb + (separated and ' ' or '') + s        else:            s = s + (separated and ' ' or '') + smb     sign_pos = conv[val<0 and 'n_sign_posn' or 'p_sign_posn']    sign = conv[val<0 and 'negative_sign' or 'positive_sign']     if sign_pos == 0:        s = '(' + s + ')'    elif sign_pos == 1:        s = sign + s    elif sign_pos == 2:        s = s + sign    elif sign_pos == 3:        s = s.replace('<', sign)    elif sign_pos == 4:        s = s.replace('>', sign)    else:        # the default if nothing specified;        # this should be the most fitting sign position        s = sign + s     return s.replace('<', '').replace('>', '') def str(val):    """Convert float to integer, taking the locale into account."""    return format("%.12g", val) def atof(string, func=float):    "Parses a string as a float according to the locale settings."    #First, get rid of the grouping    ts = localeconv()['thousands_sep']    if ts:        string = string.replace(ts, '')    #next, replace the decimal point with a dot    dd = localeconv()['decimal_point']    if dd:        string = string.replace(dd, '.')    #finally, parse the string    return func(string) def atoi(str):    "Converts a string to an integer according to the locale settings."    return atof(str, int) def _test():    setlocale(LC_ALL, "")    #do grouping    s1 = format("%d", 123456789,1)    print s1, "is", atoi(s1)    #standard formatting    s1 = str(3.14)    print s1, "is", atof(s1) ### Locale name aliasing engine # Author: Marc-Andre Lemburg, mal@lemburg.com# Various tweaks by Fredrik Lundh <fredrik@pythonware.com> # store away the low-level version of setlocale (it's# overridden below)_setlocale = setlocale def normalize(localename):     """ Returns a normalized locale code for the given locale        name.         The returned locale code is formatted for use with        setlocale().         If normalization fails, the original name is returned        unchanged.         If the given encoding is not known, the function defaults to        the default encoding for the locale code just like setlocale()        does.     """    # Normalize the locale name and extract the encoding    fullname = localename.lower()    if ':' in fullname:        # ':' is sometimes used as encoding delimiter.        fullname = fullname.replace(':', '.')    if '.' in fullname:        langname, encoding = fullname.split('.')[:2]        fullname = langname + '.' + encoding    else:        langname = fullname        encoding = ''     # First lookup: fullname (possibly with encoding)    norm_encoding = encoding.replace('-', '')    norm_encoding = norm_encoding.replace('_', '')    lookup_name = langname + '.' + encoding    code = locale_alias.get(lookup_name, None)    if code is not None:        return code    #print 'first lookup failed'     # Second try: langname (without encoding)    code = locale_alias.get(langname, None)    if code is not None:        #print 'langname lookup succeeded'        if '.' in code:            langname, defenc = code.split('.')        else:            langname = code            defenc = ''        if encoding:            # Convert the encoding to a C lib compatible encoding string            norm_encoding = encodings.normalize_encoding(encoding)            #print 'norm encoding: %r' % norm_encoding            norm_encoding = encodings.aliases.aliases.get(norm_encoding,                                                          norm_encoding)            #print 'aliased encoding: %r' % norm_encoding            encoding = locale_encoding_alias.get(norm_encoding,                                                 norm_encoding)        else:            encoding = defenc        #print 'found encoding %r' % encoding        if encoding:            return langname + '.' + encoding        else:            return langname     else:        return localename def _parse_localename(localename):     """ Parses the locale code for localename and returns the        result as tuple (language code, encoding).         The localename is normalized and passed through the locale        alias engine. A ValueError is raised in case the locale name        cannot be parsed.         The language code corresponds to RFC 1766.  code and encoding        can be None in case the values cannot be determined or are        unknown to this implementation.     """    code = normalize(localename)    if '@' in code:        # Deal with locale modifiers        code, modifier = code.split('@')        if modifier == 'euro' and '.' not in code:            # Assume Latin-9 for @euro locales. This is bogus,            # since some systems may use other encodings for these            # locales. Also, we ignore other modifiers.            return code, 'iso-8859-15'     if '.' in code:        return tuple(code.split('.')[:2])    elif code == 'C':        return None, None    raise ValueError, 'unknown locale: %s' % localename def _build_localename(localetuple):     """ Builds a locale code from the given tuple (language code,        encoding).         No aliasing or normalizing takes place.     """    language, encoding = localetuple    if language is None:        language = 'C'    if encoding is None:        return language    else:        return language + '.' + encoding def getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')):     """ Tries to determine the default locale settings and returns        them as tuple (language code, encoding).         According to POSIX, a program which has not called        setlocale(LC_ALL, "") runs using the portable 'C' locale.        Calling setlocale(LC_ALL, "") lets it use the default locale as        defined by the LANG variable. Since we don't want to interfere        with the current locale setting we thus emulate the behavior        in the way described above.         To maintain compatibility with other platforms, not only the        LANG variable is tested, but a list of variables given as        envvars parameter. The first found to be defined will be        used. envvars defaults to the search path used in GNU gettext;        it must always contain the variable name 'LANG'.         Except for the code 'C', the language code corresponds to RFC        1766.  code and encoding can be None in case the values cannot        be determined.     """     try:        # check if it's supported by the _locale module        import _locale        code, encoding = _locale._getdefaultlocale()    except (ImportError, AttributeError):        pass    else:        # make sure the code/encoding values are valid        if sys.platform == "win32" and code and code[:2] == "0x":            # map windows language identifier to language name            code = windows_locale.get(int(code, 0))        # ...add other platform-specific processing here, if        # necessary...        return code, encoding     # fall back on POSIX behaviour    import os    lookup = os.environ.get    for variable in envvars:        localename = lookup(variable,None)        if localename:            if variable == 'LANGUAGE':                localename = localename.split(':')[0]            break    else:        localename = 'C'    return _parse_localename(localename)  def getlocale(category=LC_CTYPE):     """ Returns the current setting for the given locale category as        tuple (language code, encoding).         category may be one of the LC_* value except LC_ALL. It        defaults to LC_CTYPE.         Except for the code 'C', the language code corresponds to RFC        1766.  code and encoding can be None in case the values cannot        be determined.     """    localename = _setlocale(category)    if category == LC_ALL and ';' in localename:        raise TypeError, 'category LC_ALL is not supported'    return _parse_localename(localename) def setlocale(category, locale=None):     """ Set the locale for the given category.  The locale can be        a string, a locale tuple (language code, encoding), or None.         Locale tuples are converted to strings the locale aliasing        engine.  Locale strings are passed directly to the C lib.         category may be given as one of the LC_* values.     """    if locale and type(locale) is not type(""):        # convert to string        locale = normalize(_build_localename(locale))    return _setlocale(category, locale) def resetlocale(category=LC_ALL):     """ Sets the locale for category to the default setting.         The default setting is determined by calling        getdefaultlocale(). category defaults to LC_ALL.     """    _setlocale(category, _build_localename(getdefaultlocale())) if sys.platform in ('win32', 'darwin', 'mac'):    # On Win32, this will return the ANSI code page    # On the Mac, it should return the system encoding;    # it might return "ascii" instead    def getpreferredencoding(do_setlocale = True):        """Return the charset that the user is likely using."""        import _locale        return _locale._getdefaultlocale()[1]else:    # On Unix, if CODESET is available, use that.    try:        CODESET    except NameError:

Lines 1–500 of 1562.