changeset 392e44a1e0b8 in trytond:default details: https://hg.tryton.org/trytond?cmd=changeset;node=392e44a1e0b8 description: Replace memoize with functools.lru_cache
issue9081 review286781002 diffstat: CHANGELOG | 1 + trytond/model/modelstorage.py | 24 ++++++++-------- trytond/tools/__init__.py | 4 +- trytond/tools/misc.py | 63 ------------------------------------------- 4 files changed, 15 insertions(+), 77 deletions(-) diffs (202 lines): diff -r 87b102b5b4ab -r 392e44a1e0b8 CHANGELOG --- a/CHANGELOG Tue Mar 24 10:02:58 2020 +0100 +++ b/CHANGELOG Wed Mar 25 17:34:35 2020 +0100 @@ -1,3 +1,4 @@ +* Replace memoize with functools.lru_cache * Add support for Python 3.8 * Set all fields readonly for inactive record * Enable check_access context when checking wizard access (issue9108) diff -r 87b102b5b4ab -r 392e44a1e0b8 trytond/model/modelstorage.py --- a/trytond/model/modelstorage.py Tue Mar 24 10:02:58 2020 +0100 +++ b/trytond/model/modelstorage.py Wed Mar 25 17:34:35 2020 +0100 @@ -7,15 +7,14 @@ from decimal import Decimal from itertools import islice, chain -from functools import wraps +from functools import lru_cache, wraps from operator import itemgetter from collections import defaultdict from trytond.exceptions import UserError from trytond.model import Model from trytond.model import fields -from trytond.tools import reduce_domain, memoize, is_instance_method, \ - grouped_slice +from trytond.tools import reduce_domain, is_instance_method, grouped_slice from trytond.tools.domain_inversion import ( domain_inversion, parse as domain_parse) from trytond.pyson import PYSONEncoder, PYSONDecoder, PYSON @@ -696,7 +695,7 @@ ''' pool = Pool() - @memoize(1000) + @lru_cache(maxsize=1000) def get_many2one(relation, value): if not value: return None @@ -718,7 +717,7 @@ res = res[0].id return res - @memoize(1000) + @lru_cache(maxsize=1000) def get_many2many(relation, value): if not value: return None @@ -748,7 +747,7 @@ def get_one2one(relation, value): return ('add', get_many2one(relation, value)) - @memoize(1000) + @lru_cache(maxsize=1000) def get_reference(value, field): if not value: return None @@ -758,7 +757,7 @@ raise ImportDataError( gettext('ir.msg_reference_syntax_error', value=value, - field='/'.join(field))) + field=field)) Relation = pool.get(relation) res = Relation.search([ ('rec_name', '=', value), @@ -777,7 +776,7 @@ res = '%s,%s' % (relation, res[0].id) return res - @memoize(1000) + @lru_cache(maxsize=1000) def get_by_id(value, field, ftype): if not value: return None @@ -792,7 +791,7 @@ raise ImportDataError( gettext('ir.msg_reference_syntax_error', value=value, - field='/'.join(field))) + field=field)) value = [value] else: value = [value] @@ -804,7 +803,7 @@ raise ImportDataError( gettext('ir.msg_xml_id_syntax_error', value=word, - field='/'.join(field))) + field=field)) db_id = ModelData.get_id(module, xml_id) res_ids.append(db_id) if ftype == 'many2many' and res_ids: @@ -838,7 +837,8 @@ value = line[i] if is_prefix_len and field[-1].endswith(':id'): ftype = fields_def[field[-1][:-3]]['type'] - row[field[0][:-3]] = get_by_id(value, field, ftype) + row[field[0][:-3]] = get_by_id( + value, '/'.join(field), ftype) elif is_prefix_len and ':lang=' in field[-1]: field_name, lang = field[-1].split(':lang=') translate.setdefault(lang, {})[field_name] = value or False @@ -904,7 +904,7 @@ elif field_type == 'one2one': res = get_one2one(this_field_def['relation'], value) elif field_type == 'reference': - res = get_reference(value, field) + res = get_reference(value, '/'.join(field)) else: res = value or None row[field[-1]] = res diff -r 87b102b5b4ab -r 392e44a1e0b8 trytond/tools/__init__.py --- a/trytond/tools/__init__.py Tue Mar 24 10:02:58 2020 +0100 +++ b/trytond/tools/__init__.py Wed Mar 25 17:34:35 2020 +0100 @@ -2,12 +2,12 @@ # this repository contains the full copyright notices and license terms. from .misc import ( - file_open, get_smtp_server, memoize, reduce_ids, reduce_domain, + file_open, get_smtp_server, reduce_ids, reduce_domain, grouped_slice, is_instance_method, resolve, strip_wildcard, lstrip_wildcard, rstrip_wildcard, slugify) from .decimal_ import decistmt -__all__ = ['file_open', 'get_smtp_server', 'memoize', 'reduce_ids', +__all__ = ['file_open', 'get_smtp_server', 'reduce_ids', 'reduce_domain', 'grouped_slice', 'is_instance_method', 'resolve', 'strip_wildcard', 'lstrip_wildcard', 'rstrip_wildcard', 'slugify', 'decistmt', 'ClassProperty', 'cursor_dict'] diff -r 87b102b5b4ab -r 392e44a1e0b8 trytond/tools/misc.py --- a/trytond/tools/misc.py Tue Mar 24 10:02:58 2020 +0100 +++ b/trytond/tools/misc.py Wed Mar 25 17:34:35 2020 +0100 @@ -90,69 +90,6 @@ return get_smtp_server() -def memoize(maxsize): - """ - Decorator to 'memoize' a function - caching its results with a - near LRU implementation. - - The cache keeps a list of keys logicaly separated in 4 segment : - - segment 1 | ... | segment4 - [k,k,k,k,k,k,k, .. ,k,k,k,k,k,k,k] - - For each segment there is a pointer that loops on it. When a key - is accessed from the cache it is promoted to the first segment (at - the pointer place of segment one), the key under the pointer is - moved to the next segment, the pointer is then incremented and so - on. A key that is removed from the last segment is removed from - the cache. - - :param: maxsize the size of the cache (must be greater than or - equal to 4) - """ - assert maxsize >= 4, "Memoize cannot work if maxsize is less than 4" - - def wrap(fct): - cache = {} - keys = [None for i in range(maxsize)] - seg_size = maxsize // 4 - - pointers = [i * seg_size for i in range(4)] - max_pointers = [(i + 1) * seg_size for i in range(3)] + [maxsize] - - def wrapper(*args): - key = repr(args) - res = cache.get(key) - if res: - pos, res = res - keys[pos] = None - else: - res = fct(*args) - - value = res - for segment, pointer in enumerate(pointers): - newkey = keys[pointer] - keys[pointer] = key - cache[key] = (pointer, value) - - pointers[segment] = pointer + 1 - if pointers[segment] == max_pointers[segment]: - pointers[segment] = segment * seg_size - - if newkey is None: - break - segment, value = cache.pop(newkey) - key = newkey - - return res - - wrapper.__doc__ = fct.__doc__ - wrapper.__name__ = fct.__name__ - - return wrapper - return wrap - - def reduce_ids(field, ids): ''' Return a small SQL expression for the list of ids and the sql column