| # Copyright 2009 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| # $Id$ |
| |
| __all__ = ['lazyimport'] |
| |
| import sys |
| import types |
| |
| try: |
| import threading |
| except ImportError: |
| import dummy_threading as threading |
| |
| from portage.proxy.objectproxy import ObjectProxy |
| |
| if sys.hexversion >= 0x3000000: |
| basestring = str |
| |
| _module_proxies = {} |
| _module_proxies_lock = threading.RLock() |
| |
| def _register_module_proxy(name, proxy): |
| _module_proxies_lock.acquire() |
| try: |
| proxy_list = _module_proxies.get(name) |
| if proxy_list is None: |
| proxy_list = [] |
| _module_proxies[name] = proxy_list |
| proxy_list.append(proxy) |
| finally: |
| _module_proxies_lock.release() |
| |
| def _unregister_module_proxy(name): |
| """ |
| Destroy all proxies that reference the give module name. Also, check |
| for other proxies referenced by modules that have been imported and |
| destroy those proxies too. This way, destruction of a single proxy |
| can trigger destruction of all the rest. |
| """ |
| _module_proxies_lock.acquire() |
| try: |
| proxy_list = _module_proxies.get(name) |
| if proxy_list is not None: |
| # First delete this name from the dict so that |
| # if this same thread reenters below, it won't |
| # enter this path again. |
| del _module_proxies[name] |
| for proxy in proxy_list: |
| object.__getattribute__(proxy, '_get_target')() |
| |
| modules = sys.modules |
| for name, proxy_list in list(_module_proxies.items()): |
| if name not in modules: |
| continue |
| # First delete this name from the dict so that |
| # if this same thread reenters below, it won't |
| # enter this path again. |
| del _module_proxies[name] |
| for proxy in proxy_list: |
| object.__getattribute__(proxy, '_get_target')() |
| finally: |
| _module_proxies_lock.release() |
| |
| class _LazyImport(ObjectProxy): |
| |
| __slots__ = ('_scope', '_alias', '_name', '_target') |
| |
| def __init__(self, scope, alias, name): |
| ObjectProxy.__init__(self) |
| object.__setattr__(self, '_scope', scope) |
| object.__setattr__(self, '_alias', alias) |
| object.__setattr__(self, '_name', name) |
| _register_module_proxy(name, self) |
| |
| def _get_target(self): |
| try: |
| return object.__getattribute__(self, '_target') |
| except AttributeError: |
| pass |
| name = object.__getattribute__(self, '_name') |
| __import__(name) |
| target = sys.modules[name] |
| object.__setattr__(self, '_target', target) |
| object.__getattribute__(self, '_scope')[ |
| object.__getattribute__(self, '_alias')] = target |
| _unregister_module_proxy(name) |
| return target |
| |
| class _LazyImportFrom(_LazyImport): |
| |
| __slots__ = ('_attr_name',) |
| |
| def __init__(self, scope, name, attr_name, alias): |
| object.__setattr__(self, '_attr_name', attr_name) |
| _LazyImport.__init__(self, scope, alias, name) |
| |
| def _get_target(self): |
| try: |
| return object.__getattribute__(self, '_target') |
| except AttributeError: |
| pass |
| name = object.__getattribute__(self, '_name') |
| attr_name = object.__getattribute__(self, '_attr_name') |
| __import__(name) |
| target = getattr(sys.modules[name], attr_name) |
| object.__setattr__(self, '_target', target) |
| object.__getattribute__(self, '_scope')[ |
| object.__getattribute__(self, '_alias')] = target |
| _unregister_module_proxy(name) |
| return target |
| |
| def lazyimport(scope, *args): |
| """ |
| Create a proxy in the given scope in order to performa a lazy import. |
| |
| Syntax Result |
| foo import foo |
| foo:bar,baz from foo import bar, baz |
| foo:bar@baz from foo import bar as baz |
| |
| @param scope: the scope in which to place the import, typically globals() |
| @type myfilename: dict |
| @param args: module names to import |
| @type args: strings |
| """ |
| |
| modules = sys.modules |
| |
| for s in args: |
| parts = s.split(':', 1) |
| if len(parts) == 1: |
| name = s |
| |
| if not name or not isinstance(name, basestring): |
| raise ValueError(name) |
| |
| components = name.split('.') |
| parent_scope = scope |
| for i in range(len(components)): |
| alias = components[i] |
| if i < len(components) - 1: |
| parent_name = ".".join(components[:i+1]) |
| __import__(parent_name) |
| mod = modules.get(parent_name) |
| if not isinstance(mod, types.ModuleType): |
| # raise an exception |
| __import__(name) |
| parent_scope[alias] = mod |
| parent_scope = mod.__dict__ |
| continue |
| |
| already_imported = modules.get(name) |
| if already_imported is not None: |
| parent_scope[alias] = already_imported |
| else: |
| parent_scope[alias] = \ |
| _LazyImport(parent_scope, alias, name) |
| |
| else: |
| name, fromlist = parts |
| already_imported = modules.get(name) |
| fromlist = fromlist.split(',') |
| for s in fromlist: |
| alias = s.split('@', 1) |
| if len(alias) == 1: |
| alias = alias[0] |
| attr_name = alias |
| else: |
| attr_name, alias = alias |
| if already_imported is not None: |
| try: |
| scope[alias] = getattr(already_imported, attr_name) |
| except AttributeError: |
| raise ImportError('cannot import name %s' % attr_name) |
| else: |
| scope[alias] = \ |
| _LazyImportFrom(scope, name, attr_name, alias) |