[PATCH stable] demandimport: add __main__ to the blacklist (because of setuptools)

Greg Ward greg-hg at gerg.ca
Mon Jan 25 21:40:39 CST 2010


On Sun, Jan 24, 2010 at 12:34 PM, Benoit Boissinot
<benoit.boissinot at ens-lyon.org> wrote:
> On Wed, Jan 20, 2010 at 08:24:03PM -0500, Greg Ward wrote:
>> # HG changeset patch
>> # User Greg Ward <greg-hg at gerg.ca>
>> # Date 1264037016 18000
>> # Branch stable
>> # Node ID 992d832eecd81bcb22bcd889c01cbbb5431c2c68
>> # Parent  acf001ee5ef80c698b75d437d9685ed926a3e2fc
>> demandimport: add __main__ to the blacklist (because of setuptools)
>
> Couldn't we just blacklist setuptools instead? __main__ seems more
> general.

I don't think so.  I tried a couple of things to make this work, and
blacklisting __main__ was all I could come up with.  Let me explain.
I have a Python hook that does

  import pytz

because it has to do timezone calculations.  pytz/__init__.py does this:

try:
    from pkg_resources import resource_stream
except ImportError:
    resource_stream = None

where pkg_resources.py ultimately comes from setuptools.

pkg_resources.py does this:

try:
    # Does the main program list any requirements?
    from __main__ import __requires__
except ImportError:
    pass # No: just use the default working set based on sys.path
else:
    # Yes: ensure the requirements are met, by prefixing sys.path if necessary
    [...]

which, in most Python apps, would land in the "except ImportError"
code.  But in Mercurial, the "from __main__ import ..." succeeds,
putting a _demandmod object in pkg_resources' namespace.

Later, pkg_resources tries to do something with the __resources__ list
that it thought it imported from __main__.  But it's not a list, it's
a _demandmod object:

  [...]
  File "/usr/lib/python2.6/dist-packages/pkg_resources.py", line 1813,
in yield_lines
    for ss in strs:
TypeError: '_demandmod' object is not iterable

This is fairly easy to reproduce if you have any module that depends
on pkg_resources:

"""
$ python
Python 2.6.4 (r264:75706, Dec  7 2009, 18:45:15)
[...]
>>> from mercurial import demandimport
>>> demandimport.enable()
>>> import pytz
>>> pytz
<unloaded module 'pytz'>
>>> pytz.tz
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/pymodules/python2.6/mercurial/demandimport.py", line
75, in __getattribute__
[...]
  File "/usr/lib/python2.6/dist-packages/pkg_resources.py", line 1814,
in yield_lines
    for s in yield_lines(ss):
  File "/usr/lib/python2.6/dist-packages/pkg_resources.py", line 1813,
in yield_lines
    for ss in strs:
TypeError: '_demandmod' object is not iterable
"""

I tried blacklisting several module names, but only '__main__' worked.

While it seems odd to import __main__, the fact remains that this code
is out there, and works fine with Python apps other than Mercurial.
(Although I imagine it will give Bazaar's lazyimport trouble sooner or
later. ;-)  And there is certainly no cost to letting imports of
__main__ go through: by definition, it's already in memory and in
sys.modules.

Greg


More information about the Mercurial-devel mailing list