Running PP4E Examples on Python 3.3 through 3.7
Last revised: September 2019
The book Programming Python, 4th Edition (a.k.a. PP4E) was published when Python 3.2 was current. This page summarizes the 4 minor steps required to run this book's major examples with Python versions 3.3 through 3.7 (and likely later), and documents the corresponding changes applied to the current release 1.4 of the book's examples package, created October 15, 2013. In brief:
The first 2 of these steps are needed for Windows only, and the first 3
are incorporated into release 1.4 of the book's examples package automatically. For instance,
with release 1.4 the PyGadgets and PyDemos auto-launchers, as well as the larger
stand-alone programs such as PyMailGUI, PyEdit, and PyCalc will work on 3.3
through 3.7 out of the box. Still, you'll need to manually install Pillow instead of PIL for
image-processing examples such as PyPhoto, and may need to set your
PY_PYTHON on Windows
for general 3.X use.
Note that all these changes are backward-compatible with earlier 3.X releases, so release 1.4 of the book's examples package works with Pythons 3.0 through 3.7, and perhaps later. Later Pythons may or may not require additional patches; watch for updates to be posted here as impact warrants and time allows.
Also note that most book examples will run unchanged under 3.3 through 3.7; the steps outlined here are mainly required for specific programs only, and mostly for larger graphical programs. That said, you shouldn't be surprised if some examples do require updates not listed here. Change is a constant in software development—an indirect but important lesson in itself.
PY_PYTHONsettings (the first bullet above) optional in some contexts. Unfortunately, this was more convolution than fix, and the settings are still required in some cases—and always, for Pythons 3.0 through 3.5. Read the full story here.
PP4E\Internet\Email\PyMailGui. You must use your own servers and accounts for most email, FTP, and other client-side Internet examples in the book. For more on Python 3.3 itself, see this review.
the new Windows launcher automatically installed with Python 3.3 runs scripts
and commands with an installed 2.X by default, unless they give a more specific
release number in
#! patterns or command-argument options. This can effectively
break programs meant to be used with 3.X if they're launched with version-agnostic
techniques or have top-level script files with missing or release-ambiguous
As it's common practice on Windows to omit a
#! line formerly useful only in some
Unix usage modes, this can have broad impacts. Even on Unix, scripts with
that name a generic "python" resolved by file links are a norm, but may now fail if run by
Python 3.3 on Windows.
To work around this, you can either:
#!...python3line to the top of every Python 3.X script you run on Windows—a bizarre expectation that needlessly adds extra work, and seems just plain rude to people who use Python on Windows.
PY_PYTHONenvironment variable to
3to force the 3.3 launcher to default to the highest-numbered 3.X globally for all programs that don't give a version explicitly.
PY_PYTHONsetting applies to all scripts run on your machine, it doesn't help much for scripts meant to run immediately with no user configuration steps—like some of those in the book. In lieu of
PY_PYTHONsettings, the changes required in auto-launcher scripts are subtle, and depend on how the launched code is run:
#!...python3line, so that the entire process is started with 3.X.
os.spawnv(), etc.—the launcher script may or may not require a
#!...python3line for its own code, but must set
os.environfor any version-generic spawned programs (
os.environsettings are inherited by subprocesses).
PY_PYTHONmakes both of these changes unnecessary and is recommended in general, but that obviously violates auto-launchers' goals.
To work even in the absence of
release 1.4 of the examples package applies
the required changes to the book's four auto-launcher scripts, plus one utility module that they
leverage; the launchers see regular action as drug-out shortcuts on my Windows desktops (and were
starting points for later standalone releases):
|Added both |
Notice the last entry in this table:
to be inherited by spawned programs. This may be moot for its immediate subprocesses, because this
module runs explicit
python ... command lines with
os.spawnv() after setting
Python and system paths automatically, thereby cutting out the 3.3
py launcher in the Windows registry;
deeper program descendants, however, may still require this setting. Complex to be sure, but avoidable
if you're willing to require an extra environment setting for using existing 3.X code under 3.3
on Windows, a manual task PP4E's top-level launchers were designed to obviate.
Also keep in mind that this patch applies to the selected auto-launcher scripts only.
When using 3.3 and later, you'll still probably want to manually set your
environment variable eventually in order to run other version-agnostic 3.X scripts on Windows
with 3.X instead of 2.X. This includes any book examples not updated by this patch; see the
Advanced system settings in your Control Panel's System entry to set the version default
Although this patch suffices in the normal case, there are three issues that may impact your Windows launcher experience worth noting here:
It's not impossible that hardcoding a
python3 in a
#! line for use on Windows may not
be portable to some Unix systems, which would virtually force a manual
setting on Windows machines with 2.X installs. If this impacts you, you'll have to modify
the patched scripts as needed—set
PY_PYTHON, and either use a generic
lines, or avoid
#! lines altogether (per the next bullet,
PATH can do similar work with
#! on 3.4). All Unix portability bets are off, of course, if you use a
#! pattern not common on Unix; an abbreviated
#!python3 may be a prime suspect.
The Windows launcher was modified in 3.4 to give a
PATH variable setting
priority over searching the registry for the highest-numbered Python, when just a generic
python appears in a
#! line. This better emulates the
/usr/bin/env paradigm on Unix
(and may allow for more portable
#! lines in general), but is apparently employed only when
python is used in
#! lines—not when
#! lines are absent altogether,
the normal case on Windows, and an additional case addressed by
For more background on this change, see Python issue reports:
For unknown reasons, Python 3.3+ Windows MSI installers
can sometimes fail to properly set filename association in the Windows registry for launcher use.
This should automatically associate
.pyw file types
(plus bytecode) with the launcher's
pyw.exe executables, respectively.
However, this has been seen to fail on 2 of 6 Windows machines tested—on 2 machines running Windows Vista and 7,
Python files are incorrectly associated with a former
after installing 3.3;
on 4 other machines running XP, Vista, 7, and 8, the associations are correctly set to the launcher's
programs instead. All these machines had prior Pythons installed, of various versions (3.2 and 2.6
where associations failed; 3.2, 3.1, and 2.5 where they worked).
Without the expected filename associations, the launcher is never invoked for scripts run without explicit
command lines—scripts will instead run under any Python named in the registry, and will
happily ignore any
#! lines completely, despite 3.3's best intentions. This was noticeable when 3.2 was
still inexplicably running scripts after a 3.3 install, despite contradictory
#! directives; more generally,
you can detect this problem in simpler terms by running a script from the command line as in the following, shown here
on a system with incorrect associations:
c:\PP4E> type t.py #!/usr/bin/python3.3 import sys print(sys.version) c:\PP4E> t.py 3.2.2 (default, Sep 4 2011, 09:07:29) [MSC v.1500 64 bit (AMD64)] c:\PP4E> py t.py 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:06:53) [MSC v.1600 64 bit (AMD64)]
On another failer, this turns out even worse (the middle command here also emulates the effect of a filename icon click):
C:\PP4E> type t.py #!/usr/bin/python3 import sys print(sys.version) C:\PP4E> t.py 2.6 (r26:66721, Oct 2 2008, 11:35:03) [MSC v.1500 32 bit (Intel)] C:\PP4E> py t.py 3.4.0a2 (v3.4.0a2:9265a2168e2c+, Sep 8 2013, 19:41:05) [MSC v.1600 32 bit (Intel)]
If the first command doesn't run the same highest-numbered 3.X Python installed and
print the same version as the second, your associations are probably incorrect. In
this case, the association reported by
assoc .py and
Windows command-line tools seem correct, but are apparently overridden by registry settings,
and link to "python.exe" instead of "Python Launcher for Windows" in the Default Programs GUI.
To fix: open your Default Programs in Control Panel (or similar), and manually
associate all 4 Python file extensions with the launcher's
(normally installed in
C:\Windows), a regrettable and error-prone step, but a one-time
event. This may or may not be a "bug" in the Windows launcher (the registry is notoriously
fragile), but it's certainly an issue; it is especially difficult to justify a mandatory
change that requires extra work and can break existing programs, if that change itself
cannot be relied on to work as advertised.
For more on the 3.3 Windows launcher in general, see the new Appendix B in
Learning Python, 5th Edition,
or its early draft
often prescribed alternative to environment settings mentioned earlier—adding
#! line at the top of every script on Windows—seems absurd and extreme,
especially given other launcher issues like those of the former and following sections.
Update: per the updates list above Python 3.6 bifurcated the Windows launcher's defaults to select a Python 3.X in some—but not all—contexts; read the details here. Unfortunately, this muddles the story further.
the new Windows launcher installed with Python 3.3 fails when
a script begins with a Unix
#! line that it cannot recognize. Some book
example scripts used a
#!/bin/env python pattern that was ignored on Windows
by every Python through 3.2, but is now treated as an error by 3.3. Such
#! forms are valid and even required on some Unix systems, but must be changed
to a launcher-accepted pattern such as
#!/usr/bin/env python in order to run under 3.3 on Windows—a platform
where such lines are otherwise irrelevant (yes: !).
Note that this is true even if you use
python3 in the
#! line or set your
PY_PYTHON=3 per the prior section; an unrecognized
#! pattern fails in 3.3
even if it's not version-ambiguous, and even if it's valid on a Unix system.
In release 1.4,
this fix was applied to the
#! lines at the top of the following
10 files, changing their
no other code or environment changes are required for this issue:
|PP4E\Launch_PyDemos.pyw (also changed for prior section)|
|PP4E\Launch_PyGadgets_bar.pyw (also changed for prior section)|
You can locate and edit these files yourself in older example packages
by using the Search/Grep tool in the book's PyEdit GUI example on the
package's PP4E root directory, or by running one of the directory search and
edit/replace utilities presented in the System Programming part of the book
Visitor subclasses later in Chapter 6).
Fixing any additional unrecognized
#! patterns lurking in the examples
package is an officially suggested exercise.
For more on the 3.3 Windows launcher in general, see the new Appendix B in Learning Python, 5th Edition, or its early draft here.
Python 3.3 changed a utility in the standard library's
PP4E\Internet\Email\PyMailGui, a new file that restores the prior version of the
PP4E\Internet\Email\PyMailGui, a modified file which runs an import of the patch to affect the change
This is a small bit of "monkey-patching"—replacing part of a module at runtime—which should be avoided in general, but in this case suffices to make the existing code run as is. The changes required to fully support Python's email API changes since the book's publication would be much more involved.
The new and modified file of the patch are automatically included in PyMailGUI's source code directory of the new release 1.4 of the examples package; no other code or environment changes are required.
As mentioned here, the third-party PIL image-processing library used by examples in the book has been subsumed by the Pillow fork—an open source drop-in replacement for PIL which is being actively supported for use in newer Python releases. You'll want to fetch and install Pillow to use most of the book's image examples, including the PyPhoto viewer, which employs image display, thumbnail generation, and resize operations. This page's author has also used Pillow successfully for EXIF photo metadata tag processing.
Pillow is currently
available at this site. For example, all
image-based book examples run fine under on Python 3.3 after installing the following on my
Windows 32- and 64-bit systems, respectively:
Also note that Pillow is not required of all image display-only programs: as of Python 3.4
No fix was applied, because the example package doesn't ship 3rd-party systems as bundled
items as a rule; you'll want to fetch and install Pillow for your Python and platform, from
its official site above.
tkinter can display PNG images (thanks to Tk 8.6), and all Python 3.X's can display GIF and
PPM/PPG images. Pillow is still required for other image type and Python combinations,
as well as for other tasks beyond display—including the thumbnail and resizing operations
implemented by PyPhoto (now part of standalone PyGadgets).
In the Book Examples Package
Also note that Pillow is not required of all image display-only programs: as of Python 3.4
No fix was applied, because the example package doesn't ship 3rd-party systems as bundled items as a rule; you'll want to fetch and install Pillow for your Python and platform, from its official site above.