From 779ceac97309fd69086446bc14e6d613aeb9bbdd Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 23 Jul 2018 16:15:10 +0200 Subject: [PATCH] [FIX] base: always preload dateutil The dateutil package uses lazy import to selectively expose its features: `parser`, `relativedelta`, `rrule`, `tz`. Depending on installed modules and order of initialization, there was no guarantee that a given feature was already loaded during the preparation of the action context. This patch ensures that we always preload the feature set we need, and only that feature set. This way we have a consistent `dateutil` lib in the action context at all times. --- openerp/addons/base/ir/ir_actions.py | 13 +++++++++++-- openerp/tools/misc.py | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index 1c03a7ec5b6..d3e14d68ae1 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -7,7 +7,6 @@ import operator import os import time import datetime -import dateutil import pytz import openerp @@ -19,7 +18,7 @@ from openerp.osv import fields, osv from openerp.osv.orm import browse_record import openerp.report.interface from openerp.report.report_sxw import report_sxw, report_rml -from openerp.tools import ormcache +from openerp.tools import ormcache, wrap_module from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ import openerp.workflow @@ -28,6 +27,16 @@ from openerp.exceptions import MissingError, UserError _logger = logging.getLogger(__name__) +# build dateutil helper, starting with the relevant *lazy* imports +import dateutil +import dateutil.parser +import dateutil.relativedelta +import dateutil.rrule +import dateutil.tz +mods = {'parser', 'relativedelta', 'rrule', 'tz'} +attribs = {atr for m in mods for atr in getattr(dateutil, m).__all__} +dateutil = wrap_module(dateutil, mods | attribs) + class actions(osv.osv): _name = 'ir.actions.actions' _table = 'ir_actions' diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index 74b89f1c2c1..c7cf97eba86 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -19,6 +19,7 @@ import socket import sys import threading import time +import types import werkzeug.utils import zipfile from cStringIO import StringIO @@ -1234,3 +1235,23 @@ class Pickle(object): dump = cPickle.dump pickle = Pickle + +def wrap_module(module, attr_list): + """Helper for wrapping a package/module to expose selected attributes + + :param Module module: the actual package/module to wrap, as returned by ``import `` + :param iterable attr_list: a global list of attributes to expose, usually the top-level + attributes and their own main attributes. No support for hiding attributes in case + of name collision at different levels. + """ + attr_list = set(attr_list) + class WrappedModule(object): + def __getattr__(self, attrib): + if attrib in attr_list: + target = getattr(module, attrib) + if isinstance(target, types.ModuleType): + return wrap_module(target, attr_list) + return target + raise AttributeError(attrib) + # module and attr_list are in the closure + return WrappedModule()