##// END OF EJS Templates
stdio: add machinery to identify failed stdout/stderr writes...
stdio: add machinery to identify failed stdout/stderr writes Mercurial currently fails to notice failures to write to stdout or stderr. A correctly functioning command line tool should detect this and exit with an error code. To achieve this, we need a little extra plumbing, which we start adding here.

File last commit:

r31644:f80d9ddc default
r31959:b445a3f0 default
Show More
demandimport.py
330 lines | 11.6 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):
FUJIWARA Katsunori
demandimport: omit default value of "level" at construction of _demandmod...
r29737 """module demand-loader and proxy
Specify 1 as 'level' argument at construction, to import module
relatively.
"""
def __init__(self, name, globals, locals, level):
Matt Mackall
Replace demandload with new demandimport
r3877 if '.' in name:
head, rest = name.split('.', 1)
after = [rest]
else:
head = name
after = []
Yuya Nishihara
py3: abuse r'' to preserve str-ness of literals passed to __setattr__()
r31644 object.__setattr__(self, r"_data",
Gregory Szorc
demandimport: replace more references to _demandmod instances...
r26457 (head, globals, locals, after, level, set()))
Yuya Nishihara
py3: abuse r'' to preserve str-ness of literals passed to __setattr__()
r31644 object.__setattr__(self, r"_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)
FUJIWARA Katsunori
demandimport: avoid infinite recursion at actual module importing (issue5304)...
r29642 if mod is self:
# In this case, _hgextimport() above should imply
# _demandimport(). Otherwise, _hgextimport() never
# returns _demandmod. This isn't intentional behavior,
# in fact. (see also issue5304 for detail)
#
# If self._module is already bound at this point, self
# should be already _load()-ed while _hgextimport().
# Otherwise, there is no way to import actual module
# as expected, because (re-)invoking _hgextimport()
# should cause same result.
# This is reason why _load() returns without any more
# setup but assumes self to be already bound.
mod = self._module
assert mod and mod is not self, "%s, %s" % (self, mod)
return
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:
FUJIWARA Katsunori
demandimport: import sub-module relatively as expected (issue5208)...
r29736 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__,
level=1))
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)
Yuya Nishihara
py3: abuse r'' to preserve str-ness of literals passed to __setattr__()
r31644 object.__setattr__(self, r"_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)
Bryan O'Sullivan
demandimport: add support for PyPy...
r27536 _pypy = '__pypy__' in sys.builtin_module_names
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__')
Yuya Nishihara
demandimport: fix level passed to loader of sub-modules...
r26873 def processfromitem(mod, attr):
Gregory Szorc
demandimport: consolidate code for processing items in fromlist...
r26455 """Process an imported symbol in the import statement.
Yuya Nishihara
demandimport: error out early on missing attribute of non package (issue5373)...
r30022 If the symbol doesn't exist in the parent module, and if the
parent module is a package, it must be a module. We set missing
modules up as _demandmod instances.
Gregory Szorc
demandimport: consolidate code for processing items in fromlist...
r26455 """
Gregory Szorc
demandimport: refactor processfromitem...
r26456 symbol = getattr(mod, attr, nothing)
Yuya Nishihara
demandimport: error out early on missing attribute of non package (issue5373)...
r30022 nonpkg = getattr(mod, '__path__', nothing) is nothing
Gregory Szorc
demandimport: refactor processfromitem...
r26456 if symbol is nothing:
Yuya Nishihara
demandimport: error out early on missing attribute of non package (issue5373)...
r30022 if nonpkg:
Yuya Nishihara
demandimport: do not raise ImportError for unknown item in fromlist...
r30647 # do not try relative import, which would raise ValueError,
# and leave unknown attribute as the default __import__()
# would do. the missing attribute will be detected later
# while processing the import statement.
return
Yuya Nishihara
demandimport: enforce ignore list while processing modules in fromlist...
r28175 mn = '%s.%s' % (mod.__name__, attr)
if mn in ignore:
importfunc = _origimport
else:
importfunc = _demandmod
symbol = importfunc(attr, mod.__dict__, locals, level=1)
Gregory Szorc
demandimport: refactor processfromitem...
r26456 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)
FUJIWARA Katsunori
demandimport: delay loading for "from a import b" with absolute_import...
r29375 def chainmodules(rootmod, modname):
# recurse down the module chain, and return the leaf module
mod = rootmod
for comp in modname.split('.')[1:]:
if getattr(mod, comp, nothing) is nothing:
FUJIWARA Katsunori
demandimport: import sub-module relatively as expected (issue5208)...
r29736 setattr(mod, comp, _demandmod(comp, mod.__dict__,
mod.__dict__, level=1))
FUJIWARA Katsunori
demandimport: delay loading for "from a import b" with absolute_import...
r29375 mod = getattr(mod, comp)
return mod
Gregory Szorc
demandimport: refactor logic and add documentation...
r25935 if level >= 0:
Gregory Szorc
demandimport: support lazy loading for absolute_import...
r25937 if name:
FUJIWARA Katsunori
demandimport: delay loading for "from a import b" with absolute_import...
r29375 # "from a import b" or "from .a import b" style
rootmod = _hgextimport(_origimport, name, globals, locals,
level=level)
mod = chainmodules(rootmod, name)
elif _pypy:
Bryan O'Sullivan
demandimport: add support for PyPy...
r27536 # PyPy's __import__ throws an exception if invoked
# with an empty name and no fromlist. Recreate the
# desired behaviour by hand.
mn = globalname
mod = sys.modules[mn]
if getattr(mod, '__path__', nothing) is nothing:
mn = mn.rsplit('.', 1)[0]
mod = sys.modules[mn]
if level > 1:
mn = mn.rsplit('.', level - 1)[0]
mod = sys.modules[mn]
else:
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:
Yuya Nishihara
demandimport: fix level passed to loader of sub-modules...
r26873 processfromitem(mod, x)
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)
FUJIWARA Katsunori
demandimport: delay loading for "from a import b" with absolute_import...
r29375 mod = chainmodules(mod, name)
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',
Gregory Szorc
demandimport: add _imp to ignore list...
r28252 # ImportError during pkg_resources/__init__.py:fixup_namespace_package
'_imp',
Patrick Mezard
demandimport: ignore resource module, not available under Windows.
r5098 '_xmlplus',
'fcntl',
Yuya Nishihara
demandimport: add 'nt' to ignore list (issue5373)...
r30021 'nt', # pathlib2 tests the existence of built-in 'nt' module
Patrick Mezard
demandimport: ignore resource module, not available under Windows.
r5098 '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
GƔbor Stefanik
demandimport: fix TypeError when importing Python regex library (issue4920)
r26830 '_sre', # issue4920
Augie Fackler
demandimport: blacklist rfc822 and mimetools to prevent spurious warnings
r14976 'rfc822',
'mimetools',
Yuya Nishihara
demandimport: blacklist sqlalchemy.events as it has side effects (issue5085)...
r28176 'sqlalchemy.events', # has import-time side effects (issue5085)
Augie Fackler
demandimport: blacklist distutils.msvc9compiler (issue4475)...
r23643 # setuptools 8 expects this module to explode early when not on windows
timeless
demandimport: add trailing comma
r29980 'distutils.msvc9compiler',
Mads Kiilerich
demandimport: disable lazy import of __builtin__...
r30156 '__builtin__',
'builtins',
Patrick Mezard
demandimport: ignore resource module, not available under Windows.
r5098 ]
Matt Mackall
Replace demandload with new demandimport
r3877
Yuya Nishihara
demandimport: add '_ctypes.pointer' to ignore list on PyPy...
r30020 if _pypy:
ignore.extend([
# _ctypes.pointer is shadowed by "from ... import pointer" (PyPy 5)
'_ctypes.pointer',
])
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()