[go: up one dir, main page]

File: sclapp.txt

package info (click to toggle)
sclapp 0.5.2-1
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 312 kB
  • ctags: 464
  • sloc: python: 2,916; makefile: 35
file content (837 lines) | stat: -rw-r--r-- 29,608 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
================================================================================
                                    sclapp 
================================================================================

--------------------------------------------------------------------------------
              A framework for python command-line applications.
--------------------------------------------------------------------------------


:Author: Forest Bond
:Copyright: © 2005-2007 Forest Bond
:License: GPL version 2 (see the COPYING file)


Overview
========

sclapp is a Python module that makes it easier to write well-behaved
command-line applications.

Good command-line applications respond in a consistent manner to common
situations that occur during normal use in a shell.  Programs that do not
behave consistently with other command-line applications are not intuitive for
command-line users.  This makes them unpleasant to use.

sclapp helps command-line programs deal with the following issues:

* Signal handling
* Terminal character encodings
* Standard output failures (broken pipes)
* Common command-line options (like --help and --version)

Using sclapp to implement functionality most command-line programs should
implement reduces boiler-plate code and increases consistency across
applications.

In addition to these standard features, sclapp also provides other functionality
that developers of command-line programs may find helpful.  Many of these
features we added as I needed them for my programs, but are general enough that
others may find use for them as well.


A Note On The ``locale`` Module
===============================

The standard Python ``locale`` module always returns "mac-roman" on Darwin
systems, ignoring the LC_* and LANG environment variables.  sclapp takes special
precautions against this by wrapping the import with some code that works
around this quirk.  To take advantage of this, callers should not import the
``locale`` module directly, but rather import it directly from sclapp::

  from sclapp import locale


Wrapping The Main Function
==========================

Wrapping the main function with some additional functionality is, to a limited
extent, the raison d'être of the sclapp module. The goals of these features are
the following:

* Improved signal handling on POSIX systems
* More graceful handling of stdout and stderr failure conditions
* Reduced boiler-plate code and increased consistency for:
  - Standard help (--help/-h) and (--version/-v) options
  - User-friendly handling of uncaught exceptions
  - Reporting critical failures to the user
  - Daemonization
  - Stdio character decoding and encoding
  - Passing of sys.argv into the main function

At its simplest, utilizing sclapp's main-wrapping features is as easy as adding
a decorator to our main function::

>>> import sys, sclapp

>>> @sclapp.main_function
... def main(argv):
...     do_something()

>>> if __name__ == '__main__':
...     sys.exit(main())

While this example utilizes the main_function decorator, the mainWrapper
function could have been just as easily used::

>>> import sys, sclapp

>>> def main(argv):
...     do_something()
>>> main = sclapp.mainWrapper(main)

>>> if __name__ == '__main__':
...     sys.exit(main())

The differences between the main_function decorator and the mainWrapper function
are covered in detail below.  While duplicate examples will not be provided
throughout the entirety of this document, be aware that both methods can be used
to achieve the same end.  The distinction between the two is largely syntactic.

The following sub-sections discuss the benefits provided by sclapp's main
function-wrapping features.


Improved Signal Handling
------------------------


Rationale
~~~~~~~~~

Python's default signal handling behavior is less than satisfactory for most
command-line applications.  The following simple example illustrates this::

  #!/usr/bin/env python

  '''yes.py: a yes(1) pseudo-clone in Python'''

  import sys

  def main(argv):
      if len(argv) > 1:
          string = ' '.join(argv[1:])
      else:
          string = 'y'
      while True:
          print string

  if __name__ == '__main__':
      main(sys.argv)

Let's try our yes clone out.  If we run it, we'll see an endless stream of
y's fly by on the screen.  To stop the program, our instinct would be to press
Control-C to send it an interrupt::

  $ python yes.py 
  y
  y
  y
  [...]
  y
  y
  y
  Traceback (most recent call last):
    File "yes.py", line 16, in <module>
      main(sys.argv)
    File "yes.py", line 13, in main
      print string
  KeyboardInterrupt

Python converts SIGINT to a KeyboardInterrupt (a perfectly reasonable thing to
do, given Python's cross-platform nature), which causes a traceback to be
printed when we press Control-C.  The program does quit like we wanted it to,
but this error message is surely not going to go over well with users.  In order
to write a good yes clone, we'll need to handle KeyboardInterrupt's.

How about another experiment::

  $ python yes.py | head -n5
  y
  y
  y
  y
  y
  Traceback (most recent call last):
    File "yes.py", line 16, in <module>
      main(sys.argv)
    File "yes.py", line 13, in main
      print string
  IOError: [Errno 32] Broken pipe

We get another traceback, but with a different exception.  Here, Python is
converting SIGPIPE into an IOError.  Again, this is probably reasonable behavior
for many cross-platform programs, but programs that are designed for use at the
command-line will need to handle this better.  Pipes are a key operation with
UNIX shells.

Other situations can be tested, too.  Python responds to SIGHUP by immediately
exiting, with no exception raised, and printing "Hangup" to standard error.
With our yes clone that's not too far from what we want, but what if our program
required some cleanup action to be performed prior to exiting?  With no
exception raised, our cleanup code would never have the opportunity to run.

The problem here is that we really do want (or need) to handle signals
explicitly in order to avoid these issues.  There are a few reasons many Python
command-line programs don't actually do this, though:

* It's easy to write bad signal-handling code that is either not responsive
  enough, incorrect, or unreliable.
* Writing signal handlers for every Python program is a lot of work, and would
  result in a lot of duplicate code.
* It takes extra attention to handle signals explicitly for systems that support
  them, without breaking cross-platform compatibility.

sclapp makes it possible to handle signals correctly with minimal code changes,
and eliminates the duplication of code that would be the result of writing
signal handlers for every program.


Handling Signals The sclapp Way
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A more behaviorally correct yes clone could be written as follows::

  #!/usr/bin/env python

  '''yes.py: a yes(1) pseudo-clone in Python'''

  import sys, sclapp

  @sclapp.main_function
  def main(argv):
      if len(argv) > 1:
          string = ' '.join(argv[1:])
      else:
          string = 'y'
      while True:
          print string

  if __name__ == '__main__':
      sys.exit(main())

Using sclapp's default options, SIGINT, SIGPIPE, and SIGHUP trigger immediate
exit (with no exception raised).  This behavior is perfect for our yes clone.

For applications that require more sophisticated signal handling, sclapp's
signal-handling strategy is to convert signals to exceptions.  While signals can
be difficult to deal with appropriately, exceptions, are trivially handled in
Python, which has language constructs to make them easy to work with.

If we needed to perform some cleanup prior to exit, the following Pythonic idiom
would be more appropriate::

>>> import sys, sclapp

>>> @sclapp.main_function(
...   exit_signals = ('SIGPIPE', 'SIGINT', 'SIGHUP', 'SIGTERM')
... )
... def main(argv):
...     try:
...         do_something()
...     finally:
...         cleanup()

Since signals are mapped to exceptions, we can actually use a try...finally
block to execute cleanup actions.  This, of course, is the most appropriate way
to perform cleanup actions in Python.

Note that signals can be specified by name as strings or by number as integers.
If specified by name (like in the example above), any of the specified signals
which are nonexistent (due to lack of support by the local system) will be
silently ignored.  However, unsupported signals specified by number will likely
generate exceptions from calls to signal.signal.  See the documentation for the
signal module in the standard library for more information.


More Advanced Signal Handling Configurations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Of course, we may need to handle some signals differently than others, depending
on the kind of program that we are writing.

In general, sclapp considers each signal to be in one of the following four
sets:

* ``notify_signals``
* ``exit_signals``
* ``default_signals``
* ``ignore_signals``

sclapp expects that callers will specify which signals are of each type at
initialization time::

>>> import sys, signal, sclapp

>>> @sclapp.main_function(
...   notify_signals = (signal.SIGHUP, signal.SIGUSR1),
...   exit_signals = (signal.SIGINT, signal.SIGTERM, signal.SIGPIPE),
...   default_signals = ( ),
...   ignore_signals = (signal.SIGUSR2, signal.SIGALRM),
... )
... def main(argv):
...     try:
...         do_something()
...     except SignalError, e:
...         if e.signum == signal.SIGHUP:
...             handle_sighup()
...         elif e.signum == signal.SIGUSR1:
...             handle_sigusr1()
...         else:
...             raise

These signals are handled as follows:

``notify_signals``
  SignalError is raised asynchronously.  The program should catch this exception
  and do something useful with it.

``exit_signals``
  ExitSignalError is raised asynchronously, and the program should exit.
  Preferably, this exception is handled only through the use of a try...finally
  block (or equivalent).

``default_signals``
  The signal is mapped to signal.SIG_DFL.  Program behavior is system-specific.
  Many of the more command signals on many of the more common systems cause
  immediate program termination, with no exception raised.

``ignore_signals``
  The signal is mapped to signal.SIG_IGN.  The signal is completely ignored.

Note: I need to be able to ignore signals through a critical section, without
missing them altogether.

sclapp defines two exceptions that may be raised when signals are caught:

* SignalError
* ExitSignalError

Note that ExitSignalError does not inherit from SignalError, so it is
appropriate to use a try...except block to handle SignalError exceptions.

If a signal number is specified in more than one of the four categories, an
AssertionError will be raised.


Protected Output
----------------

sclapp's protected output functionality was created with the purpose of
circumventing improper program termination due to massive failure of standard
I/O.  For instance, suppose your program was run like this:

$ python myprog.py 2>&1 | head -n2

The problem that occurs in this sort of scenario is simple, but can be
destructive.  After the first two lines of output, SIGPIPE is sent to the
program.  Presumably, this triggers the program to exit, but in the process of
doing so, many programs will commonly print some messages to stderr to indicate
to the user actions being taken and the status of those actions.  If enough
output is generated, the program may be terminated without an exception being
raised.  This would be particularly bad if the program depends on an exception
initiating some cleanup action.

The scheme used by sclapp to deal with this is to disable output from stderr or
stdout if it is triggering EPIPE IOError's.  If SIGPIPE is mapped as an exit
signal, sclapp wait's until SIGPIPE has been caught before disabling the file.


Help and Version Options
------------------------

sclapp can automatically handle help and version command-line options for
callers.  The option literals are not configurable; -h/--help and -v/--version
are intercepted and handled appropriately.

To take advantage of this functionality, the ``doc`` and ``version`` keyword
arguments to main_function/mainWrapper must be specified.

Programs needing more sophisticated command-line option handling should probably
use the optparse module from the standard library.


User-Friendly Handling of Uncaught Exceptions
---------------------------------------------

Uncaught Exceptions are bugs, and sclapp handles them by printing a message to
stderr indicating that.  The exact text of that message can be customized by
passing a template string as argument bug_message to main_function or
mainWrapper.  If this is left unspecified, sclapp uses a default message (let an
exception fly to get a peek at that).

See Customizing Messages, below, for more information on template strings.


Error Reporting (CriticalError, UsageError)
-------------------------------------------

Command-line programs have a few different mechanisms by which they notify the
user of failure conditions.  In many cases, a message indicating the nature of
the error that occured should be printed to sys.stderr, and the program should
exit with a non-zero return code.

Since this functionality is a common requirement of command-line programs,
sclapp provides a few exceptions to assist:

``CriticalError``
  Indicates that an irrecoverable error that is not a usage error has occured,
  and the program must terminate.

``UsageError``
  Indicates that the user's specification of runtime parameters was erroneous.

These exceptions are caught and handled by the main-wrapping code, so callers
should raise them to indicate errors.  Note that UsageError inherits from
CriticalError.

CriticalError exceptions accept two optional positional arguments: an exit code,
and an error message.  If the message is omitted, none will be printed at exit.
If the exit code is omitted, the program will exit with zero status.

UsageError exceptions accept a signle optional positional argument: an error
message.  If this error message is not specified, the program will exit with
zero status and simply print the usage information for the program, if possible.
Otherwise, the message will be printed, the usage information for the program
will be printed, and the program will exit with the specified exit status.

Note that sclapp's main-wrapping code does not actually call the sys.exit
function.  Instead, the main function's return value will be the appropriate
exit status, and the caller should call sys.exit itself.


Daemonization
-------------

Daemonization (on UNIX-like systems) is a technique that causes programs to be
run in the background, completely detached from a terminal.  It is generally
accepted that the following four steps must be taken to properly daemonize:

1. The current working directory set to the "/" directory.
2. The current file creation mode mask set to 0.
3. Close all open files (1024). 
4. Redirect standard I/O streams to "/dev/null".

For further discussion of this, see
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731.

To this end, the keyword argument ``daemonize`` may be given to the
main_function decorator (or the mainWrapper function) and sclapp will daemonize
the program before passing control to the caller's main function.


Transparent Encoding and Decoding of Standard IO
------------------------------------------------

By default, sclapp will provide conversion to and from the preferred character
encoding (as determined by locale.getpreferredencoding) for sys.stdin,
sys.stdout, and sys.stderr.  That means that unicode objects may be printed
directly to sys.stdout and sys.stderr, and the output will be encoded
appropriately.  Characters read from sys.stdin will be decoded to unicode
strings also.

There may be some applications for which this behavior would be undesirable.  To
disable it, pass False for the one or more of the following keyword arguments to
sclapp's main_function or mainWrapper:

* decode_stdin
* encode_stdout
* encode_stderr

In particular, automatic decoding of sys.stdin causes problems for the built-in
functions input and raw_input.  Thus, when this decoding feature is engaged, the
alternative functions provided by sclapp should be used.


Command-Line Argument Decoding
------------------------------

sclapp will automatically decode the command-line arguments to Unicode strings.
If the ``decode_argv`` argument to ``main_function`` and ``mainWrapper`` is
True, the members of the ``argv`` argument passed to your main function will
contain Unicode strings instead of byte strings.  This feature is on by default.


Customizing Messages
--------------------

sclapp uses string templates to format messages.  Several arguments to the
``mainWrapper`` function represent customizable messages that the user may see
on a variety of occasions:

``version_message``
  Used for handling the --version command-line option.
``doc``
  Printed to the screen when handling the --help command-line option.
``bug_message``
  Printed to the screen when an unhandled exception is caught.

Each of these messages can be customized with the following template
substitutions:

``name``
  The name of the program.
``author``
  The author of the program.  Suggested format includes the author's e-mail
  address, like "Forest Bond <forest@alittletooquiet.net>".
``version``
  A string representing the version of the program, like "0.6.3".
``traceback``
  Traceback for the uncaught exception.  Only useful for ``bug_message``.

So, to customize these messages, use template-style string substitution.  For
instance::

>>> @sclapp.main_function(
...   version = '0.2.5',
...   author = 'Forest Bond <forest@alittletooquiet.net>',
...   doc = '''\
... Ouch!  This looks like a bug!
... 
... ${traceback}
... 
... Program version: ${version}
... 
... Please send the full text of this message in an e-mail to
... ${author}.
... '''
... )
... def main(argv):
...     return 0

See the documentation for template strings (string.Template) in the standard
library for more information on syntax.


Modules
=======


sclapp.daemonize
----------------

This module contains a function of the same name that will cause the caller's
process to become a daemon.  See the section Daemonization, above.


sclapp.pipes
------------

I implemented some strange functions that make it possible to run Python
functions in sub-processes and connect their standard I/O streams together with
pipes.  At the very least, they are useful for testing behavior with pipes.

The functions:

::

  pipeFns(fns, argses = None, kwargses = None,
    pipe_out = True, pipe_err = False,
    stdin = None, stdout = None, stderr = None)

The functions in lists ``fns`` are piped in to each other in order from first to
last.  The positional and keyword arguments for each function is specified as
arguments ``argses`` and ``kwargses``, respectively.  ``pipe_out`` and
``pipe_err`` are booleans that determine the source of the input stream for the
following function.  Arguments ``stdin``, ``stdout``, and ``stderr`` may be
specified as for ``redirection.redirectFds`` in order to change the default
standard I/O streams for the sub-processes in the pipe.


sclapp.processes
----------------

This module contains a few classes for managing background processes.  It is
often useful to run a program or function in the background.

The classes:

BackgroundFunction(
  self, function, args, kwargs, stdin = None, stdout = None, stderr = None)

This class can be used to manage a Python function running in a sub-process.
``function`` is the Python function to call; ``args`` and ``kwargs`` are the
arguments and keyword arguments (respectively) to pass to the function.

BackgroundCommand(
  self, command, args, stdin = None, stdout = None, stderr = None)

This class can be used to manage an external program running in a sub-process.
The arguments ``command`` and ``args`` should specify the command to run and the
arguments to pass to it, as for ``os.execvp``.

For both classes, the arguments ``stdin``, ``stdout``, and ``stderr`` are as for
``redirection.redirectFds``, and the sub-process's standard I/O streams will be
redirected as indicated prior to launching the caller's function or command.


sclapp.redirection
------------------

Implements a single function:

``redirectFds(stdin = None, stdout = None, stderr = None)``

The standard I/O streams will be immediately redirected as specified.  Each
argument may be one of the following:

* None, indicating no redirection for that stream.
* A file-like object supporting method ``fileno``.
* An integer file descriptor.
* A filename.


sclapp.services
---------------

startService(pid_file_name, fn, args = None, kwargs = None)

stopService(pid_file_name)


sclapp.shell
------------

This module implements some classes for interacting with ongoing shell sessions.

Warning: my testing with the version of bash distributed with Mac OSX 10.4
indicates that the readline support causes problems due to interference with
disabling the tty echo function.  If you are dealing with such a version of
bash, you should pass the ``--noediting`` command-line option to bash in order
to disable readline, and the ``--posix`` command-line option for more
standards-compliant behavior.


Starting A Session
~~~~~~~~~~~~~~~~~~

A shell session is begun by instantiating the approprate class.  Generally, you
will use the ``Shell`` class.  The class initializer has no required arguments,
however, the following optional keyword arguments may be specified:

shell (default: '/bin/sh')
  Path to the shell executable; must be Bourne compatible.
prompt (default: randomly generated prompt)
  The desired shell prompt.  Be aware that the prompt is the only indication
  that a command has finished executing, so it is normally set to a reasonably
  long random string of alpha-numeric characters.  See Searching For The Prompt,
  below.
failure_exceptions (default: True)
  Boolean specifying whether or not a CommandFailed exception should be raised
  when a command exits with a non-zero return code.
signal_exceptions (default: True)
  Boolean specifying whether or not a CommandSignalled exception should be
  raised when a command exits due to a signal
trace (default: False)
  If True, commands are printed to stdout prior to being executed.  This is for
  debugging.
timeout (default: 30)
  The initial timeout used by the underling Pexpect object.  Note that other
  timeout values may be used by various ``Shell`` methods as necessary, but
  the timeout will be restored after those temporary changes.
delaybeforesend (default: 100; defined by pexpect module)
  The number of milliseconds to delay before sending input to the shell process.
  See the pexpect documentation for more information.

Any additional keyword arguments are passed on to the underlying Pexpect
object's initializer.

The ``SudoShell`` class provides the same functionality as the ``Shell`` class,
but the shell session is started using the sudo command.  Thus, the shell has
root privileges.  The ``SudoShell`` initializer takes an additional optional
keyword argument, ``password``.  If this argument is not specified and sudo
requests a password, or if an incorrect password is specified, a ValueError is
raised.


Executing Commands
~~~~~~~~~~~~~~~~~~

There are a few different ways that commands can be executed, depending upon
what kind of behavior is desired.  The following methods can be used:

``close()``
  Close the tty that the shell is attached to.  This should always be called to
  clean up after the Shell instance.

``execute(cmd, *values)``
  Execute ``cmd``, using ``sclapp.shinterp.interpolate`` to interpolate
  ``values`` into ``cmd``.  The return value is the tuple ``(status, output)``,
  indicating the exit status and output of the command.

``follow(cmd, *values, **kwargs)``
  ``cmd`` is executed and ``values`` interpolated as with ``execute``, but
  ``follow`` is actually a generator.  Callers should iterate over the return
  value to handle each output character individually.  For instance::

    output = ''
    for ch in follow('echo foo'):
        output = output + ch
    print output

  The above code would print the string 'foo\n' to the screen.

  ``follow`` and the following wrapper methods accept a keyword argument
  ``follow_input``.  If True, the executed command is included in the resulting
  character stream.  This argument defaults to False.

``followCallback(cmd, *values, **kwargs)``
  Like ``follow``, but rather than yielding each output character to the caller,
  ``followCallback`` requires a keyword argument, ``callback``, which should be
  used to pass a callback function that will be called once for each output
  character.  The callback function should accept a single positional argument,
  the character being handled.  Returns the exit status of the command.

``followWrite(cmd, *values, **kwargs)``
  Calls ``followCallback`` with a simple callback function that writes each
  output character to a file-like object.  This object can be specified using
  optional keyword argument ``outfile``, which defaults to ``sys.stdout``.
  Returns the exit status of the command.

``followWriteReturn(cmd, *values, **kwargs)``
  Like ``followWrite``, but in addition to writing each output character to
  ``outfile``, the command output is also captured and returned as part of a
  two-tuple like that returned by ``execute``.

``interact(fitted = False)``
  Cause the shell to be connected to stdin/stdout/stderr so that the user can
  use the shell directly.  If keyword argument ``fitted`` is set to True, the
  window size for the underlying tty object is kept in sync with the window size
  of stdout.

It has been observed that commands including lines longer than 4095 characters
cause problems with some shells on some systems.  The methods above will refuse
to execute such a command unless the optional keyword argument ``force`` is
passed with a boolean True value.  Otherwise, if such a command is passed for
execution, a ValueError will be raised.


sclapp.shinterp
---------------

This module provides some simple functions for performing string interpolation
with shell quoting of parameters.  For instance:

>>> from sclapp import shinterp
>>> x, y = 'foo', 'bar'
>>> print shinterp.interpolate('cat ? ?', x, y)
cat 'foo' 'bar'

However, this function will also correctly handle double and single quotes:

>>> from sclapp import shinterp
>>> x, y = 'fo"o', "ba'r"
>>> print shinterp.interpolate('cat ? ?', x, y)
cat 'fo"o' 'ba'\''r'

To insert a literal question mark, use two of them:

>>> from sclapp import shinterp
>>> x = 'foo'
>>> print shinterp.interpolate('cat ? ??', x)
cat 'foo' ?


sclapp.stdio_encoding
---------------------

This module implements support for transparent character encoding for standard
I/O.  In addition it contains a few utility functions related to this
implementation.

Transparent decoding of standard input has been found to conflict with the
built-in functions ``raw_input`` and ``input``.  As a result, alternatives have
been implemented that do not cause problems.  These functions can be imported
from this module, and are have the same names as the functions they replace.


sclapp.termcontrol
------------------

Based on a ASPN Python Cookbook recipe written by Edward Loper, this module
wraps some basic functionality provided by the curses module to provide easy
access to some simple terminal features.

To use the module, the initialization funcdtion ``initializeTermControl`` must
first be called.  Terminal capabilities are utilized by writing control strings
to standard output.  These control strings are accessible to callers via the
dict ``sclapp.termcontrol.caps`` that is populated during initialization.

If a given terminal capability is not supported, the corresponding value in the
``caps`` dict will be the empty string.  Thus, most applications will degrade
gracefully when dealing with a terminal lacking capabilities.

The following keys are used to access terminal capabilities in the ``caps``
dict:

BOL
  Move the cursor to the beginning of the line.
UP
  Move the cursor up one line.
DOWN
  Move the cursor down one line.
LEFT
  Move the cursor left one column.
RIGHT
  Move the cursor right one column.
CLEAR_SCREEN
  Clear the screen.
CLEAR_EOL
  Clear to the end of the current line.
CLEAR_BOL
  Clear to the beginning of the current line.
CLEAR_EOS
  Clear to the end of the screen.
BOLD
  Use bold text.
BLINK
  Use blinking text.
DIM
  Use dim text.
REVERSE
  Use reverse-color text.
UNDERLINE
  Use underlined text.
NORMAL
  Use normal text (cancels previously set text color and style).
HIDE_CURSOR
  Hide the cursor.
SHOW_CURSOR
  Show the cursor.

The following dict keys correspond with various text colors.  For each color,
the same key can be used with the prefix "BG\_" to affect the background color
instead of the foreground color.

* BLACK
* BLUE
* GREEN
* CYAN
* RED
* MAGENTA
* YELLOW
* WHITE


sclapp.util
-----------

A few miscellaneous functions:

safe_encode(s)
  Encodes the Unicode string s using the encoding returned by
  locale.getprefferedencoding()  without ever raising a UnicodeEncodeError.

safe_decode(s)
  Decodes the byte string s to unicode using the encoding returned by
  locale.getpreferredencoding() without ever raising a UnicodeDecodeError.