Author: reinhard Date: 2009-10-02 08:06:16 -0500 (Fri, 02 Oct 2009) New Revision: 9922
Modified: trunk/gnue-common/src/apps/CommandOption.py trunk/gnue-common/src/apps/GConfig.py trunk/gnue-common/src/apps/i18n.py trunk/gnue-common/src/base/errors.py trunk/gnue-common/src/base/i18n.py trunk/gnue-common/src/utils/plugin.py trunk/gnue-common/src/utils/tree.py Log: Cleanup of i18n code. Modified: trunk/gnue-common/src/apps/CommandOption.py =================================================================== --- trunk/gnue-common/src/apps/CommandOption.py 2009-10-02 10:46:00 UTC (rev 9921) +++ trunk/gnue-common/src/apps/CommandOption.py 2009-10-02 13:06:16 UTC (rev 9922) @@ -80,6 +80,6 @@ # FIXME: only for compatibility, remove in some later version! if isinstance (self.help, str): - self.help = unicode (self.help, i18n.get_encoding()) + self.help = i18n.inconv(self.help) if isinstance (self.argumentName, str): - self.argumentName = unicode (self.argumentName, i18n.get_encoding()) + self.argumentName = i18n.inconv(self.argumentName) Modified: trunk/gnue-common/src/apps/GConfig.py =================================================================== --- trunk/gnue-common/src/apps/GConfig.py 2009-10-02 10:46:00 UTC (rev 9921) +++ trunk/gnue-common/src/apps/GConfig.py 2009-10-02 13:06:16 UTC (rev 9922) @@ -30,7 +30,7 @@ import ConfigParser from gnue import paths -from gnue.common.base import i18n, errors +from gnue.common.base import errors, i18n from gnue.common import GCConfig @@ -509,11 +509,11 @@ (output_width - len(nameString + defaultString)), defaultString) + description = option['Description'] # FIXME: This allows for non-unicode descriptions. Remove at some # point. - description = option['Description'] if isinstance(description, str): - description = unicode(description, i18n.get_encoding()) + description = i18n.inconv(description) output += "%s\n" % textwrap.fill(description, output_width) else: Modified: trunk/gnue-common/src/apps/i18n.py =================================================================== --- trunk/gnue-common/src/apps/i18n.py 2009-10-02 10:46:00 UTC (rev 9921) +++ trunk/gnue-common/src/apps/i18n.py 2009-10-02 13:06:16 UTC (rev 9922) @@ -30,27 +30,159 @@ @var encoding: Encoding of the current locale """ +import locale +import os +import sys + from gnue.common.base import i18n -__all__ = ['language', 'encoding', 'outconv', 'utranslate', 'translate', - 'getlanguage', 'getencoding', 'getuserlocale', 'setcurrentlocale'] +__all__ = ['language', 'encoding', 'getlanguage', 'getuserlocale', + 'setcurrentlocale'] # ----------------------------------------------------------------------------- -# Global variables +# Global variables - DEPRECATED. # ----------------------------------------------------------------------------- -language = i18n.language -encoding = i18n.encoding +language = None +encoding = None # ----------------------------------------------------------------------------- -# Functions +# Get the current language - DEPRECATED. # ----------------------------------------------------------------------------- -outconv = i18n.outconv +def getlanguage(): + """ + Returns the language of the currently active locale. This can be changed + with L{set_current_locale}. + + @return: language of the current locale. + """ + return language + + +# ----------------------------------------------------------------------------- +# Get the locale string of the current user +# ----------------------------------------------------------------------------- + +def getuserlocale(): + """ + Try to find out which locale the user is using. This is always the locale + of the user running the program and is not touched by + L{set_current_locale}. + + @return: localestring of the user's locale, i.e. de...@euro. + """ + + # *Actually* the following is very much what locale.getdefaultlocale should + # do anyway. However, that function is broken for $LANGUAGE containing a + # list of locales, separated with ":". So we have to manually rebuild it... + items = ['LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'] + for key in items: + if os.environ.has_key(key): + # Some systems (most notably Debian...) set $LANGUAGE to a *list* + # of locales, separated by a ":". + env_lang = (os.environ[key]).split(':')[0] + return locale.locale_alias.get(env_lang, env_lang) + + # On Mac OS X getdefaultlocale always returns 'C', so we will query the + # system preferences here. + if sys.platform == 'darwin': + pipe = os.popen('defaults read -g AppleLocale') + result = pipe.read().strip() + '.UTF-8' + pipe.close() + + else: + # Now this should only happen on Windows, where getdefaultlocale seems + # to work ok. + try: + result = locale.getdefaultlocale()[0] + except locale.Error: + result = '' + + return result or 'C' + + +# ----------------------------------------------------------------------------- +# Update global variables +# ----------------------------------------------------------------------------- + +def __updateglobals(): + + global language, encoding + + # On win32, getlocale is broken - it returns strings like Hungarian_Hungary + # instead of hu_HU. + if sys.platform in ['win32', 'Pocket PC']: + (language, encoding) = locale.getdefaultlocale() + else: + (language, encoding) = locale.getlocale() + + # Make sure language and encoding are not None + if not language: + language = 'C' + if not encoding: + encoding = 'ascii' + + +# ----------------------------------------------------------------------------- +# Change the current locale - DEPRECATED. +# ----------------------------------------------------------------------------- + +def setcurrentlocale(new_locale): + """ + Set the current locale. + + If it fails it tries to succeed using a more general form of the requested + locale, i.e. if 'de...@euro' fails, 'de_AT' will be tried next. If that + fails too, 'de' will be tried. + + @param new_locale: string of the locale to be set, e.g. de_at.iso88...@euro + (full blown) or 'de_AT' or 'en_AU' + """ + # Setting a locale different than the current locale doesn't work on + # Windows. + if sys.platform == 'win32': + return + + if new_locale is None: + new_locale = '' + + new_locale = new_locale.encode() + + parts = [] + add = [] + normal = locale.normalize(new_locale) # lc_cc.encod...@variant + next = normal.split('@')[0] # lc_CC.ENCODING + lang = next.split('.')[0] # lc_CC + + alias = locale.locale_alias.get(lang.split('_')[0].lower()) + if alias: + add = [alias.split('@')[0]] + add.append(add[-1].split('.')[0]) + add.append(locale.locale_alias.get(add[-1].split('_')[0].lower())) + + for item in [normal, next, lang] + add: + if item is not None and item not in parts: + parts.append(item) + + for item in parts: + try: + locale.setlocale(locale.LC_ALL, item) + + except locale.Error: + pass + + else: + break + + __updateglobals() + + +# ----------------------------------------------------------------------------- +# Module initialization +# ----------------------------------------------------------------------------- + utranslate = i18n.utranslate -translate = i18n.translate -getlanguage = i18n.get_language -getencoding = i18n.get_encoding -getuserlocale = i18n.get_user_locale -setcurrentlocale = i18n.set_current_locale + +__updateglobals() Modified: trunk/gnue-common/src/base/errors.py =================================================================== --- trunk/gnue-common/src/base/errors.py 2009-10-02 10:46:00 UTC (rev 9921) +++ trunk/gnue-common/src/base/errors.py 2009-10-02 13:06:16 UTC (rev 9922) @@ -27,6 +27,7 @@ import sys import traceback +import types import exceptions from gnue.common.base import i18n @@ -82,10 +83,17 @@ @return: Name of the exception. @rtype: Unicode """ - rep = self.name or "%s" % (sys.exc_info()[0] or exc_type) - return _unicode(rep.split('.')[-1]) + if self.name is not None: + return self.name + t = sys.exc_info()[0] or exc_type + if issubclass(t, Exception): + return unicode(t.__name__) + else: + return unicode(t) + + # ------------------------------------------------------------------------- # Get the detail of an exception # ------------------------------------------------------------------------- @@ -103,21 +111,22 @@ @rtype: Unicode """ if self.detail is not None: - return _unicode(self.detail, i18n.get_encoding()) + return self.detail (stype, svalue, strace) = sys.exc_info() stype = stype or etype svalue = svalue or evalue strace = strace or etrace - tStack = traceback.format_exception(stype, svalue, strace) + stack = traceback.format_exception(stype, svalue, strace) if count is not None: - del tStack[1:count + 1] + del stack[1:count + 1] - return _unicode("%s" % ''.join(tStack), i18n.get_encoding()) + # sys.exc_info() and traceback.format_exception() return 8-bit strings + # in the current encoding, so we have to convert them back to Unicode. + return i18n.inconv(''.join(stack)) - # ============================================================================= # System Error # ============================================================================= @@ -226,35 +235,14 @@ if count is not None: del lines[1:count + 1] - name = unicode("%s" % exc_type, i18n.get_encoding(), 'replace') + if isinstance(exc_type, types.InstanceType): + name = unicode(exc_type) + else: + name = unicode(exc_type.__name__) name = name.split('.')[-1] - message = unicode("%s" % exc_value, i18n.get_encoding(), 'replace') + message = unicode(exc_value) detail = ''.join(lines) if isinstance(detail, str): - detail = unicode(detail, i18n.get_encoding(), 'replace') + detail = i18n.inconv(detail) return ('system', name, message, detail) - - -# ----------------------------------------------------------------------------- -# Create a unicode string using a given encoding -# ----------------------------------------------------------------------------- - -def _unicode(text, encoding=None): - """ - Convert the given text into unicode using the optionally passed encoding. - If no encoding is specified the systems' default encoding will be used. - - @returns: the text in unicode - @rtype: unicode - """ - - result = text - - if isinstance(text, str): - if encoding is not None: - result = unicode(text, encoding, 'replace') - else: - result = unicode(text, errors='replace') - - return result Modified: trunk/gnue-common/src/base/i18n.py =================================================================== --- trunk/gnue-common/src/base/i18n.py 2009-10-02 10:46:00 UTC (rev 9921) +++ trunk/gnue-common/src/base/i18n.py 2009-10-02 13:06:16 UTC (rev 9922) @@ -23,57 +23,109 @@ """ Internationalization support. - -...@var language: Language of the current locale -...@var encoding: Encoding of the current locale """ import gettext import locale import os -import os.path import sys from gnue import paths from gnue.common.lib import modules -__all__ = ['language', 'encoding', 'outconv', 'utranslate', 'translate', - 'get_language', 'get_encoding', 'get_user_locale', - 'set_current_locale'] +__all__ = ['outconv', 'inconv', 'utranslate', 'translate'] +# ============================================================================= +# Locale initialization +# ============================================================================= + +def init_locale(): + """ + Initialize locale support. This is called automatically in module + initialization, no need to call it manually. + """ + + # On Mac, locale.setlocale() does not set the default locale. Instead we + # have to query the system preferences for the AppleLocale. + if sys.platform == 'darwin': + pipe = os.popen('defaults read -g AppleLocale') + deflc = pipe.read().strip() + '.UTF-8' + pipe.close() + else: + deflc = '' + + locale.setlocale(locale.LC_ALL, deflc) + + +# ============================================================================= +# Character set support +# ============================================================================= + # ----------------------------------------------------------------------------- -# Global variables +# Convert Unicode to String, using the current encoding # ----------------------------------------------------------------------------- -language = None -encoding = None +def outconv(text): + """ + Encode a text to the current encoding. This function is available as + the builtin function "o()". + """ + checktype(text, unicode) + encoding = locale.getlocale()[1] + if encoding is None: + encoding = 'ascii' + return text.encode(encoding, 'replace') + + # ----------------------------------------------------------------------------- -# Convert Unicode to String, let everything else untouched. This is o(). +# Convert String to Unicode, using the current encoding # ----------------------------------------------------------------------------- -def outconv(message): +def inconv(text): """ - Encodes a message to L{encoding} (the current locale's encoding). This - function is available as the builtin function "o()". + Encode a text to the current encoding. This function is available as + the builtin function "o()". """ - if isinstance(message, unicode): - return message.encode(encoding, 'replace') - else: - return message + checktype(text, str) + encoding = locale.getlocale()[1] + if encoding is None: + encoding = 'ascii' + return unicode(text, encoding, 'replace') + + +# ============================================================================= +# Language translation support +# ============================================================================= + # ----------------------------------------------------------------------------- +# Cache variables +# ----------------------------------------------------------------------------- + +__DOMAINS = {} # Domains by module name +__CATALOGS = {} # Message catalogs by domain:language + + +# ----------------------------------------------------------------------------- # Translate a message and return unicode # ----------------------------------------------------------------------------- def utranslate(message): """ - Translates a message and returns a unicode string. This function is + Translate a message and return a unicode string. This function is available as the builtin function "u_()". """ + + # TODO: with 0.8, deprecate non-unicode input, with 0.9 throw an error on + # non-unicode input. + language = locale.getlocale()[0] + if language is None: + return message + caller_module = modules.caller_module() if not __DOMAINS.has_key(caller_module): @@ -83,10 +135,12 @@ if domain is None: # For gnue.common.foo.bar, try gnue-common-foo-bar, # gnue-common-foo, gnue-common and gnue (in that order). + # TODO: deprecate with 0.8, remove with 0.9 - all modules should + # define __gettext_domain__. parts = caller_module.split('.') while parts: try_domain = '-'.join(parts) - if __find_catalog(try_domain) is not None: + if __find_catalog(try_domain, language) is not None: domain = try_domain break del parts[-1] @@ -97,7 +151,7 @@ if domain is None: return message - catalog = __find_catalog(domain) + catalog = __find_catalog(domain, language) if catalog is None: return message @@ -105,7 +159,7 @@ # ----------------------------------------------------------------------------- -def __find_catalog(domain): +def __find_catalog(domain, language): if __CATALOGS.has_key(domain + ':' + language): return __CATALOGS[domain + ':' + language] @@ -113,7 +167,7 @@ try: catalog = gettext.translation(domain, paths.data + '/share/locale', [language]) - except: + except IOError: catalog = None __CATALOGS[domain + ':' + language] = catalog @@ -127,189 +181,22 @@ def translate(message): """ - Translates a message and returns an 8 bit string, encoded with L{encoding} - (the current locale's encoding). This function is available as the builtin - function "_()". + Translate a message and return an 8 bit string, encoded with the current + encoding. This function is available as the builtin function "_()". """ + + # TODO: deprecate with 0.8, remove with 0.9, make _() an alias of u_() with + # 1.0 return outconv(utranslate(message)) -# ----------------------------------------------------------------------------- -# Cache variables -# ----------------------------------------------------------------------------- - -__DOMAINS = {} # Domains by module name -__CATALOGS = {} # Message catalogs by domain:language - - -# ----------------------------------------------------------------------------- -# Get the current language -# ----------------------------------------------------------------------------- - -def get_language(): - """ - Returns the language of the currently active locale. This can be changed - with L{set_current_locale}. - - @return: language of the current locale. - """ - return language - - -# ----------------------------------------------------------------------------- -# Get the current encoding -# ----------------------------------------------------------------------------- - -def get_encoding(): - """ - Returns the encoding of the currently active locale. This can be changed - with L{set_current_locale}. - - @return: encoding of the current locale. - """ - return encoding - - -# ----------------------------------------------------------------------------- -# Get the locale string of the current user -# ----------------------------------------------------------------------------- - -def get_user_locale(): - """ - Try to find out which locale the user is using. This is always the locale - of the user running the program and is not touched by - L{set_current_locale}. - - @return: localestring of the user's locale, i.e. de...@euro. - """ - - # *Actually* the following is very much what locale.getdefaultlocale should - # do anyway. However, that function is broken for $LANGUAGE containing a - # list of locales, separated with ":". So we have to manually rebuild it... - items = ['LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'] - for key in items: - if os.environ.has_key(key): - # Some systems (most notably Debian...) set $LANGUAGE to a *list* - # of locales, separated by a ":". - env_lang = (os.environ[key]).split(':')[0] - return locale.locale_alias.get(env_lang, env_lang) - - # On Mac OS X getdefaultlocale always returns 'C', so we will query the - # system preferences here. - if sys.platform == 'darwin': - pipe = os.popen('defaults read -g AppleLocale') - result = pipe.read().strip() + '.UTF-8' - pipe.close() - - else: - # Now this should only happen on Windows, where getdefaultlocale seems - # to work ok. - try: - result = locale.getdefaultlocale()[0] - except locale.Error: - result = '' - - return result or 'C' - - -# ----------------------------------------------------------------------------- -# Update global variables -# ----------------------------------------------------------------------------- - -def __updateglobals(): - - global language, encoding - - # On win32, getlocale is broken - it returns strings like Hungarian_Hungary - # instead of hu_HU. - if sys.platform in ['win32', 'Pocket PC']: - (language, encoding) = locale.getdefaultlocale() - else: - (language, encoding) = locale.getlocale() - - # Make sure language and encoding are not None - if not language: - language = 'C' - if not encoding: - encoding = 'ascii' - - -# ----------------------------------------------------------------------------- -# Change the current locale -# ----------------------------------------------------------------------------- - -def set_current_locale(new_locale): - """ - Set the current locale. - - If it fails it tries to succeed using a more general form of the requested - locale, i.e. if 'de...@euro' fails, 'de_AT' will be tried next. If that - fails too, 'de' will be tried. - - @param new_locale: string of the locale to be set, e.g. de_at.iso88...@euro - (full blown) or 'de_AT' or 'en_AU' - """ - # Setting a locale different than the current locale doesn't work on - # Windows. - if sys.platform == 'win32': - return - - if new_locale is None: - new_locale = '' - - new_locale = new_locale.encode() - - parts = [] - add = [] - normal = locale.normalize(new_locale) # lc_cc.encod...@variant - next = normal.split('@')[0] # lc_CC.ENCODING - lang = next.split('.')[0] # lc_CC - - alias = locale.locale_alias.get(lang.split('_')[0].lower()) - if alias: - add = [alias.split('@')[0]] - add.append(add[-1].split('.')[0]) - add.append(locale.locale_alias.get(add[-1].split('_')[0].lower())) - - for item in [normal, next, lang] + add: - if item is not None and item not in parts: - parts.append(item) - - for item in parts: - try: - locale.setlocale(locale.LC_ALL, item) - - except locale.Error: - pass - - else: - break - - __updateglobals() - - -# ----------------------------------------------------------------------------- +# ============================================================================= # Module initialization -# ----------------------------------------------------------------------------- +# ============================================================================= -# Initialize locale. On Mac locale.setlocale() does not return the default -# locale. Instead we have to query the system preferences for the AppleLocale -if sys.platform == 'darwin': - pipe = os.popen('defaults read -g AppleLocale') - deflc = pipe.read().strip() + '.UTF-8' - pipe.close() -else: - deflc = '' - -try: - locale.setlocale(locale.LC_ALL, deflc) -except: - pass - -__updateglobals() - -# Now define the new builtin stuff import __builtin__ __builtin__.__dict__['o'] = outconv __builtin__.__dict__['u_'] = utranslate __builtin__.__dict__['_'] = translate + +init_locale() Modified: trunk/gnue-common/src/utils/plugin.py =================================================================== --- trunk/gnue-common/src/utils/plugin.py 2009-10-02 10:46:00 UTC (rev 9921) +++ trunk/gnue-common/src/utils/plugin.py 2009-10-02 13:06:16 UTC (rev 9922) @@ -57,10 +57,9 @@ import os import sys -import traceback from types import ModuleType -from gnue.common.base import errors, i18n +from gnue.common.base import errors __all__ = ['LoadError', 'list_plugins', 'find'] @@ -90,10 +89,7 @@ message = u_("Cannot load plugin '%s'") % self.name detail = u_("The following plugins failed:\n") for (name, exc) in self.exceptions.items(): - tlist = traceback.format_exception_only(exc[0], exc[1]) - del tlist[1:2] - msg = unicode(''.join(tlist), i18n.get_encoding()) - detail += "* %s: %s" % (name, msg) + detail += "* %s: %s: %s" % (name, exc[1], exc[2]) else: message = u_("Cannot find plugin '%s'") % self.name detail = None @@ -241,7 +237,7 @@ try: mod = __import__(base, None, None, '*') except: - __failed[base] = sys.exc_info() + __failed[base] = errors.get_exception() return {base: __failed[base]} if hasattr(mod, '__noplugin__'): @@ -255,7 +251,7 @@ try: mod.__initplugin__() except: - __failed[base] = sys.exc_info() + __failed[base] = errors.get_exception() return {base: __failed[base]} return {base: mod} @@ -282,7 +278,7 @@ try: mod = __import__(base, None, None, '*') except: - __failed[base] = sys.exc_info() + __failed[base] = errors.get_exception() return {base: __failed[base]} if hasattr(mod, '__noplugin__'): @@ -295,7 +291,7 @@ try: mod.__initplugin__() except: - __failed[base] = sys.exc_info() + __failed[base] = errors.get_exception() return {base: __failed[base]} return mod @@ -323,7 +319,7 @@ try: mod = __import__(base, None, None, '*') except: - __failed[base] = sys.exc_info() + __failed[base] = errors.get_exception() return {base: __failed[base]} if hasattr(mod, '__noplugin__'): @@ -344,7 +340,7 @@ except ImportError: pass except: - __failed[base + '.' + name] = sys.exc_info() + __failed[base + '.' + name] = errors.get_exception() return {base + '.' + name: __failed[base + '.' + name]} else: return __first(base + '.' + name, identifier) @@ -376,8 +372,7 @@ if isinstance(iresult, ModuleType): print "ok" else: - data = traceback.format_exception_only(iresult[0], iresult[1]) - print ''.join(data), + print iresult[1] + ": " + iresult[2] elif sys.argv[1] == 'test': Modified: trunk/gnue-common/src/utils/tree.py =================================================================== --- trunk/gnue-common/src/utils/tree.py 2009-10-02 10:46:00 UTC (rev 9921) +++ trunk/gnue-common/src/utils/tree.py 2009-10-02 13:06:16 UTC (rev 9922) @@ -1,3 +1,4 @@ +# encoding:utf-8 # GNU Enterprise Common Library - Tree structure classes # # Copyright 2001-2009 Free Software Foundation @@ -27,11 +28,9 @@ TODO: This module is work in progress. """ -import locale +from gnue.common.base import errors, i18n +from gnue.common.base.i18n import utranslate as u_ # for epydoc -from gnue.common.apps import errors -from gnue.common.apps.i18n import utranslate as u_ # for epydoc - __all__ = ['CircularReferenceError', 'DuplicateChildNameError', 'DuplicateDescendantNameError', 'NodeDictNotAvailableError', 'Node', 'NamedNode', 'AttribNode'] @@ -374,7 +373,7 @@ return "<unnamed " + self._node_type_ + ">" elif isinstance(self.__node_name, unicode): - return self.__node_name.encode(locale.getlocale()[0], 'replace') + return i18n.outconv(self.__node_name) else: return self.__node_name _______________________________________________ commit-gnue mailing list commit-gnue@gnu.org http://lists.gnu.org/mailman/listinfo/commit-gnue