An expanded and polished version of this article appears as a new appendix in 2013's Learning Python, 5th Edition. See the end of this page for recent updates to this story, and links to related reading. This page has a toggled Contents display and floating Top button if JavaScript is enabled in your browser. |
October 2012 (updated July 2018)
This page describes the new Windows launcher for Python, installed automatically with Python 3.3 and later, and available separately on the Web for use with older versions. The new launcher provides an extra layer of code that chooses and starts an installed Python. Though it comes with some pitfalls, it provides some much-needed coherence for program execution when multiple Pythons coexist on the same computer.
I've written this page for programmers using Python on Windows. Though it's platform-specific by nature, it's targeted at both Python beginners (most of whom get started on this platform), as well as Python developers who write code to work portably between Windows and Unix. As we'll see, the new launcher changes the rules on Windows radically enough to impact everyone who uses Python on Windows, or may in the future.
To fully understand the launcher's protocols, we have to begin
with a short history lesson. Unix developers long ago devised
a protocol for designating a program to run a script's code.
On Unix, the first line in a script's text file is special if it
begins with the two characters #!
, sometimes called a "shebang"
(an arguably silly phrase I promise not to repeat from here on).
In Unix scripts, such lines designate a program to run the rest
of the script's code, by coding it after the #!
—using
either the directory path to the desired program itself, or an
invocation of the env
Unix utility which looks up the target
per your PATH
setting, the customizable system environment
variable that lists directories to be searched for executables:
#!/usr/local/bin/python ...script's code # run under this specific program #!/usr/bin/env python ...script's code # run under "python" found on PATHBy making such a script executable (e.g., via
chmod +x script.py
),
it can be run by giving just its filename in a command line; the
#!
line at the top then directs the Unix shell to a program which
will run the rest of the file's code. Depending on the platform's
install structure, the python
which these #!
lines name might
be a real executable, or a symbolic link to a version-specific
executable located elsewhere. These lines might also name a more
specific executable explicitly, such as python3
. Either way,
by changing #!
lines, symbolic links, or PATH
settings,
Unix developers can route a script to the appropriate installed
Python.
None of this applies to Windows itself, of course, where #!
lines
have no inherent meaning. Python itself has historically ignored
such lines as comments if present in Windows ("#" starts a comment
in the language). Still, the idea of
selecting Python executables on a per-file basis is a compelling
feature in a world where Python 2.X and 3.X often coexist on the
same machine. Given that many programmers coded #!
lines for
portability to Unix anyhow, the idea seemed ripe for emulating.
The install model has been very different on the other side of the fence. In the past (well, in every Python until 3.3), Windows installs updated the global Windows registry such that the latest Python version installed on your computer was the version that opened Python files when they were clicked or run by direct filename in command lines. Some Windows users may know this registry as filename associations, configurable in Control Panel's Default Programs dialog.
Under this install model, if you wished to open a file with a
different version than the latest install, you had to run a
command line giving the full path to the Python you wanted, or
update your filename associations manually to use the desired version.
You could also point generic python
command lines to a specific Python
by setting or changing your PATH
setting, but Python didn't set this
for you, and this wouldn't apply to scripts launched by icon clicks
and other contexts.
This reflects the natural model on Windows (when you click on a
.doc
file, Windows usually opens it in the latest Word installed),
and has been the state of things ever since there was a Python on Windows.
It's less ideal if you have Python scripts that require different versions
on the same machine, though—a situation that has become
increasingly common, and perhaps even normal in the dual Python
2.X/3.X era. Running multiple Pythons on Windows prior to 3.3
can be tedious for developers, and discouraging for newcomers.
The new Windows launcher, installed automatically with Python 3.3 and available as a stand-alone package for use with other versions, addresses these deficits in the former install model by providing two new executables:
py.exe
for console programs
pyw.exe
for non-console (typically GUI) programs
.py
and .pyw
files, respectively
via Windows filename associations. Like Python's original python.exe
main program (which they do not deprecate, but automatically spawn and can largely subsume),
these new executables are also registered to open bytecode files launched directly.
Amongst their weapons, these two new executables:
PATH
settings when used as command lines
#!
comment lines at the top of scripts
to determine which Python version should be used to run a file's code
The net effect is that under the new launcher, when multiple Pythons are installed on Windows, you are no longer limited to either the latest version installed or explicit/full command lines. Instead, you can now select versions explicitly on both a per-file and per-command basis, and specify versions in either partial or full form in both contexts:
#!python2
, #!/usr/bin/python2.7
, or
#!/usr/bin/env python3
.
py -2 m.py
, py -2.7 m.py
, or py -3 m.py
.
For example, the first of these techniques can serve as a sort of directive to declare which Python version the script depends upon, and will be applied by the launcher whenever the script is run by command line or icon click:
#!python3 ... ...a 3.X script # runs under latest 3.X installed ... #!python2 ... ...a 2.X script # runs under latest 2.X installed ... #!python2.6 ... ...a 2.6 script # runs under 2.6 (only) ...On Windows, command lines are typed in a Command Prompt window, designated by its
C:\temp>
prompt in this document. The first
of the following is the same as both the second and an icon click,
because of filename associations:
C:\temp> script.py # run per file's #! line if present, else per default C:\temp> py script.py # ditto, but py.exe is run explicitlyAlternatively, the second technique listed above can select versions with argument switches in command lines instead:
C:\temp> py -3 script.py # runs under latest 3.X C:\temp> py -2 script.py # runs under latest 2.X C:\temp> py -2.6 script.py # runs under 2.6 (only)This works both when launching scripts, and when starting the interactive interpreter (when no script is named):
C:\temp> py -3 # starts latest 3.X, interactive C:\temp> py -2 # starts latest 2.X, interactive C:\temp> py -3.1 # starts 3.1 (only), interactive C:\temp> py # start default Python (initially 2.X: see ahead)If there are both
#!
lines in the file and a version number
switch in the command line used to start it, the command line's version
overrides that in the file's directive:
#! python3.2 ... ...a 3.X script ... C\temp> py script.py # runs under 3.2, per file directive C\temp> py -3.1 script.py # runs under 3.1, even if 3.2 presentThe launcher also applies heuristics to select a specific Python version when it is missing or only partly described. For instance, the latest 2.X is run when only a "2" is specified, and a 2.X is preferred for files that do not name a version in a
#!
line when launched by icon
click or generic command lines (e.g., py m.py
, m.py
), unless
the default is configured to use 3.X instead by setting PY_PYTHON
or
a configuration file entry (more on this ahead).
Especially in the current dual 2.X/3.X Python world, explicit version
selection seems a useful addition for Windows, where many (and probably
most) newcomers get their first exposure to the language. Although it
is not without potential pitfalls—including failures on unrecognized
Unix #!
lines and a puzzling 2.X default—it does allow for a
more graceful coexistence of 2.X and 3.X files on the same machine, and
provides a rational approach to version control in command lines.
For more on the Windows launcher, including more advanced features and
use cases I'll either condense or largely omit here, see
this, and
this. Among other
things, the launcher also allows selecting between 32- and 64-bit installs,
specifying defaults in configuration files, and defining custom #!
command
string expansion.
Some readers familiar with Unix scripting may find the prior section enough to get started. For others, this section provides additional context in the form of a tutorial, which gives concrete examples of the launcher in action for you to trace through. This section also discloses additional launcher details along the way, though, so even well-seasoned Unix veterans may benefit from a quick scan here before FTPing all their Python scripts to the local Windows box.
To get started, we'll be using the following simple script, what.py
,
which can be run under both 2.X and 3.X to echo the version number of the
Python which runs its code (sys.version
is a string, whose first
component after splitting on whitespace is Python's version number):
#!python3 import sys print(sys.version.split()[0]) # first part of stringThis script's first-line comment serves to designate the required Python version; it must begin with
#!
per Unix convention, and
allows for a space before the python3
or not.
On my machine I currently have 2.7, 3.1, 3.2, and 3.3 all
installed; let's watch which version is invoked as the
script's first line is modified in the following sections,
exploring file directives, command lines, and defaults along the way.
As this script is coded, when run by icon click or command line, the first
line directs the registered py.exe
launcher to run using the latest
3.X installed:
#! python3 import sys print(sys.version.split()[0]) C:\temp> what.py # run per file directive 3.3.0 C:\temp> py what.py # ditto: latest 3.X 3.3.0Again, the space after
#!
is optional; I added a space to
demonstrate the point here. Note that the first what.py
command
here is equivalent to both an icon click and py what.py
, because
the py.exe
program is registered to open .py
files automatically
in the Windows filename-associations registry when the launcher is
installed.
Also note that when launcher documentation talks about the "latest" version,
it means the highest-numbered version. That is, it refers to the latest
released, not the latest installed on your computer (if you install 3.1
after 3.3, #!python3
selects the latter). The launcher cycles through
the Pythons on your computer to find the highest-numbered version that
matches your specification or defaults; this differs from the former
last-installed-wins model.
Now, changing the first line to name to python2
triggers the latest (really,
highest-numbered) 2.X installed instead. Here's this change at work; I'll omit
the last two lines of our script from this point on because they won't be altered:
#! python2 ...rest of script unchanged C:\temp> what.py # run with latest 2.X per #! 2.7.3And you can request a more specific version if needed—for example, if you don't want the latest in a Python line:
#! python3.1 ... C:\temp> what.py # run with 3.1 per #! 3.1.4This is true even if the requested version is not installed—which is treated as an error case by the launcher:
#! python2.6 ... C:\temp> what.py Requested Python version (2.6) is not installedUnrecognized Unix
#!
lines are also treated as errors, unless
you give a version number as a command-line switch to compensate,
as the next section describes in more detail (and as the section
on launcher issues will revisit as a pitfall):
#!/bin/python ... C:\temp> what.py Unable to create process using '/bin/python "C:\temp\what.py" ' C:\temp> py what.py Unable to create process using '/bin/python what.py' C:\temp> py -3 what.py 3.3.0Technically, the launcher recognizes Unix-style
#!
lines at
the top of script files which follow one of the following four
patterns:
#!/usr/bin/env python* #!/usr/bin/python* #!/usr/local/bin/python* #!python*Any
#!
line that does not take one of these recognized and
parseable forms is assumed to be a fully specified command line
to start a process to run the file, which is passed to Windows
as is, and generates the error message we saw previously if not
a valid Windows command.
(The launcher also supports "customized" command expansions via its
configuration files which are attempted before passing unrecognized
commands on to Windows, but we'll gloss over these here.)
In recognizable #!
lines, directory paths are coded per Unix convention,
for portability to that platform. The *
part at the end of the
four recognized patterns above denotes an optional Python version number,
in one of three forms:
python3
), to run the version installed with
the highest minor release number among those with the major release number given
python3.1
), to run that specific version only,
optionally suffixed by -32
to prefer a 32-bit version (e.g., python3.1-32
)
python
), to run the launcher's default version,
which is 2
unless set via PY_PYTHON=3
(another pitfall described ahead)
#!
line at all behave the same as those that name just
a generic python
, the omitted case above, and are influenced by PY_PYTHON
default settings. The first case, partials, may also be affected by
version-specific environment settings (e.g. PY_PYTHON3=3.1
to select
3.1 for python3
, and PY_PYTHON2=2.6
to pick 2.6 for python2
).
We'll revisit defaults later in this tutorial.
First, though, note that anything after the *
part in a #!
line is
assumed to be command-line arguments to Python itself (i.e., program
python.exe
, spawned by the launcher), unless you also give arguments
in a py
command line which are deemed to supersede #!
line
arguments by the launcher:
#!python3 [any python.exe arguments go here] ...But this leads us to launcher command lines in general, and will suffice as a natural segue to the next section.
As mentioned, version switches on command lines can be used to
select a Python version if one isn't present in the file. You
run a py
or pyw
command line to pass them a switch this way,
instead of relying on filename associations in the registry,
and instead of (or in addition to) versions in #!
lines in files.
In the following, we modify our script so it has no #!
directive:
# not a launcher directive ... C:\temp> py -3 what.py # run per command-line switch 3.3.0 C:\temp> py -2 what.py # ditto: latest 2.X installed 2.7.3 C:\temp> py -3.2 what.py # ditto: 3.2 specifically (and only) 3.2.3 C:\temp> py what.py # run per launcher's default (ahead) 2.7.3But command-line switches also take precedence over a version designation in a file's directive:
#! python3.1 ... C:\temp> what.py # run per file directive 3.1.4 C:\temp> py what.py # ditto 3.1.4 C:\temp> py -3.2 what.py # switches override directives 3.2.3 C:\temp> py -2 what.py # ditto 2.7.3Formally, the launcher accepts the following command-line argument types (which exactly mirror the
*
part at the end of
a file's #!
line described in the prior section):
-2 Launch the latest Python 2.X version -3 Launch the latest Python 3.X version -X.Y Launch the specified Python version -X.Y-32 Launch the specified 32-bit Python versionAnd the launcher's command lines take the following general form:
py [py.exe arg] [python.exe args] script.py [script.py args]Anything following the launcher's own argument (if present) is treated as though it were passed to the
python
program—typically, this includes any arguments for Python itself,
followed by the script filename, followed by any arguments meant
for the script, though the usual -m mod
, -c cmd
, and -
program
specification forms work in a py
command line too (as mentioned
earlier, arguments to python.exe
can also appear at the end of the
#!
directive line in a file, if used).
Let's write a new script which extends the prior with argument display
to trace; sys.argv
is the script's own arguments, and I'm using
the Python (python.exe
) -i
switch, which directs it to the interactive
prompt (>>>
) after a script runs:
# args.py, show my arguments too import sys print(sys.version.split()[0]) print(sys.argv) C:\temp> py -3 -i args.py -a 1 -b -c # -3: py, -i: python, rest: script 3.3.0 ['args.py', '-a', '1', '-b', '-c'] >>> ^Z C:\temp> py -i args.py -a 1 -b -c # args to python, script 2.7.3 ['args.py', '-a', '1', '-b', '-c'] >>> ^Z C:\temp> py -3 -c print(99) # -3 to py, rest to python: "-c cmd" 99 C:\temp> py -2 -c "print 99" 99Notice how the first two launches above run the default Python unless a version is given in the command line, because no
#!
line
appears in the script itself. Somewhat coincidentally, that leads
us to the last topic of this tutorial.
As also mentioned, the launcher defaults to 2.X for a
generic python
in a #!
directive with no specific
version number. This is true whether this generic form
appears in a full Unix path (e.g., #!/usr/bin/python
)
or not (#!python
). Here's the latter case in action,
coded in our original what.py
script:
#!python ... # same as #!/usr/bin/python C:\temp> what.py # run per launcher default 2.7.3The default is also applied when no directive is present at all—perhaps the most common case for code written to be used on Windows primarily or exclusively:
# not a launcher directive ... C:\temp> what.py # also run per default 2.7.3 C:\temp> py what.py # ditto 2.7.3But you can set the launcher's default to 3.X with initialization-file or environment-variable settings, which will apply to both files run from command lines and by icon clicks via their name's association with
py.exe
or pyw.exe
in the Windows registry:
# not a launcher directive ... C:\temp> what.py # run per default 2.7.3 C:\temp> set PY_PYTHON=3 # or via Control Panel/System C:\temp> what.py # run per changed default 3.3.0
As suggested earlier, for more fine-grained control you can also set version-specific environment variables to direct partial selections to a specific release, instead of falling back on the release with highest minor number installed:
#!python3 ... C:\temp> py what.py # runs "latest" 3.X 3.3.0 C:\temp> set PY_PYTHON3=3.1 # use PY_PYTHON2 for 2.X C:\temp> py what.py # override highest-minor choice 3.1.4
Making such settings in the Control Panel's System window
will make them apply globally across your machine. You
may or may not want to set defaults this way, depending
on the majority of the Python code you'll be running.
Many Python 2.X users can probably rely on defaults unchanged,
and override them in #!
lines or command lines as needed.
However, the setting used for directive-less files, PY_PYTHON, seems fairly crucial. Most programmers who have used Python on Windows in the past will probably expect 3.X to be the default after installing 3.3, especially given that the launcher is installed by 3.3 in the first place!—a seeming paradox, which leads us to the next section.
Update: 2016's Python 3.6 eventually changed the default to be Python 3.X—but only in some contexts, and only for code run on 3.6 and later. Read about the bifurcation here.
Though the new Windows launcher in 3.3 is a nice addition, like much in 3.X it may have been nicer had it appeared years ago. Unfortunately, it comes with some backward incompatibilities which may be an inevitable byproduct of today's multi-version Python world, but which may break some existing programs. This includes examples in book's I've written, and probably many others. While porting code to 3.3, I've come across three launcher issues worth noting:
!#
lines now make scripts fail on Windows
!#
Lines Now Make Scripts Fail on Windows
The new Windows launcher recognizes Unix #!
lines that begin
with #!/usr/bin/env python
but not the other common Unix
form #!/bin/env python
(which is actually mandated on some Unixes).
Scripts which use the latter of these, including some book examples,
worked on Windows in the past because their #!
lines coded for Unix
compatibility have been ignored as comments by all Windows Pythons to date.
These scripts now fail to run in 3.3 because the new launcher doesn't
recognize their format and posts an error message.
More generally, scripts with any #!
Unix line not recognized will
now fail to run on Windows. This includes scripts having any first line
that begins with a #!
which is not followed by one of the four recognized
patterns described earlier: /usr/bin/env python*
, /usr/bin/python*
,
/usr/local/bin/python*
, or python*
.
Anything else won't work, and requires code changes.
For instance, a somewhat common #!/bin/python
line also causes a script
to now fail, unless a version number is given in command-line switches.
Unix-style #!
lines probably aren't present in Windows-only programs,
but can be common in programs meant to be run on Unix too. Treating
unrecognized Unix directives as errors on Windows seems a bit extreme,
especially given that this is new behavior in 3.3, and will likely be
unexpected. Why not just ignore unrecognized #!
lines and run the file
with the default Python—like every Windows Python to date has?
It's possible that this might be improved in a future 3.X release (I'd
expect to see some backlash on this), but today you must change any files
using a #!/bin/env
or other unrecognized pattern, if you want them to
run under the launcher installed automatically with Python 3.3 on Windows.
With respect to the book examples I ported to 3.3, this broke roughly a dozen
scripts that started with #!/bin/env python
. Regrettably, this includes some
of the book's user-friendly and top-level demo launcher scripts (PyGadgets
and PyDemos). To fix, I changed these to use the accepted #!/usr/bin/env python
form instead. Altering your Windows file associations to omit the launcher
altogether may be another option (e.g., associating .py
files with python.exe
instead of py.exe
), but this negates the launcher's benefits, and seems a bit
much to ask of users, especially newcomers.
One open issue here:
strangely, passing any command-line switch to the launcher,
even a python.exe
argument, seems to negate this effect and fall back
on the default Python—m.py
and py m.py
both issue errors
on unrecognized #!
lines, but py -i m.py
runs such a file with
the default Python. This seems a possible launcher bug (TBD), but
also relies on the default, the subject of the next issue.
Oddly, the Windows 3.3 launcher defaults to using an installed Python 2.X
when running scripts that don't select 3.X explicitly. That is, scripts which
either have no #!
directive, or use one that names python
generically
will be run by a 2.X Python by default when launched by icon clicks,
direct filename command lines (m.py
), or py
command lines which give
no version switch (py m.py
). This is true even if 3.3 is installed
after a 2.X on your machine, and has the potential to make many 3.X
scripts fail initially.
The implications of this are potentially broad. As one example, clicking
the icon of a directive-less 3.X file just after installing 3.3 may now fail,
because the associated launcher assumes you mean to use 2.X by default.
This probably won't be a pleasant first encounter for some Python newcomers!
This assumes the 3.X file has no #!
directive that provides an explicit
python3
version number, but most scripts meant to run on Windows won't
have a #!
line at all, and many files coded before the launcher came
online won't accommodate its version number expectations.
Program launches which don't give an explicit version number might be arguably
ambiguous on Unix too, and often rely on symbolic links from python
to a
specific version (which is most likely 2.X today—a state the new Windows
launcher seems to emulate). But as for the prior issue, this probably shouldn't
trigger a new error on Windows in 3.3 for scripts that worked there formerly.
Most programmers wouldn't expect Unix comment lines to matter on Windows,
and wouldn't expect 2.X to be used just after installing 3.X.
In terms of my book examples port, this 2.X default caused multiple 3.X script
failures after installing 3.3, for both scripts with no #!
line, as well as
scripts with a Unix-compatible #!/usr/bin/python
line. To fix just the
latter, change all scripts in this category to name python3
explicitly
instead of just python
. To fix both the former and the latter in a single
step, set the Windows launcher's default to be 3.X globally with either a
py.ini
file (see the launcher's documentation for details) or a PY_PYTHON
environment variable setting as shown in the earlier examples (e.g.,
set PY_PYTHON=3
). As mentioned in the prior point, manually changing
your file associations is another solution, but none of these options seem
simpler than those imposed by prior install schemes.
Update: see also the earlier note about launcher-default changes in 2016's Python 3.6; it's a partial fix.
Besides installing the new launcher, the Windows Python 3.3 installer
can automatically add the directory containing 3.3's python.exe
executable to your system PATH
setting. The reasoning behind this
is that it might make life easier for some Windows beginners—they
can type just python
instead of a full directory path. This
isn't a feature of the launcher per se, and shouldn't cause scripts to
fail in general. It had no impact on the book examples. But it seems to
clash with the launcher's operation and goals, and may be best avoided.
As described, the new launcher's py
and pyw
executables are by
default installed on your system search path, and running them requires
neither directory paths nor PATH
settings. If you start scripts
with py
instead of python
command lines, the new PATH
feature is
irrelevant. In fact, py
not only spawns python
, it completely
subsumes it in most contexts. Given that file associations will launch
py
or pyw
instead of python
anyhow, you probably should
too—using python
instead of py
may prove redundant and
inconsistent, and might even launch a version different than that used in launcher
contexts should the two schemes' settings grow out of synch. In short, adding
python
to PATH
seems contradictory to the new launcher's world view,
and potentially error-prone.
Also note that updating your PATH
assumes you want python
to run
3.3 normally, and this feature is disabled by default; be sure to
select this in the install screen if you want this to work (but
not if you don't!). Due to the second point above, many users may
still need to set PY_PYTHON
to 3 for programs run by icon clicks
that invoke the new launcher, which seems no simpler than setting
PATH
, a step that the launcher was meant to remove. You may be better
served by using just the launcher's executables, and changing just
PY_PYTHON
as needed.
To be fair, some of the prior section's pitfalls may be an inevitable consequence of trying to simultaneously support a Unix feature on Windows and multiple installed versions. In exchange, it provides a coherent way to manage mixed-version scripts and installations. You'll probably find the Windows launcher shipped with 3.3 and later to be a major asset once you start using it, and get past any initial incompatibilities you may encounter.
In fact, you may also want to start getting into the habit of coding
compatible Unix-style #!
lines in your Windows scripts, with explicit
version numbers (e.g., #!/usr/bin/python3
). Not only does this declare
your code's requirements and arrange for its proper execution on Windows,
it will also subvert the launcher's defaults, and may also make your
script usable as a Unix executable in the future.
But you should be aware that the launcher may break some formerly
valid scripts with #!
lines; may choose a default version which
you don't expect and your scripts can't use; and may require configuration
and code changes on the order of those it was intended to obviate.
The new boss is better than the old boss, but seems to have gone to
the same school.
This page's material was revised and published in this book, which has more on using Python on Windows and elsewhere. There was also a minor update to the Windows-launcher story in 2016's Python 3.6, described here.
For additional reading, try these other articles popular at learning-python.com:
These and more are available on the blog page.