From ac282e029420ec5ad44cc6c3b99784d24faa043b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 7 Jul 2014 13:51:53 +0200 Subject: [PATCH] [FIX] unsafe listification of weakref in Python < 2.7.4 Fixes #966 * As a preallocation optimization, ``list()`` calls ``__len__`` on its parameter if it's available * Before Python 2.7.4, WeakSet has a bug[0] where ``len()`` is unsafe: it is done by iteration and weakrefs may be removed from the underlying set during the iteration As a result, the safety feature of listifying a WeakSet to ensure we have strong refs on all items during iteration may blow up. Wrapping the weakset in a ``iter()`` makes ``__len__()`` invisible and ensures we're within the IterationGuard[1]. Which now that I think about it means we *should* be able to safely iterate weaksets in the first place and may not have needed to listify them... [0] http://bugs.python.org/issue14159 [1] http://hg.python.org/cpython/file/b6acfbe2bdbe/Lib/_weakrefset.py#l58 --- openerp/api.py | 4 ++-- openerp/models.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openerp/api.py b/openerp/api.py index 5c8b93892b7..6c70910633f 100644 --- a/openerp/api.py +++ b/openerp/api.py @@ -756,7 +756,7 @@ class Environment(object): """ if not spec: return - for env in list(self.all): + for env in list(iter(self.all)): c = env.cache for field, ids in spec: if ids is None: @@ -769,7 +769,7 @@ class Environment(object): def invalidate_all(self): """ Clear the cache of all environments. """ - for env in list(self.all): + for env in list(iter(self.all)): env.cache.clear() env.prefetch.clear() env.computed.clear() diff --git a/openerp/models.py b/openerp/models.py index 9e289965733..9307b82cee9 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -5415,7 +5415,7 @@ class BaseModel(object): """ If `field` must be recomputed on some record in `self`, return the corresponding records that must be recomputed. """ - for env in [self.env] + list(self.env.all): + for env in [self.env] + list(iter(self.env.all)): if env.todo.get(field) and env.todo[field] & self: return env.todo[field] @@ -5437,7 +5437,7 @@ class BaseModel(object): """ Recompute stored function fields. The fields and records to recompute have been determined by method :meth:`modified`. """ - for env in list(self.env.all): + for env in list(iter(self.env.all)): while env.todo: field, recs = next(env.todo.iteritems()) # evaluate the fields to recompute, and save them to database