Commit Graph

55 Commits

Author SHA1 Message Date
Yuya Nishihara
44b7401396 demandimport: fix level passed to loader of sub-modules
As the fromlist gives the names of sub-modules, they should be searched in
the parent directory of the package's __init__.py, which is level=1.

I got the following error by rewriting hgweb to use absolute_import, where
the "mercurial" package is referenced as ".." (level=2):

  ValueError: Attempted relative import beyond toplevel package

I know little about the import mechanism, but this change seems correct.
Before this patch, the following code did import the os module with no error:

  from mercurial import demandimport
  demandimport.enable()
  from mercurial import os
  print os.name
2015-11-01 21:19:09 +09:00
Gábor Stefanik
9c2ffda81c demandimport: fix TypeError when importing Python regex library (issue4920) 2015-10-28 16:27:09 +01:00
Gregory Szorc
54a54d074c demandimport: replace more references to _demandmod instances
_demandmod instances may be referenced by multiple importing modules.
Before this patch, the _demandmod instance only maintained a reference
to its first consumer when using the "from X import Y" syntax. This is
because we only created a single _demandmod instance (attached to the
parent X module). If multiple modules A and B performed
"from X import Y", we'd produce a single _demandmod instance
"demandmod" with the following references:

  X.Y = <demandmod>
  A.Y = <demandmod>
  B.Y = <demandmod>

The locals from the first consumer (A) would be stored in <demandmod1>.
When <demandmod1> was loaded, we'd look at the locals for the first
consumer and replace the symbol, if necessary. This resulted in state:

  X.Y = <module>
  A.Y = <module>
  B.Y = <demandmod>

B's reference to Y wasn't updated and was still using the proxy object
because we just didn't record that B had a reference to <demandmod> that
needed updating!

With this patch, we add support for tracking which modules in addition
to the initial importer have a reference to the _demandmod instance and
we replace those references at module load time.

In the case of posix.py, this fixes an issue where the "encoding" module
was being proxied, resulting in hundreds of thousands of
__getattribute__ lookups on the _demandmod instance during dirstate
operations on mozilla-central, speeding up execution by many
milliseconds. There are likely several other operation that benefit from
this change as well.

The new mechanism isn't perfect: references in locals (not globals) may
likely linger. So, if there is an import inside a function and a symbol
from that module is used in a hot loop, we could have unwanted overhead
from proxying through _demandmod. Non-global imports are discouraged
anyway. So hopefully this isn't a big deal in practice. We could
potentially deploy a code checker that bans use of attribute lookups of
function-level-imported modules inside loops.

This deficiency in theory could be avoided by storing the set of globals
and locals dicts to update in the _demandmod instance. However, I tried
this and it didn't work. One reason is that some globals are _demandmod
instances. We could work around this, but it's a bit more work. There
also might be other module import foo at play. The solution as
implemented is better than what we had and IMO is good enough for the
time being.

It's worth noting that this sub-optimal behavior was made worse by the
introduction of absolute_import and its recommended "from . import X"
syntax for importing modules from the "mercurial" package. If we ever
wrote performance tests, measuring the amount of module imports and
__getattribute__ proxy calls through _demandmod instances would be
something I'd have it check.
2015-10-04 11:17:43 -07:00
Gregory Szorc
74682dae31 demandimport: refactor processfromitem
This will match the next patch smaller and easier to read.
2015-10-04 10:36:54 -07:00
Gregory Szorc
38b34aee9f demandimport: consolidate code for processing items in fromlist
This code was mostly duplicated. An upcoming patch will add more
complexity, making the duplication harder to justify. Consolidate the
code.
2015-10-03 15:30:17 -07:00
Gregory Szorc
026a7a3f9f demandimport: use absolute_import 2015-08-08 19:05:28 -07:00
Gregory Szorc
5f5e77fde2 demandimport: support lazy loading for absolute_import
Before, we didn't support lazy loading if absolute_import was in
effect and a fromlist was used. This meant that "from . import X"
wasn't lazy and performance could suffer as a result.

With this patch, we now support lazy loading for this scenario.
As part of developing this, I discovered issues when module names
are defined. Since the enforced import style only allows
"from X import Y" or "from .X import Y" in very few scenarios
when absolute_import is enabled - scenarios where Y is not a
module and thus there is nothing to lazy load - I decided to drop
support for this case instead of chasing down the errors. I don't
think much harm will come from this. But I'd like to take another
look once all modules are using absolute_import and I can see the
full extent of what is using names in absolute_import mode.
2015-08-08 16:13:27 -07:00
Gregory Szorc
2a92fddf91 demandimport: support keyword arguments on _hgextimport
__import__ supports keyword arguments since Python 2.5. This
proxy should too.

An upcoming patch will use this feature.
2015-08-08 17:07:34 -07:00
Gregory Szorc
fd4a9d9759 demandimport: refactor logic and add documentation
demandimport doesn't currently support absolute imports (level >= 0).
In preparation for this, we add some documentation and a code branch
to handle the absolute_import case.
2015-08-08 16:24:57 -07:00
Gregory Szorc
c5d1f82f1f demandimport: add __future__ to ignore list
__future__ is special. We should definitely not be trying to lazy
load it.
2015-08-08 17:12:37 -07:00
Gregory Szorc
463e808be1 demandimport: remove support for Python < 2.5
The removed code was to support an __import__ function that doesn't
support the "level" argument. This argument was added in Python 2.5,
which we no longer support.
2015-08-08 15:01:27 -07:00
Gregory Szorc
f20e6c2557 demandimport: support importing builtins for Python 3
__builtin__ was renamed to builtins in Python 3. If importing
__builtin__ fails, fall back to importing builtins in order to support
Python 3.
2015-06-27 17:31:06 -07:00
Gregory Szorc
11ddb8efd9 demandimport: alias __builtin__ as builtins
Python 3 renamed the __builtin__ module to builtins. In preparation for
supporting Python 3, alias the imported module as "builtins."
2015-06-27 17:29:15 -07:00
Jordi Gutiérrez Hermoso
cd59b21ffd demandimport: define a deactivated context manager
This can be useful for use in "with" blocks for temporarily disabling
demandimport.
2015-05-28 16:11:26 -04:00
Augie Fackler
ffd2cf1dba demandimport: blacklist distutils.msvc9compiler (issue4475)
This module depends on _winreg, which is windows-only. Recent versions
of setuptools load distutils.msvc9compiler and expect it to
ImportError immediately when on non-Windows platforms, so we need to
let them do that. This breaks in an especially mystifying way, because
setuptools uses vars() on the imported module. We then throw an
exception, which vars doesn't pick up on well. For example:

In [3]: class wat(object):
   ...:     @property
   ...:     def __dict__(self):
   ...:         assert False
   ...:

In [4]: vars(wat())
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-2781ada5ffe6> in <module>()
----> 1 vars(wat())

TypeError: vars() argument must have __dict__ attribute

Which is similar to the problem we run into.
2014-12-22 17:27:31 -05:00
Gregory Szorc
093c1fe1ab demandimport: pass proper level to __import__ in Python 3
demandimport was failing in Python 3 with a ValueError because
__import__'s level=-1 has gone away (-1 means to try both relative
and absolute imports and relative imports don't exist in Python 3).

With this patch, demandimport still doesn't work in Python 3 (it
fails when importing a non-package module).
2014-05-10 14:57:25 -07:00
Mads Kiilerich
2572d4c880 demandimport: make it possible to disable by setting HGDEMANDIMPORT=disable
Convenient for debugging weird problems that are caused by demandimport or
obfuscated by it.

This is an undocumented developer feature.
2014-04-08 01:35:13 +02:00
Brodie Rao
fc55ccd1ef hooks: only disable/re-enable demandimport when it's already enabled
This fixes an issue introduced in 818c8992811a where, when disabling
demandimport while running hooks, it's inadvertently re-enabled even when
it was never enabled in the first place.

This doesn't affect normal command line usage of Mercurial; it only matters
when Mercurial is run with demandimport intentionally disabled.
2014-02-10 14:51:06 -08:00
FUJIWARA Katsunori
d01f604fec demandimport: allow extensions to import own modules by absolute name
Before this patch, python modules of each extensions can't import
another one in own extension by absolute name, because root modules of
each extensions are loaded with "hgext_" prefix.

For example, "import extroot.bar" in "extroot/foo.py" of "extroot"
extension fails, even though "import bar" in it succeeds.

Installing extensions into site-packages of python library path can
avoid this problem, but this solution is not reasonable in some cases:
using binary package of Mercurial on Windows, for example.

This patch retries to import with "hgext_" prefix after ImportError,
if the module in the extension may try to import another one in own
extension.

This patch doesn't change some "_import()"/"_origimport()" invocations
below, because ordinary extensions shouldn't cause such invocations.

    - invocation of "_import()" when root module imports sub-module by
      absolute path without "fromlist"

      for example, "import a.b" in "a.__init__.py".

      extensions are loaded with "hgext_" prefix, and this causes
      execution of another (= fixed by this patch) code path.

    - invocation of "_origimport()" when "level != -1" with "fromlist"

      for example, importing after "from __future__ import
      absolute_import" (level == 0), or "from . import b" or "from .a
      import b" (0 < level),

      for portability between python versions and environments,
      extensions shouldn't cause "level != -1".
2013-10-05 01:02:22 +09:00
FUJIWARA Katsunori
24ac3edef8 demandimport: support "absolute_import" for external libraries (issue4029)
Before this patch, demandimport of Mercurial may fail to load external
libraries using "from __future__ import absolute_import": for example,
importing "foo" in "bar.baz" module will load "bar.foo" if it exists,
even though "absolute_import" is enabled in "bar.baz" module.

So, extensions for Mercurial can't use such external libraries.

This patch saves "level" of import request for on-demand module
loading in the future: default value of level is -1, and level is 0
when "absolute_import" is enabled.

"level" value is passed to built-in import function in
"_demandmod._load()" and it should load target module correctly.

This patch changes only one "_demandmod" construction case other than
cases below:

    - construction in "_demandmod._load()"

      this code path should be used only in relative sub-module
      loading case

    - constructions other than patched one in"_demandimport()"

      these code paths shouldn't be used in "level != -1" case
2013-10-05 01:02:22 +09:00
Simon Heimberg
ef1c701e57 demandimport: determine at load time if __import__ has level argument 2011-08-22 22:50:52 +02:00
Augie Fackler
5a1e206eb8 demandimport: use getattr instead of hasattr
We don't use util.safehasattr() here to avoid adding new dependencies
for demandimport. This change may expose previously-silenced
deprecation warnings to appear, as hasattr silently hides warnings
that occur during module import when using demandimport.
2011-07-25 21:15:48 -05:00
Augie Fackler
f8078839f5 demandimport: blacklist rfc822 and mimetools to prevent spurious warnings 2011-03-01 23:35:22 -06:00
Dan Villiom Podlaski Christiansen
312fdcf8d8 demandimport: change default for level from None to -1
The Python default for this function is -1, indicating both relative
and absolute imports should be used.[1] Previously, we relied on the
Python VM not passing level when such semantics were
requisted. This is not the case for PyPy, however, where a level of -1
is always passed to __import__.

[1] <http://docs.python.org/library/functions.html#__import__>
2010-12-01 21:46:08 +01:00
Martin Geisler
9d82b9ed1f check-code: using and/or/not as a function is bad style 2010-11-11 00:08:09 +01:00
Matt Mackall
627b247624 demandimport: back out ccffe6051751 (issue2467) 2010-10-30 11:32:04 -05:00
Steve Borho
0ef8d80996 demandimport: backout 907b6ae6f918
Using a keyword argument here breaks zipimporter for at least some
versions of Python2.6 on Windows.
2010-10-21 10:13:03 -05:00
Dan Villiom Podlaski Christiansen
4b3349db92 demandimport: fix an obscure corner-case.
Python's __import__() function has 'level' as the fourth argument, not the
third. The code path in question probably never worked.

(This was seen trying to run Mercurial in PyPy. Fixing this made it
die somewhere else...)
2010-09-14 23:00:39 +02:00
Dan Villiom Podlaski Christiansen
d8105bddb7 demandimport: store level argument on _demandmod instances
The 'level' argument to __import__ was added in Python 2.6, and is
specified for either relative or absolute imports. The fix introduced
in 5b0fda8ff209 allowed such imports to proceed without failure, but
effectively disabled demandimport for them. This is particularly
unfortunate in Python 3.x, where *all* imports are either relative or
absolute.

The solution introduced here is to store the level argument on the
demandimport instance, and propagate it to _origimport() when its
value isn't None.

Please note that this patch hasn't been tested in Python 3.x, and thus
may not be complete. I'm worried about how sub-imports are handled; I
don't know what they are, or whether the level argument should be
modified for them. I've added 'TODO' notes to these cases; hopefully,
someone more knowledgable of these issues will deal with them.
2010-08-17 17:46:10 +02:00
Dirkjan Ochtman
8ea7020d75 demandimport: blacklist _ssl (issue1964) 2010-03-09 16:03:57 +01:00
Greg Ward
b0b25f04a1 demandimport: add __main__ to the blacklist (because of setuptools) 2010-01-20 20:23:36 -05:00
Matt Mackall
595d66f424 Update license to GPLv2+ 2010-01-19 22:20:08 -06:00
Dirkjan Ochtman
8fec18223e demandimport: ignore _winreg (used in python-2.7 mimetypes) 2010-01-13 11:57:32 +01:00
Steve Borho
136a6eb1d4 demandimport: blacklist gtk
Demandimport breaks gtk.  You get a meaningless error about
'failed loading gobject\_gobject.pyd'.  Mercurial does not use gtk,
but this trips up many extension writers.
2009-09-17 17:39:43 -05:00
Dan Villiom Podlaski Christiansen
68ad384d38 dispatch: also pass level argument to __import__ for ignored modules
I wanted to check if mercurial.demandimport could speed up the loading of
PyObjC, and ran into this: the level argument for __import__, available in
Python 2.5 and later, is silently dropped when doing an 'import *'. I have no
idea what these arguments mean, but this minor change made it work.

(Oh, and because of that 'from ... import *', PyObjC still took about 2s...)
2009-08-05 17:19:37 +02:00
Martin Geisler
750183bdad updated license to be explicit about GPL version 2 2009-04-26 01:08:54 +02:00
Steve Borho
1495d07855 demandimport: blacklist pythoncom
win32com.shell would segfault at import time if pythoncom
was demand loaded.
2009-03-09 21:00:37 -05:00
Dirkjan Ochtman
309bdac20d demandimport: patch __builtin__ instead of __builtins__
This helps on implementations other than CPython, where __builtins__ isn't
necessarily defined (as it's an implementation detail for CPython).
2009-02-05 17:40:25 +01:00
Ali Gholami Rudi
bdaf736fbc demandimport: handling new relative imports
Mercurial does not work on python2.6 because __import__ takes an
additional argument called level.  This patch merely calls the
built-in __import__ when level is passed.
2008-01-16 19:14:54 +03:30
Matt Mackall
f200ad645a demandload: give better diagnostic for call of an unloaded module 2007-12-10 10:24:47 -06:00
Patrick Mezard
38b7c18bab demandimport: ignore resource module, not available under Windows. 2007-08-14 16:03:15 +02:00
Patrick Mezard
e7663b6ff8 demandimport: ignore pwd and grp.
Both are unavailable under Windows and tarfile detects them via ImportError.
2007-08-13 19:42:52 +02:00
Thomas Arendsen Hein
4d29c6dc8e Updated copyright notices and add "and others" to "hg version" 2007-06-19 08:51:34 +02:00
Matt Mackall
ab72eb680f demandimport: fix issue579 and add a test
fix suggested by Brendan
2007-06-18 19:43:26 -05:00
Lee Cantey
416c20c0e1 Fix for demandimport.py and Windows compiled version.
From Shun-ichi Goto in BTS issue 457 http://www.selenic.com/mercurial/bts/msg2780
2007-06-18 15:03:55 -07:00
Brendan Cully
464fa9a95b Add _xmlplus to the demandimport blacklist. (HGCIA was failing to load.) 2007-03-03 18:59:54 -08:00
Matt Mackall
4aa5ee68ce Add some modules to the demandimport ignore list for Windows 2006-12-17 22:16:57 -06:00
Brendan Cully
eab04761d3 demandimport: blacklist _hashlib again (imported in ImportError try block) 2006-12-17 18:45:46 -08:00
Brendan Cully
99bac46845 demandimport: handle already-loaded nested modules in subload 2006-12-17 18:34:42 -08:00
Matt Mackall
aa18646078 demandimport: fix import x.y.z as a when x.y is already imported. 2006-12-17 14:56:12 -06:00