summaryrefslogtreecommitdiff
path: root/openerp/modules/module.py
diff options
context:
space:
mode:
authorVo Minh Thu <vmt@openerp.com>2012-01-09 13:52:43 +0100
committerVo Minh Thu <vmt@openerp.com>2012-01-09 13:52:43 +0100
commitd348a0de79139db57e5d736e36b44ef7fa35d494 (patch)
treefc4840623fbdcad636e1981bf72f2e6526894d88 /openerp/modules/module.py
parent455e47e5e83bbf70203f7513b876c420bd6abb06 (diff)
[IMP] import hook: moved to a proper location (openerp.modules.module instead of openerp-server).
bzr revid: vmt@openerp.com-20120109125243-qhtr070dvtc3akb5
Diffstat (limited to 'openerp/modules/module.py')
-rw-r--r--openerp/modules/module.py87
1 files changed, 86 insertions, 1 deletions
diff --git a/openerp/modules/module.py b/openerp/modules/module.py
index 5c50384c2bc..1e46d3c8478 100644
--- a/openerp/modules/module.py
+++ b/openerp/modules/module.py
@@ -56,6 +56,91 @@ loaded = []
logger = netsvc.Logger()
+class AddonsImportHook(object):
+ """
+ Import hook to load OpenERP addons from multiple paths.
+
+ OpenERP implements its own import-hook to load its addons. OpenERP
+ addons are Python modules. Originally, they were each living in their
+ own top-level namespace, e.g. the sale module, or the hr module. For
+ backward compatibility, `import <module>` is still supported. Now they
+ are living in `openerp.addons`. The good way to import such modules is
+ thus `import openerp.addons.module`.
+
+ For backward compatibility, loading an addons puts it in `sys.modules`
+ under both the legacy (short) name, and the new (longer) name. This
+ ensures that
+ import hr
+ import openerp.addons.hr
+ loads the hr addons only once.
+
+ When an OpenERP addons name clashes with some other installed Python
+ module (for instance this is the case of the `resource` addons),
+ obtaining the OpenERP addons is only possible with the long name. The
+ short name will give the expected Python module.
+
+ Instead of relying on some addons path, an alternative approach would be
+ to use pkg_resources entry points from already installed Python libraries
+ (and install our addons as such). Even when implemented, we would still
+ have to support the addons path approach for backward compatibility.
+ """
+
+ def find_module(self, module_name, package_path):
+ module_parts = module_name.split('.')
+ if len(module_parts) == 3 and module_name.startswith('openerp.addons.'):
+ return self # We act as a loader too.
+
+ # TODO list of loadable modules can be cached instead of always
+ # calling get_module_path().
+ if len(module_parts) == 1 and \
+ get_module_path(module_parts[0],
+ display_warning=False):
+ try:
+ # Check if the bare module name clashes with another module.
+ f, path, descr = imp.find_module(module_parts[0])
+ logger = logging.getLogger('init')
+ logger.warning("""
+Ambiguous import: the OpenERP module `%s` is shadowed by another
+module (available at %s).
+To import it, use `import openerp.addons.<module>.`.""" % (module_name, path))
+ return
+ except ImportError, e:
+ # Using `import <module_name>` instead of
+ # `import openerp.addons.<module_name>` is ugly but not harmful
+ # and kept for backward compatibility.
+ return self # We act as a loader too.
+
+ def load_module(self, module_name):
+
+ module_parts = module_name.split('.')
+ if len(module_parts) == 3 and module_name.startswith('openerp.addons.'):
+ module_part = module_parts[2]
+ if module_name in sys.modules:
+ return sys.modules[module_name]
+
+ if len(module_parts) == 1:
+ module_part = module_parts[0]
+ if module_part in sys.modules:
+ return sys.modules[module_part]
+
+ try:
+ # Check if the bare module name shadows another module.
+ f, path, descr = imp.find_module(module_part)
+ is_shadowing = True
+ except ImportError, e:
+ # Using `import <module_name>` instead of
+ # `import openerp.addons.<module_name>` is ugly but not harmful
+ # and kept for backward compatibility.
+ is_shadowing = False
+
+ # Note: we don't support circular import.
+ f, path, descr = imp.find_module(module_part, ad_paths)
+ mod = imp.load_module(module_name, f, path, descr)
+ if not is_shadowing:
+ sys.modules[module_part] = mod
+ sys.modules['openerp.addons.' + module_part] = mod
+ return mod
+
def initialize_sys_path():
""" Add all addons paths in sys.path.
@@ -68,7 +153,7 @@ def initialize_sys_path():
ad_paths = map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
ad_paths.append(_ad) # for get_module_path
-
+ sys.meta_path.append(AddonsImportHook())
def get_module_path(module, downloaded=False, display_warning=True):
"""Return the path of the given module.