[Distutils] How to handle launcher script importability?

PJ Eby pje at telecommunity.com
Tue Aug 13 17:58:19 CEST 2013

On Tue, Aug 13, 2013 at 8:54 AM, Jason R. Coombs <jaraco at jaraco.com> wrote:
> 1. Renames, deletes, and other actions must be synchronized.

Why are you manually deleting or altering executables?  Why are you
renaming them at all?

I've been using .exe wrappers since they were written, and have never
had a single one of the issues you mention, because I never do any of
the things you mention by hand.  IMO that's what tools are for.
Doesn't pip uninstall scripts?

I may be slightly biased in my preference for .exe, because files with
other extensions don't work with Cygwin (which doesn't support
PATHEXT), but I work primarily with Windows Python rather than Cygwin
Python.  So, if there *has* to be a single file, I would greatly
prefer an .exe with the script embedded, rather than a non-.exe file.
It's a bit less discoverable, but at least it'll discourage anybody
from editing the contents.  (Because nobody should be editing
generated scripts anyway.)

(Also relevant: not every situation where wrapper scripts are used is
going to be one where a PyLauncher install is possible. For example,
portable deployment of an app to USB stick with a bundled Python can't
assume PATHEXT and a globally-installed PyLauncher.)

> 4. Updates to the launcher won't apply to existing scripts. If the launcher is
> updated, the side-by-side versions will remain out-of-date until their scripts
> are re-installed.

This is kind of a bogus point; *any* update to how scripts are
generated isn't automatically applied to existing scripts; the format
in which they're written is of no relevance.

> 5. Files in use can't be replaced. Because a Windows executable that's in use
> is not allowed to be overwritten,

But they can be renamed, and deleted afterwards.  For example, when
updating, you can do the simple dance of:

1. delete scriptname.exe.deleteme if it exists
2. rename scriptname.exe to scriptname.exe.deleteme
3. replace scriptname.exe
4. try to delete the .deleteme file created in step 2, ignoring errors.

And since this only needs to be done for the wrappers on installation
tools themselves (pip, easy_install, etc.), it's not like a lot of
people are going to have to write this code.

It can also be further enhanced, by having the .exe wrapper check (as
it exits) whether it was renamed, and if so, spin off a 'python -c
"import os, time; time.sleep(0.1); os.unlink('path to .deleteme')"'
and immediately exit.  (Or use one of the other tricks from
http://www.catch22.net/tuts/self-deleting-executables -- but I think
this one is the simplest and best for our purposes, since the wrapper
already knows at this point it can invoke Python using the path it
previously found, and it's not doing anything questionable with
process invocations that might raise red flags with security tools.)

More information about the Distutils-SIG mailing list