From 5472c050aff29d0cb40d80c4e5166b2dc3cea1f0 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Nov 2010 12:52:54 +0100 Subject: [PATCH 01/11] [FIX] base: don't return search_view and search_view_id from ir_values.get bzr revid: xmo@openerp.com-20101109115254-svfwdr3kfb7as7a6 --- bin/addons/base/ir/ir_values.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index 310f3552403..e4e60bc4731 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -196,10 +196,12 @@ class ir_values(osv.osv): id = int(id) fields = self.pool.get(model).fields_get_keys(cr, uid) pos = 0 + # FIXME: It might be a good idea to opt-in that kind of stuff + # FIXME: instead of arbitrarily removing random fields while pos Date: Tue, 9 Nov 2010 12:57:41 +0100 Subject: [PATCH 02/11] [IMP] make type tests more readable in ir_values.get bzr revid: xmo@openerp.com-20101109115741-15iyg5tpblgmb2yy --- bin/addons/base/ir/ir_values.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index e4e60bc4731..77c6a572337 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -149,8 +149,8 @@ class ir_values(osv.osv): def get(self, cr, uid, key, key2, models, meta=False, context={}, res_id_req=False, without_user=True, key2_req=True): result = [] for m in models: - if type(m)==type([]) or type(m)==type(()): - m,res_id = m + if isinstance(m, (list, tuple)): + m, res_id = m else: res_id=False @@ -223,7 +223,7 @@ class ir_values(osv.osv): res = filter(bool, map(lambda x: _result_get(x, keys), list(result))) res2 = res[:] for r in res: - if type(r[2])==type({}) and 'type' in r[2]: + if isinstance(r[2], dict) and 'type' in r[2]: if r[2]['type'] in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'): groups = r[2].get('groups_id') if groups: From b89aef19f15cff658ef3f6db04be8cb2669569d2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Nov 2010 14:08:34 +0100 Subject: [PATCH 03/11] [IMP] base: tentative code simplifications (removal of redundant calls) in ir_values.get bzr revid: xmo@openerp.com-20101109130834-2rdsy3u7hm8kzj5n --- bin/addons/base/ir/ir_values.py | 44 +++++++++++++-------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index 77c6a572337..abc6aa5d928 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -159,9 +159,8 @@ class ir_values(osv.osv): if key2: where.append('key2=%s') params.append(key2[:200]) - else: - if key2_req and not meta: - where.append('key2 is null') + elif key2_req and not meta: + where.append('key2 is null') if res_id_req and (models[-1][0]==m): if res_id: where.append('res_id=%s') @@ -193,7 +192,6 @@ class ir_values(osv.osv): keys.append(x[1]) if x[3]: model,id = x[2].split(',') - id = int(id) fields = self.pool.get(model).fields_get_keys(cr, uid) pos = 0 # FIXME: It might be a good idea to opt-in that kind of stuff @@ -206,37 +204,29 @@ class ir_values(osv.osv): else: pos+=1 try: - datas = self.pool.get(model).read(cr, uid, [id], fields, context) + datas = self.pool.get(model).read(cr, uid, [int(id)], fields, context) except except_orm, e: return False - datas= datas and datas[0] or None + datas = datas and datas[0] if not datas: - #ir_del(cr, uid, x[0]) return False else: - datas = pickle.loads(str(x[2].encode('utf-8'))) + datas = pickle.loads(x[2].encode('utf-8')) if meta: - meta2 = pickle.loads(x[4]) - return (x[0],x[1],datas,meta2) - return (x[0],x[1],datas) + return (x[0], x[1], datas, pickle.loads(x[4])) + return (x[0], x[1], datas) keys = [] - res = filter(bool, map(lambda x: _result_get(x, keys), list(result))) + res = filter(None, map(lambda x: _result_get(x, keys), result)) res2 = res[:] for r in res: - if isinstance(r[2], dict) and 'type' in r[2]: - if r[2]['type'] in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'): - groups = r[2].get('groups_id') - if groups: - cr.execute('SELECT COUNT(1) FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',(tuple(groups), uid)) - cnt = cr.fetchone()[0] - if not cnt: - res2.remove(r) - if r[1] == 'Menuitem' and not res2: - raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!') + if isinstance(r[2], dict) and r[2].get('type') in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'): + groups = r[2].get('groups_id') + if groups: + cr.execute('SELECT COUNT(1) FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',(tuple(groups), uid)) + cnt = cr.fetchone()[0] + if not cnt: + res2.remove(r) + if r[1] == 'Menuitem' and not res2: + raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!') return res2 ir_values() - - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - From f3722bbfe410fd113daaa45d419435764be4b28c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Nov 2010 14:08:47 +0100 Subject: [PATCH 04/11] [IMP] base: make ir_values.get._result_get more readable and slightly faster. On a test set of ~20 fields, using a precomputed set instead of a spot-computed tuple for 'in' testing yields ~30% speed improvement, and replacing the manual removal of elements by a listcomp filtering yields ~30% more. Tests were also performed using sets and set.difference, while it yields better performances than manual element removal it is still worse than listcomp, especially when taking in account the re-serialization to a list bzr revid: xmo@openerp.com-20101109130847-p1xfmk3zhhivebj1 --- bin/addons/base/ir/ir_values.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index abc6aa5d928..f600f53ec79 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -24,6 +24,11 @@ from osv.orm import except_orm import pickle from tools.translate import _ +EXCLUDED_FIELDS = set(( + 'report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml', + 'report_sxw_content_data', 'report_rml_content_data', 'search_view', + 'search_view_id')) + class ir_values(osv.osv): _name = 'ir.values' @@ -192,17 +197,13 @@ class ir_values(osv.osv): keys.append(x[1]) if x[3]: model,id = x[2].split(',') - fields = self.pool.get(model).fields_get_keys(cr, uid) - pos = 0 # FIXME: It might be a good idea to opt-in that kind of stuff # FIXME: instead of arbitrarily removing random fields - while pos Date: Tue, 9 Nov 2010 14:17:34 +0100 Subject: [PATCH 05/11] [IMP] type tests simplifications in ir_values.set bzr revid: xmo@openerp.com-20101109131734-aby85mwhfhc4501w --- bin/addons/base/ir/ir_values.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index f600f53ec79..7c519551a0c 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -101,7 +101,7 @@ class ir_values(osv.osv): cr.execute('CREATE INDEX ir_values_key_model_key2_index ON ir_values (key, model, key2)') def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False): - if type(value)==type(u''): + if isinstance(value, unicode): value = value.encode('utf8') if not isobject: value = pickle.dumps(value) @@ -109,7 +109,7 @@ class ir_values(osv.osv): meta = pickle.dumps(meta) ids_res = [] for model in models: - if type(model)==type([]) or type(model)==type(()): + if isinstance(model, (list, tuple)): model,res_id = model else: res_id=False From bc156c64e4786ea30f07cb851b5f85e4b483b0e7 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Nov 2010 14:21:37 +0100 Subject: [PATCH 06/11] [IMP] remove redundancy in ir_values.set bzr revid: xmo@openerp.com-20101109132137-nmkg6uc5sdqovu1o --- bin/addons/base/ir/ir_values.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index 7c519551a0c..21ee1a66f93 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -114,25 +114,19 @@ class ir_values(osv.osv): else: res_id=False if replace: + search_criteria = [ + ('key', '=', key), + ('key2', '=', key2), + ('model', '=', model), + ('res_id', '=', res_id), + ('user_id', '=', preserve_user and uid) + ] if key in ('meta', 'default'): - ids = self.search(cr, uid, [ - ('key', '=', key), - ('key2', '=', key2), - ('name', '=', name), - ('model', '=', model), - ('res_id', '=', res_id), - ('user_id', '=', preserve_user and uid) - ]) + search_criteria.append(('name', '=', name)) else: - ids = self.search(cr, uid, [ - ('key', '=', key), - ('key2', '=', key2), - ('value', '=', value), - ('model', '=', model), - ('res_id', '=', res_id), - ('user_id', '=', preserve_user and uid) - ]) - self.unlink(cr, uid, ids) + search_criteria.append(('value', '=', value)) + + self.unlink(cr, uid, self.search(cr, uid, search_criteria)) vals = { 'name': name, 'value': value, From fc158668b7e2997dc32def84b0472d36573fc262 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 9 Nov 2010 17:50:14 +0100 Subject: [PATCH 07/11] [IMP] pyflakes tools/misc.py bzr revid: chs@openerp.com-20101109165014-6yw5xvojoe7b0i4t --- bin/tools/misc.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/tools/misc.py b/bin/tools/misc.py index 9728866a5d0..6d3aba7b561 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -40,7 +40,6 @@ from email.MIMEBase import MIMEBase from email.MIMEMultipart import MIMEMultipart from email.Header import Header from email.Utils import formatdate, COMMASPACE -from email.Utils import formatdate, COMMASPACE from email import Encoders from itertools import islice, izip from which import which @@ -432,7 +431,7 @@ def _email_send(smtp_from, smtp_to_list, message, openobject_id=None, ssl=False, from mailbox import Maildir maildir_path = smtp_server[8:] mdir = Maildir(maildir_path,factory=None, create = True) - mdir.add(msg.as_string(True)) + mdir.add(message.as_string(True)) return True oldstderr = smtplib.stderr @@ -1408,7 +1407,7 @@ def upload_data(email, data, type='SURVEY'): return True -# port of python 2.6's attrgetter with support for dotted notation +# port of python 2.6's attrgetter with support for dotted notation def resolve_attr(obj, attr): for name in attr.split("."): obj = getattr(obj, name) From ad4f3dfc0b4a23088bdfec7b1e2d458f86993fb7 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 9 Nov 2010 17:57:39 +0100 Subject: [PATCH 08/11] [IMP] tools.cache now use a LRU to avoid memory overload bzr revid: chs@openerp.com-20101109165739-1pm8jhckcyzkb2z1 --- bin/tools/func.py | 25 ++++++++-- bin/tools/lru.py | 115 ++++++++++++++++++++++++++++++++++++++++++++++ bin/tools/misc.py | 5 +- 3 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 bin/tools/lru.py diff --git a/bin/tools/func.py b/bin/tools/func.py index 3168f5e6935..8cedf6715fd 100644 --- a/bin/tools/func.py +++ b/bin/tools/func.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). +# Copyright (C) 2010 OpenERP s.a. (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -15,17 +16,17 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . +# along with this program. If not, see . # ############################################################################## -__all__ = ['partial', 'wraps', 'update_wrapper'] +__all__ = ['partial', 'wraps', 'update_wrapper', 'synchronized'] try: from functools import partial, wraps, update_wrapper except ImportError: # The functools module doesn't exist in python < 2.5 - # Code taken from python 2.5 + # Code taken from python 2.5 # http://svn.python.org/view/python/tags/r254/Lib/functools.py?view=markup def partial(fun, *args, **kwargs): @@ -76,3 +77,19 @@ except ImportError: return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) + + +def synchronized(lock_attr='_lock'): + def decorator(func): + @wraps(func) + def wrapper(self, *args, **kwargs): + lock = getattr(self, lock_attr) + try: + lock.acquire() + return func(self, *args, **kwargs) + finally: + lock.release() + return wrapper + return decorator + + diff --git a/bin/tools/lru.py b/bin/tools/lru.py new file mode 100644 index 00000000000..190fe5e4ff6 --- /dev/null +++ b/bin/tools/lru.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# taken from http://code.activestate.com/recipes/252524-length-limited-o1-lru-cache-implementation/ +import threading +from func import synchronized + +__all__ = ['LRU'] + +class LRUNode(object): + __slots__ = ['prev', 'next', 'me'] + def __init__(self, prev, me): + self.prev = prev + self.me = me + self.next = None + +class LRU: + """ + Implementation of a length-limited O(1) LRU queue. + Built for and used by PyPE: + http://pype.sourceforge.net + Copyright 2003 Josiah Carlson. + """ + def __init__(self, count, pairs=[]): + self._lock = threading.RLock() + self.count = max(count, 1) + self.d = {} + self.first = None + self.last = None + for key, value in pairs: + self[key] = value + + @synchronized() + def __contains__(self, obj): + return obj in self.d + + @synchronized() + def __getitem__(self, obj): + a = self.d[obj].me + self[a[0]] = a[1] + return a[1] + + @synchronized() + def __setitem__(self, obj, val): + if obj in self.d: + del self[obj] + nobj = LRUNode(self.last, (obj, val)) + if self.first is None: + self.first = nobj + if self.last: + self.last.next = nobj + self.last = nobj + self.d[obj] = nobj + if len(self.d) > self.count: + if self.first == self.last: + self.first = None + self.last = None + return + a = self.first + a.next.prev = None + self.first = a.next + a.next = None + del self.d[a.me[0]] + del a + + @synchronized() + def __delitem__(self, obj): + nobj = self.d[obj] + if nobj.prev: + nobj.prev.next = nobj.next + else: + self.first = nobj.next + if nobj.next: + nobj.next.prev = nobj.prev + else: + self.last = nobj.prev + del self.d[obj] + + @synchronized() + def __iter__(self): + cur = self.first + while cur != None: + cur2 = cur.next + yield cur.me[1] + cur = cur2 + + @synchronized() + def __len__(self): + return len(self.d) + + @synchronized() + def iteritems(self): + cur = self.first + while cur != None: + cur2 = cur.next + yield cur.me + cur = cur2 + + @synchronized() + def iterkeys(self): + return iter(self.d) + + @synchronized() + def itervalues(self): + for i,j in self.iteritems(): + yield j + + @synchronized() + def keys(self): + return self.d.keys() + + @synchronized() + def pop(self,key): + v=self[key] + del self[key] + return v + diff --git a/bin/tools/misc.py b/bin/tools/misc.py index 6d3aba7b561..90b4c135214 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -50,6 +50,7 @@ else: import netsvc from config import config +from lru import LRU _logger = logging.getLogger('tools') @@ -696,7 +697,7 @@ class cache(object): __caches = [] - def __init__(self, timeout=None, skiparg=2, multi=None): + def __init__(self, timeout=None, skiparg=2, multi=None, size=8192): assert skiparg >= 2 # at least self and cr if timeout is None: self.timeout = config['cache_timeout'] @@ -705,7 +706,7 @@ class cache(object): self.skiparg = skiparg self.multi = multi self.lasttime = time.time() - self.cache = {} + self.cache = LRU(size) # TODO take size from config self.fun = None cache.__caches.append(self) From af817028c693ce1e500f9d3939c8bd7f688a3a68 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 9 Nov 2010 23:09:07 +0100 Subject: [PATCH 09/11] [FIX] tools.config: do not chmod existing config files lp bug: https://launchpad.net/bugs/519455 fixed bzr revid: olivier@olivier-donys-macbook-pro.local-20101109220907-t2phxs26l136bhe7 --- bin/tools/config.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/tools/config.py b/bin/tools/config.py index 2964870d427..f98cdbab1b9 100644 --- a/bin/tools/config.py +++ b/bin/tools/config.py @@ -450,11 +450,13 @@ class configmanager(object): # try to create the directories and write the file try: - if not os.path.exists(os.path.dirname(self.rcfile)): + rc_exists = os.path.exists(self.rcfile) + if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)): os.makedirs(os.path.dirname(self.rcfile)) try: p.write(file(self.rcfile, 'w')) - os.chmod(self.rcfile, 0600) + if not rc_exists: + os.chmod(self.rcfile, 0600) except IOError: sys.stderr.write("ERROR: couldn't write the config file\n") From e15d5e472251e69738645d67ed7942b5bcb7f780 Mon Sep 17 00:00:00 2001 From: "Ferdinand @ Camptocamp" <> Date: Wed, 10 Nov 2010 01:32:19 +0100 Subject: [PATCH 10/11] [IMP] ir.exports: added missing sql index lp bug: https://launchpad.net/bugs/665697 fixed bzr revid: olivier@olivier-donys-macbook-pro.local-20101110003219-l7380et3m3aqa10w --- bin/addons/base/ir/ir_exports.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/addons/base/ir/ir_exports.py b/bin/addons/base/ir/ir_exports.py index 48f4141d0b5..39477f51fbe 100644 --- a/bin/addons/base/ir/ir_exports.py +++ b/bin/addons/base/ir/ir_exports.py @@ -26,7 +26,7 @@ class ir_exports(osv.osv): _name = "ir.exports" _columns = { 'name': fields.char('Export Name', size=128), - 'resource': fields.char('Resource', size=128), + 'resource': fields.char('Resource', size=128, select=True), 'export_fields': fields.one2many('ir.exports.line', 'export_id', 'Export ID'), } From f238a614109151e3abe512c0663940a669d8fd4e Mon Sep 17 00:00:00 2001 From: Mustufa Rangwala Date: Wed, 10 Nov 2010 10:15:44 +0530 Subject: [PATCH 11/11] [FIX] l10n_ch: Removed duplicate msgids on template file (Translation) bzr revid: mra@mra-laptop-20101110044544-qfh4o0bhe6uoz87u --- addons/l10n_ch/i18n/l10n_ch.pot | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/addons/l10n_ch/i18n/l10n_ch.pot b/addons/l10n_ch/i18n/l10n_ch.pot index 6a70e020595..f2cface3687 100644 --- a/addons/l10n_ch/i18n/l10n_ch.pot +++ b/addons/l10n_ch/i18n/l10n_ch.pot @@ -152,11 +152,6 @@ msgstr "" msgid "the currenty edited account journal" msgstr "" -#. module: l10n_ch -#: view:account.journal.todo:0 -msgid "Next" -msgstr "" - #. module: l10n_ch #: rml:l10n_ch.invoice.bvr:0 msgid "Supplier Invoice" @@ -186,6 +181,7 @@ msgstr "" #. module: l10n_ch #: model:ir.actions.act_window,name:l10n_ch.action_config_journal +#: model:ir.actions.act_window,name:l10n_ch.action_config_tax msgid "Configure Journal" msgstr "" @@ -399,6 +395,7 @@ msgstr "" #. module: l10n_ch #: view:account.journal.todo:0 #: wizard_button:l10n_ch.bvr_import,init,end:0 +#: view:account.tax.template.todo:0 msgid "Cancel" msgstr "" @@ -526,11 +523,6 @@ msgstr "" msgid "Total" msgstr "" -#. module: l10n_ch -#: constraint:ir.model:0 -msgid "The Object name must start with x_ and not contain any special character !" -msgstr "" - #. module: l10n_ch #: field:account.tax.template.todo,account_paid_id:0 msgid "Refund Tax Account" @@ -554,6 +546,7 @@ msgstr "" #. module: l10n_ch #: view:account.tax.template.todo:0 +#: view:account.journal.todo:0 msgid "Next" msgstr "" @@ -562,11 +555,6 @@ msgstr "" msgid "Tax to set" msgstr "" -#. module: l10n_ch -#: constraint:ir.actions.act_window:0 -msgid "Invalid model name in the action definition." -msgstr "" - #. module: l10n_ch #: model:account.account.type,name:l10n_ch.account_type_financial_asset msgid "Bilan : Immobilisations financieres" @@ -637,11 +625,6 @@ msgstr "" msgid "Bilan : Debiteurs" msgstr "" -#. module: l10n_ch -#: model:ir.actions.act_window,name:l10n_ch.action_config_tax -msgid "Configure Journal" -msgstr "" - #. module: l10n_ch #: constraint:account.tax.code.template:0 msgid "Error ! You can not create recursive Tax Codes." @@ -657,11 +640,6 @@ msgstr "" msgid "Bilan : Liquidites et titres" msgstr "" -#. module: l10n_ch -#: constraint:ir.ui.view:0 -msgid "Invalid XML for View Architecture!" -msgstr "" - #. module: l10n_ch #: model:account.account.type,name:l10n_ch.account_type_non_ope_asset msgid "Bilan : Actifs hors exploitation" @@ -753,11 +731,6 @@ msgid "Generate Chart of Accounts from a Chart Template. You will be asked to pa " This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template." msgstr "" -#. module: l10n_ch -#: view:account.tax.template.todo:0 -msgid "Cancel" -msgstr "" - #. module: l10n_ch #: model:account.account.type,name:l10n_ch.account_type_inventory msgid "Bilan : Stocks"