Read Programming Python Online

Authors: Mark Lutz

Tags: #COMPUTERS / Programming Languages / Python

Programming Python (36 page)

BOOK: Programming Python
12.24Mb size Format: txt, pdf, ePub
ads
A Portable Program-Launch Framework

With all of these
different ways to start programs on different platforms, it
can be difficult to remember what tools to use in a given situation.
Moreover, some of these tools are called in ways that are complicated and
thus easy to forget. Although modules like
subprocess
and
multiprocessing
offer fully portable options
today, other tools sometimes provide more specific behavior that’s better
on a given platform; shell window pop ups on Windows, for example, are
often better suppressed.

I write scripts that need to launch Python programs often enough
that I eventually wrote a module to try to hide most of the underlying
details. By encapsulating the details in this module, I’m free to change
them to use new tools in the future without breaking code that relies on
them. While I was at it, I made this module smart enough to automatically
pick a “best” launch scheme based on the underlying platform. Laziness is
the mother of many a useful module.

Example 5-36
collects in a
single module many of the techniques we’ve met in this chapter. It
implements an abstract superclass,
LaunchMode
,
which defines what it means to start a Python program named
by a shell command line, but it doesn’t define how. Instead, its
subclasses provide a
run
method that
actually starts a Python program according to a given scheme and
(optionally) define an
announce
method
to display a program’s name at startup time.

Example 5-36. PP4E\launchmodes.py

"""
###################################################################################
launch Python programs with command lines and reusable launcher scheme classes;
auto inserts "python" and/or path to Python executable at front of command line;
some of this module may assume 'python' is on your system path (see Launcher.py);
subprocess module would work too, but os.popen() uses it internally, and the goal
is to start a program running independently here, not to connect to its streams;
multiprocessing module also is an option, but this is command-lines, not functions:
doesn't make sense to start a process which would just do one of the options here;
new in this edition: runs script filename path through normpath() to change any
/ to \ for Windows tools where required; fix is inherited by PyEdit and others;
on Windows, / is generally allowed for file opens, but not by all launcher tools;
###################################################################################
"""
import sys, os
pyfile = (sys.platform[:3] == 'win' and 'python.exe') or 'python'
pypath = sys.executable # use sys in newer pys
def fixWindowsPath(cmdline):
"""
change all / to \ in script filename path at front of cmdline;
used only by classes which run tools that require this on Windows;
on other platforms, this does not hurt (e.g., os.system on Unix);
"""
splitline = cmdline.lstrip().split(' ') # split on spaces
fixedpath = os.path.normpath(splitline[0]) # fix forward slashes
return ' '.join([fixedpath] + splitline[1:]) # put it back together
class LaunchMode:
"""
on call to instance, announce label and run command;
subclasses format command lines as required in run();
command should begin with name of the Python script
file to run, and not with "python" or its full path;
"""
def __init__(self, label, command):
self.what = label
self.where = command
def __call__(self): # on call, ex: button press callback
self.announce(self.what)
self.run(self.where) # subclasses must define run()
def announce(self, text): # subclasses may redefine announce()
print(text) # methods instead of if/elif logic
def run(self, cmdline):
assert False, 'run must be defined'
class System(LaunchMode):
"""
run Python script named in shell command line
caveat: may block caller, unless & added on Unix
"""
def run(self, cmdline):
cmdline = fixWindowsPath(cmdline)
os.system('%s %s' % (pypath, cmdline))
class Popen(LaunchMode):
"""
run shell command line in a new process
caveat: may block caller, since pipe closed too soon
"""
def run(self, cmdline):
cmdline = fixWindowsPath(cmdline)
os.popen(pypath + ' ' + cmdline) # assume nothing to be read
class Fork(LaunchMode):
"""
run command in explicitly created new process
for Unix-like systems only, including cygwin
"""
def run(self, cmdline):
assert hasattr(os, 'fork')
cmdline = cmdline.split() # convert string to list
if os.fork() == 0: # start new child process
os.execvp(pypath, [pyfile] + cmdline) # run new program in child
class Start(LaunchMode):
"""
run command independent of caller
for Windows only: uses filename associations
"""
def run(self, cmdline):
assert sys.platform[:3] == 'win'
cmdline = fixWindowsPath(cmdline)
os.startfile(cmdline)
class StartArgs(LaunchMode):
"""
for Windows only: args may require real start
forward slashes are okay here
"""
def run(self, cmdline):
assert sys.platform[:3] == 'win'
os.system('start ' + cmdline) # may create pop-up window
class Spawn(LaunchMode):
"""
run python in new process independent of caller
for Windows or Unix; use P_NOWAIT for dos box;
forward slashes are okay here
"""
def run(self, cmdline):
os.spawnv(os.P_DETACH, pypath, (pyfile, cmdline))
class Top_level(LaunchMode):
"""
run in new window, same process
tbd: requires GUI class info too
"""
def run(self, cmdline):
assert False, 'Sorry - mode not yet implemented'
#
# pick a "best" launcher for this platform
# may need to specialize the choice elsewhere
#
if sys.platform[:3] == 'win':
PortableLauncher = Spawn
else:
PortableLauncher = Fork
class QuietPortableLauncher(PortableLauncher):
def announce(self, text):
pass
def selftest():
file = 'echo.py'
input('default mode...')
launcher = PortableLauncher(file, file)
launcher() # no block
input('system mode...')
System(file, file)() # blocks
if sys.platform[:3] == 'win':
input('DOS start mode...') # no block
StartArgs(file, file)()
if __name__ == '__main__': selftest()

Near the end of the file, the module picks a default class based on
the
sys.platform
attribute:
PortableLauncher
is set to a class that uses
spawnv
on Windows and one that uses the
fork
/
exec
combination elsewhere; in recent Pythons,
we could probably just use the
spawnv
scheme on most platforms, but the alternatives in this module are used in
additional contexts. If you import this module and always use its
PortableLauncher
attribute, you can forget many
of the platform-specific details enumerated in this chapter.

To run a Python program, simply import the
PortableLauncher
class, make an instance by
passing a label and command line (without a leading “python” word), and
then call the instance object as though it were a function. The program is
started by a
call
operation—by its
__call__
operator-overloading method, instead of
a normally named method—so that the classes in this module can also be
used to generate callback handlers in tkinter-based GUIs. As we’ll see in
the upcoming chapters, button-presses in tkinter invoke a callable object
with no arguments; by registering a
PortableLauncher
instance to handle the press
event, we can automatically start a new program from another program’s
GUI. A GUI might associate a launcher with a GUI’s button press with code
like this:

Button(root, text=name, command=PortableLauncher(name, commandLine))

When run standalone, this module’s
selftest
function is invoked as usual. As coded,
System
blocks the caller until the
program exits, but
PortableLauncher
(really,
Spawn
or
Fork
) and
Start
do not:

C:\...\PP4E>
type echo.py
print('Spam')
input('press Enter')
C:\...\PP4E>
python launchmodes.py
default mode...
echo.py
system mode...
echo.py
Spam
press Enter
DOS start mode...
echo.py

As more practical applications, this file is also used in
Chapter 8
to launch GUI dialog demos
independently, and again in a number of
Chapter 10
’s examples, including
PyDemos
and PyGadgets—launcher scripts
designed to run major examples in this book in a portable fashion, which
live at the top of this book’s examples distribution directory. Because
these launcher scripts simply import
PortableLauncher
and register instances to
respond to GUI events, they run on both Windows and Unix unchanged
(tkinter’s portability helps, too, of course). The PyGadgets script even
customizes
PortableLauncher
to update a GUI label at
start time:

class Launcher(launchmodes.PortableLauncher):    # use wrapped launcher class
def announce(self, text): # customize to set GUI label
Info.config(text=text)

We’ll explore these two client scripts, and others, such as
Chapter 11
’s PyEdit after we start coding GUIs in
Part III
. Partly because of its role in PyEdit,
this edition extends this module to automatically replace forward slashes
with
backward slashes
in the script’s file path name.
PyEdit uses forward slashes in some filenames because they are allowed in
file opens on Windows, but some Windows launcher tools require the
backslash form instead. Specifically,
system
,
popen
, and
startfile
in
os
require backslashes, but
spawnv
does not. PyEdit and others inherit the
new pathname fix of
fix
Windows
Path
here simply by importing and using
this module’s classes; PyEdit
eventually
changed so as to make this fix
irrelevant for its own use case (see
Chapter 11
), but other clients still acquire the
fix for free.

Also notice how some of the classes in this example use the
sys.executable
path string to obtain the Python
executable’s full path name. This is partly due to their role in
user-friendly demo launchers. In prior versions that predated
sys.executable
, these classes instead called two
functions exported by a module named
Launcher.py
to
find a suitable Python executable, regardless of whether the user had
added its directory to the system
PATH
variable’s setting.

This search is no longer required. Since I’ll describe this module’s
other roles in the next chapter, and since this search has been largely
precluded by Python’s perpetual pandering to programmers’ professional
proclivities, I’ll postpone any pointless pedagogical presentation
here. (Period.)

Other System Tools Coverage

That concludes our
tour of Python system tools. In this and the prior three
chapters, we’ve met most of the commonly used system tools in the Python
library. Along the way, we’ve also learned how to use them to do useful
things such as start programs, process directories, and so on. The next
chapter wraps up this domain by using the tools we’ve just met to
implement scripts that do useful and more realistic system-level
work.

Still other system-related tools in Python appear later in this
text. For instance:

  • Sockets, used to communicate with other programs and networks
    and introduced briefly here, show up again in
    Chapter 10
    in a common GUI use case and are
    covered in full in
    Chapter 12
    .

  • Select calls, used to multiplex among tasks, are also introduced
    in
    Chapter 12
    as a way to implement
    servers.

  • File locking with
    os.open
    ,
    introduced in
    Chapter 4
    , is
    discussed again in conjunction with later examples.

  • Regular expressions, string pattern matching used by many text
    processing tools in the system administration domain, don’t appear
    until
    Chapter 19
    .

Moreover, things like forks and threads are used extensively in the
Internet scripting chapters: see the discussion of threaded GUIs in
Chapters
9
and
10
; the
server implementations in
Chapter 12
; the FTP
client GUI in
Chapter 13
; and the PyMailGUI
program in
Chapter 14
. Along the way, we’ll
also meet higher-level Python modules, such as
socketserver
, which implement fork and
thread-based socket server code for us. In fact, many of the last four
chapters’ tools will pop up constantly in later examples in this
book—about what one would expect of general-purpose portable
libraries.

Last, but not necessarily least, I’d like to point out one more time
that many additional tools in the Python library don’t appear in this book
at all. With hundreds of library modules, more appearing all the time, and
even more in the third-party domain, Python book authors have to pick and
choose their topics frugally! As always, be sure to browse the Python
library manuals and Web early and often in your Python career.

Chapter 6. Complete System Programs
“The Greps of Wrath”

This chapter wraps up our look at the system interfaces domain in
Python by presenting a collection of larger Python scripts that do real
systems work—comparing and copying directory trees, splitting files,
searching files and directories, testing other programs, configuring
launched programs’ shell environments, and so on. The examples here are
Python system utility programs that illustrate typical tasks and
techniques in this domain and focus on applying built-in tools, such as
file and directory tree processing.

Although the main point of this case-study chapter is to give you a
feel for realistic scripts in action, the size of these examples also
gives us an opportunity to see Python’s support for development paradigms
like object-oriented programming (OOP) and reuse at work. It’s really only
in the context of nontrivial programs such as the ones we’ll meet here
that such tools begin to bear tangible fruit. This chapter also emphasizes
the “why” of system tools, not just the “how”; along the way, I’ll point
out real-world needs met by the examples we’ll study, to help you put the
details in context.

One note up front: this chapter moves quickly, and a few of its
examples are largely listed just for independent study. Because all the
scripts here are heavily documented and use Python system tools described
in the preceding chapters, I won’t go through all the code in exhaustive
detail. You should read the source code listings and experiment with these
programs on your own computer to get a better feel for how to combine
system interfaces to accomplish realistic tasks. All are available in
source code form in the book’s examples distribution and most work on all
major platforms.

I should also mention that most of these are programs I have really
used, not examples written just for this book. They were coded over a
period of years and perform widely differing tasks, so there is no obvious
common thread to connect the dots here other than need. On the other hand,
they help explain why system tools are useful in the first place,
demonstrate larger development concepts that simpler examples cannot, and
bear collective witness to the simplicity and portability of automating
system tasks with Python. Once you’ve mastered the basics, you’ll wish you
had done so sooner.

BOOK: Programming Python
12.24Mb size Format: txt, pdf, ePub
ads

Other books

Blood Money by Maureen Carter
The Age of Empathy by Frans de Waal
Legion Lost by K.C. Finn
Red Mortal by Deidre Knight
The Mermaids Singing by Val McDermid
Their Solitary Way by JN Chaney
Without Chase by Jo Frances


readsbookonline.com Copyright 2016 - 2024