## page was renamed from WinShell
= The Problem =

`py2exe` doesn't find imported `win32com.shell` module, and creates invalid distribution without this file. As a result, an application that uses this module may crash at random or miss some features.

For example version 0.2 of [[http://tgolden.sc.sabren.com/python/winshell.html|winshell.py]] from Tim Golden (a thin wrapper around Windows Shell-Functions) starts with:
{{{
#!python
from win32com import storagecon
from win32com.shell import shell, shellcon
}}}

py2exe reports that something is missing:
{{{
The following modules appear to be missing
['Interface', 'intSet', 'mxDateTime.__version__', 'win32com.shell']
}}}

and starting the program leaves:
{{{
Traceback (most recent call last):
  File "xxxxxx.py", line 33, in ?
  File "xxxxxxx.pyo", line 9, in ?
  File "winshell.pyo", line 27, in ?
ImportError: No module named shell
}}}

in the log.file.


= Explanation =

`win32com` does some magic in order to allow loading of COM extensions during run time. The actual extensions reside in the `win32comext` directory under site-packages and can't be loaded directly. `win32com`'s [[http://docs.python.org/tutorial/modules.html#packages-in-multiple-directories|__path__]] variable has been changed to point to both `win32com` and `win32comext`. `py2exe`'s modulefinder can't handle runtime changes in `__path__` so we have to tell it about the change beforehand.

= Solution =

Without shame I browsed [[http://spambayes.svn.sourceforge.net/viewvc/spambayes/trunk/spambayes/|Spambayes source code]] and found the code to fix it.

{{{
#!python
# ...
# ModuleFinder can't handle runtime changes to __path__, but win32com uses them
try:
    # py2exe 0.6.4 introduced a replacement modulefinder.
    # This means we have to add package paths there, not to the built-in
    # one.  If this new modulefinder gets integrated into Python, then
    # we might be able to revert this some day.
    # if this doesn't work, try import modulefinder
    try:
        import py2exe.mf as modulefinder
    except ImportError:
        import modulefinder
    import win32com, sys
    for p in win32com.__path__[1:]:
        modulefinder.AddPackagePath("win32com", p)
    for extra in ["win32com.shell"]: #,"win32com.mapi"
        __import__(extra)
        m = sys.modules[extra]
        for p in m.__path__[1:]:
            modulefinder.AddPackagePath(extra, p)
except ImportError:
    # no build path setup, no worries.
    pass

from distutils.core import setup
import py2exe

# ...
# The rest of the setup file.
# ...
}}}

And this worked.

= Impact =

The following products included workaround:
 * Spambayes
 * Mercurial
 * hgsvn

= Ideal proposal =

`py2exe` should detect such well-known problems with well-known modules, and when `win32com.shell` is missing, a workaround should be automatically applied with a corresponding log message:
{{{
The following modules appear to be missing
['Interface', 'intSet', 'mxDateTime.__version__', 'win32com.shell']
Applying automatic workaround for known problem with win32com.shell
}}}

`py2exe` should also fail if required modules are missing unless some `--force` key in effect.