[Distutils] How to handle launcher script importability?

PJ Eby pje at telecommunity.com
Sun Aug 11 18:17:14 CEST 2013

On Sun, Aug 11, 2013 at 10:38 AM, Jason R. Coombs <jaraco at jaraco.com> wrote:
> In Setuptools 1.0 (currently in beta), I've added an experimental, opt-in
> feature to install pure Python launcher scripts on Windows instead of
> installing a launcher executable for each script, with the intention that
> these scripts will be launched by pylauncher or Python directly, eventually
> obviating the need for a launcher executable in setuptools at all.
> This means that instead of installing, for example:
>   Scripts\my-command.exe
>   Scripts\my-command-script.py
>   Scripts\my-command.exe.manifest
> Instead Setuptools just installs:
>   Scripts\my-command.py
> This technique is much like the scripts that get installed to bin/ on Unix,
> except that due to the nature of launching commands on Windows, the .py
> extension is essentially required.
> One problem with this technique is that if the script is also a valid module
> name, it can be imported, and because Python puts the script's directory at
> the top of sys.path, it _will_ be imported if that name is imported.
> This happens, for example, after installing Cython. Cython provides a
> command, 'cython', and a (extension) module called 'cython'. If one launches
> cython using the script launcher, the 'cython' module will not be importable
> (because "import cython" will import the launcher script). Presumably, this
> is why '-script' was added to the launcher scripts in previous versions.
> This is a rather unfortunate situation, and I'd like to solicit comments for
> a way to avoid this situation. I see a few options:
> 1. Have the setuptools-generated launcher scripts del sys.path[0] before
> launching.
> 2. Accept the implementation and file bugs with the offending projects like
> Cython to have them either rename their script or rename their internal
> module.
> 3. Continue to generate the script names with '-script.py' appended,
> requiring invocation to always include -script.py on Windows.
> 4. Continue to generate executables, duplicating the effort of pylauncher,
> and dealing with the maintenance burden of that functionality.
> I don't see (2), (3), or (4) as really viable, so my proposal is to move
> forward with (1) if there aren't any better suggestions.
> If we move forward with (1), there are a few concerns that come to mind.

Here's another problem with #1: you will break single-directory
standalone portable app installs, where you use "easy_install -mad
somedir" to install all of an app's dependencies to a single directory
that the app can then be run from (assuming Python is available).

In order to work around this issue, you'd need to hardcode sys.path
entries for the dependencies, or do something else more complicated in
order to ensure that dependency resolution will pick up the adjacent
distributions before searching anything else on sys.path.

> Third, is it possible some users are depending on the presence of
> sys.path[0]

Absolutely.  It's a documented feature of Python that the script
directory is always first on sys.path, so that you can provide modules
and packages adjacent to it.  That's how portable app installs work
with easy_install.

May I suggest an option 5 instead?  Use the new .pyz (or .pyzw for
non-console apps) as a zipped Python application.  .pyz files aren't
importable, but *are* executable.  That's basically all that's needed
to prevent importing -- a file extension that's launchable but not

(There's also an option 6, which is to use import system hooks to
prevent the script modules from being found in the sys.path[0] entry,
but that's rather hairier.)

Using option 5 means the feature can only work with versions of Python
on Windows that install the necessary PATHEXT support to allow that
extension to work, but you're kind of limited to that anyway, because
by default .py files aren't findable via PATH on Windows.

Your post doesn't make it clear whether you're aware of that, btw:
IIUC, on most Windows setups, executing a .py file via PATH doesn't
work unless you've set up PATHEXT to include .py.  So your feature's
going to break until that's fixed, and AFAIK there is *no* Windows
Python that fixes this, with the possible exception of 3.4 alpha,
possibly a future alpha that hasn't been released yet, because last I
saw on Python-Dev it was still being discussed *how* to update PATHEXT
safely from the installer.

In short: dropping .exe wrappers is not supportable on *any* current
version of Python for Windows, in the sense that anybody who uses it
will not yet be able to execute the scripts if they are currently
doing so via PATH (and haven't manually fixed their PATHEXT).  (This
was one of the main reasons for using .exe wrappers in the first

The .pyz approach of course has the same drawback, but at least it
should be viable for future Python versions, and doesn't have the
sys.path[0] problems.  I think you are going to have to keep .exe
wrappers the default for all Python versions < 3.4.

More information about the Distutils-SIG mailing list