[IMP] ir.translation,tools.translate._(): cleanup and speedup if translations via _()

- Removed expensive inspect.stack() (courtesy of xrg)
- Improved heuristics for detection of context/lang and cursor
- Switch to using ir.translation when possible instead of direct DB hit to benefit from cache
- Cleanup

lp bug: https://launchpad.net/bugs/624114 fixed

bzr revid: odo@openerp.com-20101215114019-psrsu998inw6cksu
This commit is contained in:
Olivier Dony 2010-12-15 12:40:19 +01:00
parent 516dc572c4
commit 650490d61d
2 changed files with 77 additions and 41 deletions

View File

@ -138,7 +138,7 @@ class ir_translation(osv.osv):
and source. All values passed to this method should be unicode (not byte strings), and source. All values passed to this method should be unicode (not byte strings),
especially ``source``. especially ``source``.
:param name: identification of the term to translate, such as field name :param name: identification of the term to translate, such as field name (optional if source is passed)
:param types: single string defining type of term to translate (see ``type`` field on ir.translation), or sequence of allowed types (strings) :param types: single string defining type of term to translate (see ``type`` field on ir.translation), or sequence of allowed types (strings)
:param lang: language code of the desired translation :param lang: language code of the desired translation
:param source: optional source term to translate (should be unicode) :param source: optional source term to translate (should be unicode)
@ -153,19 +153,22 @@ class ir_translation(osv.osv):
if isinstance(types, basestring): if isinstance(types, basestring):
types = (types,) types = (types,)
if source: if source:
cr.execute('select value ' \ query = """SELECT value
'from ir_translation ' \ FROM ir_translation
'where lang=%s ' \ WHERE lang=%s
'and type in %s ' \ AND type in %s
'and name=%s ' \ AND src=%s"""
'and src=%s', params = (lang or '', types, source)
(lang or '', types, tools.ustr(name), source)) if name:
query += " AND name=%s"
params += (tools.ustr(name),)
cr.execute(query, params)
else: else:
cr.execute('select value ' \ cr.execute("""SELECT value
'from ir_translation ' \ FROM ir_translation
'where lang=%s ' \ WHERE lang=%s
'and type in %s ' \ AND type in %s
'and name=%s', AND name=%s"""
(lang or '', types, tools.ustr(name))) (lang or '', types, tools.ustr(name)))
res = cr.fetchone() res = cr.fetchone()
trad = res and res[0] or u'' trad = res and res[0] or u''

View File

@ -26,10 +26,12 @@ import inspect
import itertools import itertools
import locale import locale
import os import os
import pooler
import re import re
import logging import logging
import tarfile import tarfile
import tempfile import tempfile
import threading
from os.path import join from os.path import join
import logging import logging
@ -153,48 +155,79 @@ logger = logging.getLogger('translate')
class GettextAlias(object): class GettextAlias(object):
def _get_db_pool(self):
# find current DB based on thread/worker db name (see netsvc)
db_name = getattr(threading.currentThread(), 'dbname', None)
if db_name:
dbname = getattr(threading.currentThread(), 'dbname')
return pooler.get_db_and_pool(dbname)
return (None, None)
def _get_cr(self, frame): def _get_cr(self, frame):
is_new_cr = False new_cr = False
cr = frame.f_locals.get('cr') cr = frame.f_locals.get('cr', frame.f_locals.get('cursor'))
if not cr: if not cr:
s = frame.f_locals.get('self', {}) s = frame.f_locals.get('self', {})
cr = getattr(s, 'cr', False) cr = getattr(s, 'cr', None)
if not cr: if not cr:
if frame.f_globals.get('pooler', False): db, _ = self._get_db_pool()
# TODO: we should probably get rid of the 'is_new_cr' case: no cr in locals -> no translation for you if db:
dbs = frame.f_globals['pooler'].pool_dic.keys() cr = db.cursor()
if len(dbs) == 1: new_cr = True
cr = pooler.get_db(dbs[0]).cursor() return cr, new_cr
is_new_cr = True
return cr, is_new_cr
def _get_lang(self, frame): def _get_lang(self, frame):
lang = frame.f_locals.get('context', {}).get('lang', False) lang = None
ctx = frame.f_locals.get('context')
if not ctx:
kwargs = frame.f_locals.get('kwargs')
if kwargs is None:
args = frame.f_locals.get('args')
if args and isinstance(args, (list, tuple)) \
and isinstance(args[-1], dict):
ctx = args[-1]
elif isinstance(kwargs, dict):
ctx = kwargs.get('context')
if ctx:
lang = ctx.get('lang')
if not lang: if not lang:
args = frame.f_locals.get('args', False) s = frame.f_locals.get('self', {})
if args: c = getattr(s, 'localcontext', None)
lang = args[-1].get('lang', False) if c:
if not lang: lang = c.get('lang')
s = frame.f_locals.get('self', {})
c = getattr(s, 'localcontext', {})
lang = c.get('lang', False)
return lang return lang
def __call__(self, source): def __call__(self, source):
is_new_cr = False
res = source res = source
cr = None
new_cr = False
try: try:
frame = inspect.stack()[1][0] frame = inspect.currentframe()
cr, is_new_cr = self._get_cr(frame) if frame is None:
return source
frame = frame.f_back
if not frame:
return source
lang = self._get_lang(frame) lang = self._get_lang(frame)
if lang and cr: if lang:
cr.execute('SELECT value FROM ir_translation WHERE lang=%s AND type IN (%s, %s) AND src=%s', (lang, 'code','sql_constraint', source)) cr, new_cr = self._get_cr(frame)
res_trans = cr.fetchone() if cr:
res = res_trans and res_trans[0] or source # Try to use ir.translation to benefit from global cache if possible
_, pool = self._get_db_pool()
if pool:
res = pool.get('ir.translation')._get_source(cr, 1, None, ('code','sql_constraint'), lang, source)
else:
cr.execute('SELECT value FROM ir_translation WHERE lang=%s AND type IN (%s, %s) AND src=%s', (lang, 'code','sql_constraint', source))
res_trans = cr.fetchone()
res = res_trans and res_trans[0] or source
else:
logger.debug('no context cursor detected, skipping translation for "%r"', source)
else:
logger.debug('no translation language detected, skipping translation for "%r" ', source)
except Exception: except Exception:
logger.debug('translation went wrong for string %s', repr(source)) logger.debug('translation went wrong for "%r", skipped', source)
finally: finally:
if is_new_cr: if cr and new_cr:
cr.close() cr.close()
return res return res