version, name = name.split('.') version = version[:-self.__version_suffix_len] except ValueError, error: raise ImportError("%s: should be %s.VERSION.COMPONENT_REFERENCE (%s)" % \ (fullname, self._namespace, error)) component_id = "%s.%s.%s" % (self._id_prefix, version, name) # Otherwise, find the Component with the highest version priority else: component_tool = aq_base(site.portal_components) # Version priority name list is ordered in descending order for version in site.getVersionPriorityNameList(): component_id = "%s.%s.%s" % (self._id_prefix, version, name) component = getattr(component_tool, component_id, None) if component is not None and component.getValidationState() in ('modified', 'validated'): break else: raise ImportError("%s: no version of Component %s in Site priority" % \ (fullname, name)) module_fullname_alias = self._namespace + '.' + name # Check whether this module has already been loaded before for a # specific version, if so, just add it to the upper level try: module = getattr(getattr(self, version + '_version'), name) except AttributeError: pass else:
def find_module(self, fullname, path=None): """ PEP-302 Finder which determines which packages and modules will be handled by this class. It must be done carefully to avoid handling packages and modules the Loader (load_module()) will not be handled later as the latter would raise ImportError... As per PEP-302, returns None if this Finder cannot handle the given name, perhaps because the Finder of another Component Package could do it or because this is a filesystem module... """ import erp5.component # ZODB Components if not path: if not fullname.startswith(self._namespace_prefix): return None # FS import backward compatibility else: try: fullname = erp5.component.filesystem_import_dict[fullname] except (TypeError, KeyError): return None else: if not fullname.startswith(self._namespace_prefix): return None import_lock_held = True try: imp.release_lock() except RuntimeError: import_lock_held = False try: site = getSite() if erp5.component.filesystem_import_dict is None: filesystem_import_dict = {} try: component_tool = aq_base(site.portal_components) except AttributeError: # For old sites, just use FS Documents... return None else: for component in component_tool.objectValues(): if component.getValidationState() == 'validated': component_module_name = '%s.%s' % (component._getDynamicModuleNamespace(), component.getReference()) if component.getSourceReference() is not None: filesystem_import_dict[component.getSourceReference()] = component_module_name if component.getPortalType() == 'Document Component': filesystem_import_dict[('Products.ERP5Type.Document.' + component.getReference())] = component_module_name erp5.component.filesystem_import_dict = filesystem_import_dict # __import__ will first try a relative import, for example # erp5.component.XXX.YYY.ZZZ where erp5.component.XXX.YYY is the current # Component where an import is done name = fullname[len(self._namespace_prefix):] # name=VERSION_version.REFERENCE if '.' in name: try: version, name = name.split('.') version = version[:-self.__version_suffix_len] except ValueError: return None id_ = "%s.%s.%s" % (self._id_prefix, version, name) # aq_base() because this should not go up to ERP5Site and trigger # side-effects, after all this only check for existence... component = getattr(aq_base(site.portal_components), id_, None) if component is None or component.getValidationState() not in ('modified', 'validated'): return None # Skip unavailable components, otherwise Products for example could be # wrongly considered as importable and thus the actual filesystem class # ignored # # name=VERSION_version elif name.endswith('_version'): if name[:-self.__version_suffix_len] not in site.getVersionPriorityNameList(): return None # name=REFERENCE else: component_tool = aq_base(site.portal_components) for version in site.getVersionPriorityNameList(): id_ = "%s.%s.%s" % (self._id_prefix, version, name) component = getattr(component_tool, id_, None) if component is not None and component.getValidationState() in ('modified', 'validated'): break else: return None return self finally: # Internal release of import lock at the end of import machinery will # fail if the hook is not acquired if import_lock_held: imp.acquire_lock()
def __load_module(self, fullname): """ Load a module with given fullname (see PEP 302) if it's not already in sys.modules. It is assumed that imports are filtered properly in find_module(). Also, when the top-level Component module is requested (erp5.component.XXX.COMPONENT_NAME), the Component with the highest version priority will be loaded into the Version package (erp5.component.XXX.VERSION_version.COMPONENT_NAME. Therefore, the top-level Component module will just be an alias of the versioned one. As per PEP-302, raise an ImportError if the Loader could not load the module for any reason... """ site = getSite() if fullname.startswith('Products.'): module_fullname_filesystem = fullname import erp5.component fullname = erp5.component.filesystem_import_dict[module_fullname_filesystem] else: module_fullname_filesystem = None name = fullname[len(self._namespace_prefix):] # if only Version package (erp5.component.XXX.VERSION_version) is # requested to be loaded, then create it if necessary if name.endswith('_version'): version = name[:-self.__version_suffix_len] return (version in site.getVersionPriorityNameList() and self._getVersionPackage(version) or None) module_fullname_alias = None version_package_name = name[:-self.__version_suffix_len] # If a specific version of the Component has been requested if '.' in name: try: version, name = name.split('.') version = version[:-self.__version_suffix_len] except ValueError as error: raise ImportError("%s: should be %s.VERSION.COMPONENT_REFERENCE (%s)" % \ (fullname, self._namespace, error)) component_id = "%s.%s.%s" % (self._id_prefix, version, name) # Otherwise, find the Component with the highest version priority else: component_tool = aq_base(site.portal_components) # Version priority name list is ordered in descending order for version in site.getVersionPriorityNameList(): component_id = "%s.%s.%s" % (self._id_prefix, version, name) component = getattr(component_tool, component_id, None) if component is not None and component.getValidationState() in ('modified', 'validated'): break else: raise ImportError("%s: no version of Component %s in Site priority" % \ (fullname, name)) module_fullname_alias = self._namespace + '.' + name # Check whether this module has already been loaded before for a # specific version, if so, just add it to the upper level try: module = getattr(getattr(self, version + '_version'), name) except AttributeError: pass else: setattr(self, name, module) sys.modules[module_fullname_alias] = module MNAME_MAP[module_fullname_alias] = module.__name__ if module_fullname_filesystem: sys.modules[module_fullname_filesystem] = module MNAME_MAP[module_fullname_filesystem] = module.__name__ return module component = getattr(site.portal_components, component_id) relative_url = component.getRelativeUrl() module_fullname = '%s.%s_version.%s' % (self._namespace, version, name) module = ModuleType(module_fullname, component.getDescription()) source_code_str = component.getTextContent(validated_only=True) version_package = self._getVersionPackage(version) # All the required objects have been loaded, acquire import lock to modify # sys.modules and execute PEP302 requisites imp.acquire_lock() try: # The module *must* be in sys.modules before executing the code in case # the module code imports (directly or indirectly) itself (see PEP 302) sys.modules[module_fullname] = module if module_fullname_alias: sys.modules[module_fullname_alias] = module if module_fullname_filesystem: sys.modules[module_fullname_filesystem] = module # This must be set for imports at least (see PEP 302) module.__file__ = '<' + relative_url + '>' # Only useful for get_source(), do it before exec'ing the source code # so that the source code is properly display in case of error module.__loader__ = self module.__path__ = [] module.__name__ = module_fullname self.__fullname_source_code_dict[module_fullname] = source_code_str try: # XXX: Any loading from ZODB while exec'ing the source code will result # in a deadlock source_code_obj = compile(source_code_str, module.__file__, 'exec') exec(source_code_obj, module.__dict__) except Exception as error: del sys.modules[module_fullname] if module_fullname_alias: del sys.modules[module_fullname_alias] if module_fullname_filesystem: del sys.modules[module_fullname_filesystem] reraise(ImportError, "%s: cannot load Component %s (%s)" % (fullname, name, error), sys.exc_info()[2]) # Add the newly created module to the Version package and add it as an # alias to the top-level package as well setattr(version_package, name, module) if module_fullname_alias: setattr(self, name, module) MNAME_MAP[module_fullname_alias] = module_fullname if module_fullname_filesystem: MNAME_MAP[module_fullname_filesystem] = module.__name__ import erp5.component erp5.component.ref_manager.add_module(module) finally: imp.release_lock() component._hookAfterLoad(module) return module