diff --git a/odoo/addons/base/ir/ir_actions.py b/odoo/addons/base/ir/ir_actions.py index 862d288ac5b..bacca6d78d3 100644 --- a/odoo/addons/base/ir/ir_actions.py +++ b/odoo/addons/base/ir/ir_actions.py @@ -5,13 +5,12 @@ import odoo from odoo import api, fields, models, tools, SUPERUSER_ID, _ from odoo.exceptions import MissingError, UserError, ValidationError, AccessError from odoo.tools.safe_eval import safe_eval, test_python_expr -from odoo.tools import pycompat +from odoo.tools import pycompat, wrap_module from odoo.http import request import base64 from collections import defaultdict import datetime -import dateutil import logging import time @@ -19,6 +18,16 @@ from pytz import timezone _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 IrActions(models.Model): _name = 'ir.actions.actions' diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py index 17518ce1525..a036932f6fc 100644 --- a/odoo/tools/misc.py +++ b/odoo/tools/misc.py @@ -1151,3 +1151,23 @@ pickle.load = _pickle_load pickle.loads = lambda text, encoding='ASCII': _pickle_load(io.BytesIO(text), encoding=encoding) pickle.dump = pickle_.dump pickle.dumps = pickle_.dumps + +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()