[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.
This commit is contained in:
Olivier Dony
2018-07-23 16:15:10 +02:00
parent 91486656ab
commit 779ceac973
2 changed files with 32 additions and 2 deletions
+11 -2
View File
@@ -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'
+21
View File
@@ -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 <module>``
: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()