The infamous "distutils has already been patched by" error

David 71david at libero.it
Tue Dec 29 07:14:31 EST 2009


I am writing these notes because there is  little or no  documentation about
this annoying problem.

The error message in subject appears when you try to get the list of
installed modules under the help() enviroment.


>>> help()
help> modules

Please wait a moment while I gather a list of all available modules...

...

AssertionError: distutils has already been patched by <class
py2exe.Distribution at 0x019132D0>



It is caused by monkeypatching of distutils module by other modules,
tipically py2exe.

In detail the cause is this:

0) The help() function is defined by the \python25\lib\site.py module
automatically imported at interpreter startup

1) the standard module \python25\lib\site.py imports the module pydoc and
call the pydoc.help() function (in the class _Helper).

2) The method run() of class ModuleScanner, used by pydoc.help() walks all
installed modules using the pkgutil.walk_packages() function from module
pkgutil, and calls it giving no arguments so the 'onerror' parameter takes
the default value None.

3) The function pkgutil.walk_packages() tries to import each package in a
try/except clause like this:

            try:
                __import__(name)
            except ImportError:
                if onerror is not None:
                    onerror(name)
            except Exception:
                if onerror is not None:
                    onerror(name)
                else:
                    raise

As you can see, if onerror is None, the function captures only the
ImportError exception, but...

4) ...when pkgutil.walk_packages() tries to import dist.py module of
setuptools package (\python25\lib\site-packages\setuptools\dist.py)
the following piece of code gets executed...

def _get_unpatched(cls):
    """Protect against re-patching the distutils if reloaded

    Also ensures that no other distutils extension monkeypatched the
distutils
    first.
    """
    while cls.__module__.startswith('setuptools'):
        cls, = cls.__bases__
    if not cls.__module__.startswith('distutils'):
        raise AssertionError(
            "distutils has already been patched by %r" % cls
        )
    return cls

_Distribution = _get_unpatched(_Distribution)


...and, if the py2exe (or other modules) had already been imported and
monkeypatched del setuptools module, the infamous "distutils has already
been patched by" is raised, caputerd by pkgutil.walk_packages() and reraised
because the onerror variable is None.

That said, there are 2 workarounds to avoid this hassle:

A) manually import setuptools module before typing help()

>>> import setuptools
>>> help()

Welcome to Python 2.5!  This is the....
help> modules


B) Patch the run() method of pydoc module at line 1862:

for importer, modname, ispkg in 
	pkgutil.walk_packages(onerror=lambda s:None):




Hope this helps
David.



More information about the Python-list mailing list