##// END OF EJS Templates
discovery: rename 'error' to 'errormsg'...
discovery: rename 'error' to 'errormsg' The variable 'error' conflict with the module name that we would like to import and use in a coming changeset.

File last commit:

r26457:7e813050 default
r26585:b38d9373 default
Show More
demandimport.py
265 lines | 8.9 KiB | text/x-python | PythonLexer
Matt Mackall
Replace demandload with new demandimport
r3877 # demandimport.py - global demand-loading of modules for Mercurial
#
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
Matt Mackall
Replace demandload with new demandimport
r3877 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Matt Mackall
Replace demandload with new demandimport
r3877
'''
demandimport - automatic demandloading of modules
To enable this module, do:
import demandimport; demandimport.enable()
Imports of the following forms will be demand-loaded:
import a, b.c
import a.b as c
from a import b,c # a will be loaded immediately
These imports will not be delayed:
from a import *
b = __import__(a)
'''
Gregory Szorc
demandimport: use absolute_import
r25943 from __future__ import absolute_import
import contextlib
import os
import sys
Gregory Szorc
demandimport: support importing builtins for Python 3...
r25674
# __builtin__ in Python 2, builtins in Python 3.
try:
import __builtin__ as builtins
except ImportError:
import builtins
Jordi GutiƩrrez Hermoso
demandimport: define a `deactivated` context manager...
r25327
Gregory Szorc
demandimport: use absolute_import
r25943 contextmanager = contextlib.contextmanager
Matt Mackall
Replace demandload with new demandimport
r3877 _origimport = __import__
Augie Fackler
demandimport: use getattr instead of hasattr...
r14977 nothing = object()
Gregory Szorc
demandimport: remove support for Python < 2.5...
r25933 # Python 3 doesn't have relative imports nor level -1.
level = -1
if sys.version_info[0] >= 3:
level = 0
_import = _origimport
Simon Heimberg
demandimport: determine at load time if __import__ has level argument
r15096
Gregory Szorc
demandimport: support keyword arguments on _hgextimport...
r25936 def _hgextimport(importfunc, name, globals, *args, **kwargs):
FUJIWARA Katsunori
demandimport: allow extensions to import own modules by absolute name...
r19933 try:
Gregory Szorc
demandimport: support keyword arguments on _hgextimport...
r25936 return importfunc(name, globals, *args, **kwargs)
FUJIWARA Katsunori
demandimport: allow extensions to import own modules by absolute name...
r19933 except ImportError:
if not globals:
raise
# extensions are loaded with "hgext_" prefix
hgextname = 'hgext_%s' % name
nameroot = hgextname.split('.', 1)[0]
contextroot = globals.get('__name__', '').split('.', 1)[0]
if nameroot != contextroot:
raise
# retry to import with "hgext_" prefix
Gregory Szorc
demandimport: support keyword arguments on _hgextimport...
r25936 return importfunc(hgextname, globals, *args, **kwargs)
FUJIWARA Katsunori
demandimport: allow extensions to import own modules by absolute name...
r19933
Matt Mackall
Replace demandload with new demandimport
r3877 class _demandmod(object):
"""module demand-loader and proxy"""
Gregory Szorc
demandimport: pass proper level to __import__ in Python 3...
r21290 def __init__(self, name, globals, locals, level=level):
Matt Mackall
Replace demandload with new demandimport
r3877 if '.' in name:
head, rest = name.split('.', 1)
after = [rest]
else:
head = name
after = []
FUJIWARA Katsunori
demandimport: support "absolute_import" for external libraries (issue4029)...
r19932 object.__setattr__(self, "_data",
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457 (head, globals, locals, after, level, set()))
Benoit Boissinot
use parent.__setattr__ instead of __dict__
r3896 object.__setattr__(self, "_module", None)
Matt Mackall
Replace demandload with new demandimport
r3877 def _extend(self, name):
"""add to the list of submodules to load"""
self._data[3].append(name)
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457
def _addref(self, name):
"""Record that the named module ``name`` imports this module.
References to this proxy class having the name of this module will be
replaced at module load time. We assume the symbol inside the importing
module is identical to the "head" name of this module. We don't
actually know if "as X" syntax is being used to change the symbol name
because this information isn't exposed to __import__.
"""
self._data[5].add(name)
Matt Mackall
Replace demandload with new demandimport
r3877 def _load(self):
if not self._module:
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457 head, globals, locals, after, level, modrefs = self._data
FUJIWARA Katsunori
demandimport: allow extensions to import own modules by absolute name...
r19933 mod = _hgextimport(_import, head, globals, locals, None, level)
Matt Mackall
Replace demandload with new demandimport
r3877 # load submodules
Matt Mackall
demandimport: fix import x.y.z as a when x.y is already imported.
r3921 def subload(mod, p):
h, t = p, None
if '.' in p:
h, t = p.split('.', 1)
Augie Fackler
demandimport: use getattr instead of hasattr...
r14977 if getattr(mod, h, nothing) is nothing:
Matt Mackall
demandimport: back out 50a4e55aa278 (issue2467)
r12894 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
Brendan Cully
demandimport: handle already-loaded nested modules in subload
r3926 elif t:
Matt Mackall
demandimport: fix import x.y.z as a when x.y is already imported.
r3921 subload(getattr(mod, h), t)
Matt Mackall
Replace demandload with new demandimport
r3877 for x in after:
Matt Mackall
demandimport: fix import x.y.z as a when x.y is already imported.
r3921 subload(mod, x)
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457 # Replace references to this proxy instance with the actual module.
Matt Mackall
Replace demandload with new demandimport
r3877 if locals and locals.get(head) == self:
locals[head] = mod
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457
for modname in modrefs:
modref = sys.modules.get(modname, None)
if modref and getattr(modref, head, None) == self:
setattr(modref, head, mod)
Benoit Boissinot
use parent.__setattr__ instead of __dict__
r3896 object.__setattr__(self, "_module", mod)
Matt Mackall
demandimport: fix issue579 and add a test...
r4631
Matt Mackall
Replace demandload with new demandimport
r3877 def __repr__(self):
Matt Mackall
demandimport: fix issue579 and add a test...
r4631 if self._module:
return "<proxied module '%s'>" % self._data[0]
Matt Mackall
Replace demandload with new demandimport
r3877 return "<unloaded module '%s'>" % self._data[0]
def __call__(self, *args, **kwargs):
Matt Mackall
demandload: give better diagnostic for call of an unloaded module
r5639 raise TypeError("%s object is not callable" % repr(self))
Brendan Cully
Make demandimport pass all tests on python2.5.
r3903 def __getattribute__(self, attr):
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457 if attr in ('_data', '_extend', '_load', '_module', '_addref'):
Brendan Cully
Make demandimport pass all tests on python2.5.
r3903 return object.__getattribute__(self, attr)
Matt Mackall
Replace demandload with new demandimport
r3877 self._load()
return getattr(self._module, attr)
def __setattr__(self, attr, val):
self._load()
setattr(self._module, attr, val)
Gregory Szorc
demandimport: pass proper level to __import__ in Python 3...
r21290 def _demandimport(name, globals=None, locals=None, fromlist=None, level=level):
Matt Mackall
Replace demandload with new demandimport
r3877 if not locals or name in ignore or fromlist == ('*',):
# these cases we can't really delay
FUJIWARA Katsunori
demandimport: allow extensions to import own modules by absolute name...
r19933 return _hgextimport(_import, name, globals, locals, fromlist, level)
Matt Mackall
Replace demandload with new demandimport
r3877 elif not fromlist:
# import a [as b]
if '.' in name: # a.b
base, rest = name.split('.', 1)
Brendan Cully
Make demandimport pass all tests on python2.5.
r3903 # email.__init__ loading email.mime
if globals and globals.get('__name__', None) == base:
Simon Heimberg
demandimport: determine at load time if __import__ has level argument
r15096 return _import(name, globals, locals, fromlist, level)
Matt Mackall
Replace demandload with new demandimport
r3877 # if a is already demand-loaded, add b to its submodule list
if base in locals:
if isinstance(locals[base], _demandmod):
locals[base]._extend(rest)
return locals[base]
FUJIWARA Katsunori
demandimport: support "absolute_import" for external libraries (issue4029)...
r19932 return _demandmod(name, globals, locals, level)
Matt Mackall
Replace demandload with new demandimport
r3877 else:
Gregory Szorc
demandimport: refactor logic and add documentation...
r25935 # There is a fromlist.
# from a import b,c,d
# from . import b,c,d
# from .a import b,c,d
# level == -1: relative and absolute attempted (Python 2 only).
# level >= 0: absolute only (Python 2 w/ absolute_import and Python 3).
# The modern Mercurial convention is to use absolute_import everywhere,
# so modern Mercurial code will have level >= 0.
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457 # The name of the module the import statement is located in.
globalname = globals.get('__name__')
Gregory Szorc
demandimport: consolidate code for processing items in fromlist...
r26455 def processfromitem(mod, attr, **kwargs):
"""Process an imported symbol in the import statement.
If the symbol doesn't exist in the parent module, it must be a
module. We set missing modules up as _demandmod instances.
"""
Gregory Szorc
demandimport: refactor processfromitem...
r26456 symbol = getattr(mod, attr, nothing)
if symbol is nothing:
symbol = _demandmod(attr, mod.__dict__, locals, **kwargs)
setattr(mod, attr, symbol)
Gregory Szorc
demandimport: consolidate code for processing items in fromlist...
r26455
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457 # Record the importing module references this symbol so we can
# replace the symbol with the actual module instance at load
# time.
if globalname and isinstance(symbol, _demandmod):
symbol._addref(globalname)
Gregory Szorc
demandimport: refactor logic and add documentation...
r25935 if level >= 0:
Gregory Szorc
demandimport: support lazy loading for absolute_import...
r25937 # Mercurial's enforced import style does not use
# "from a import b,c,d" or "from .a import b,c,d" syntax. In
# addition, this appears to be giving errors with some modules
# for unknown reasons. Since we shouldn't be using this syntax
# much, work around the problems.
if name:
return _hgextimport(_origimport, name, globals, locals,
fromlist, level)
mod = _hgextimport(_origimport, name, globals, locals, level=level)
Gregory Szorc
demandimport: consolidate code for processing items in fromlist...
r26455
Gregory Szorc
demandimport: support lazy loading for absolute_import...
r25937 for x in fromlist:
Gregory Szorc
demandimport: consolidate code for processing items in fromlist...
r26455 processfromitem(mod, x, level=level)
Gregory Szorc
demandimport: support lazy loading for absolute_import...
r25937
return mod
Gregory Szorc
demandimport: refactor logic and add documentation...
r25935
# But, we still need to support lazy loading of standard library and 3rd
# party modules. So handle level == -1.
FUJIWARA Katsunori
demandimport: allow extensions to import own modules by absolute name...
r19933 mod = _hgextimport(_origimport, name, globals, locals)
Matt Mackall
Replace demandload with new demandimport
r3877 # recurse down the module chain
for comp in name.split('.')[1:]:
Augie Fackler
demandimport: use getattr instead of hasattr...
r14977 if getattr(mod, comp, nothing) is nothing:
Gregory Szorc
demandimport: refactor logic and add documentation...
r25935 setattr(mod, comp,
_demandmod(comp, mod.__dict__, mod.__dict__))
Matt Mackall
Replace demandload with new demandimport
r3877 mod = getattr(mod, comp)
Gregory Szorc
demandimport: consolidate code for processing items in fromlist...
r26455
Matt Mackall
Replace demandload with new demandimport
r3877 for x in fromlist:
Gregory Szorc
demandimport: consolidate code for processing items in fromlist...
r26455 processfromitem(mod, x)
Matt Mackall
Replace demandload with new demandimport
r3877 return mod
Patrick Mezard
demandimport: ignore resource module, not available under Windows.
r5098 ignore = [
Gregory Szorc
demandimport: add __future__ to ignore list...
r25934 '__future__',
Patrick Mezard
demandimport: ignore resource module, not available under Windows.
r5098 '_hashlib',
'_xmlplus',
'fcntl',
'win32com.gen_py',
Dirkjan Ochtman
demandimport: ignore _winreg (used in python-2.7 mimetypes)
r10242 '_winreg', # 2.7 mimetypes needs immediate ImportError
Steve Borho
demandimport: blacklist pythoncom...
r7861 'pythoncom',
Patrick Mezard
demandimport: ignore resource module, not available under Windows.
r5098 # imported by tarfile, not available under Windows
'pwd',
'grp',
# imported by profile, itself imported by hotshot.stats,
# not available under Windows
'resource',
Steve Borho
demandimport: blacklist gtk...
r9458 # this trips up many extension authors
'gtk',
Greg Ward
demandimport: add __main__ to the blacklist (because of setuptools)
r10598 # setuptools' pkg_resources.py expects "from __main__ import x" to
# raise ImportError if x not defined
'__main__',
Dirkjan Ochtman
demandimport: blacklist _ssl (issue1964)
r10612 '_ssl', # conditional imports in the stdlib, issue1964
Augie Fackler
demandimport: blacklist rfc822 and mimetools to prevent spurious warnings
r14976 'rfc822',
'mimetools',
Augie Fackler
demandimport: blacklist distutils.msvc9compiler (issue4475)...
r23643 # setuptools 8 expects this module to explode early when not on windows
'distutils.msvc9compiler'
Patrick Mezard
demandimport: ignore resource module, not available under Windows.
r5098 ]
Matt Mackall
Replace demandload with new demandimport
r3877
Brodie Rao
hooks: only disable/re-enable demandimport when it's already enabled...
r20422 def isenabled():
Gregory Szorc
demandimport: alias __builtin__ as builtins...
r25673 return builtins.__import__ == _demandimport
Brodie Rao
hooks: only disable/re-enable demandimport when it's already enabled...
r20422
Matt Mackall
Replace demandload with new demandimport
r3877 def enable():
"enable global demand-loading of modules"
Mads Kiilerich
demandimport: make it possible to disable by setting HGDEMANDIMPORT=disable...
r21025 if os.environ.get('HGDEMANDIMPORT') != 'disable':
Gregory Szorc
demandimport: alias __builtin__ as builtins...
r25673 builtins.__import__ = _demandimport
Matt Mackall
Replace demandload with new demandimport
r3877
def disable():
"disable global demand-loading of modules"
Gregory Szorc
demandimport: alias __builtin__ as builtins...
r25673 builtins.__import__ = _origimport
Jordi GutiƩrrez Hermoso
demandimport: define a `deactivated` context manager...
r25327
@contextmanager
def deactivated():
"context manager for disabling demandimport in 'with' blocks"
demandenabled = isenabled()
if demandenabled:
disable()
try:
yield
finally:
if demandenabled:
enable()