be useful for writing minilanguages, e.g.\ in run control files for
Python applications.
-\begin{classdesc}{shlex}{\optional{stream}, \optional{file}}
+\begin{classdesc}{shlex}{\optional{stream\optional{, file}}}
A \class{shlex} instance or subclass instance is a lexical analyzer
object. The initialization argument, if present, specifies where to
read characters from. It must be a file- or stream-like object with
A \class{shlex} instance has the following methods:
+
\begin{methoddesc}{get_token}{}
Return a token. If tokens have been stacked using
\method{push_token()}, pop a token off the stack. Otherwise, read one
documented here only for the sake of completeness.)
\end{methoddesc}
-\begin{methoddesc}{openhook}{filename}
-When shlex detects a source request (see \member{source} below)
-this method is given the following token as argument, and expected to
-return a tuple consisting of a filename and an opened stream object.
-
-Normally, this method just strips any quotes off the argument and
-treats it as a filename, calling \code{open()} on it. It is exposed so that
-you can use it to implement directory search paths, addition of
-file extensions, and other namespace hacks.
-
+\begin{methoddesc}{sourcehook}{filename}
+When \class{shlex} detects a source request (see
+\member{source} below) this method is given the following token as
+argument, and expected to return a tuple consisting of a filename and
+an open file-like object.
+
+Normally, this method first strips any quotes off the argument. If
+the result is an absolute pathname, or there was no previous source
+request in effect, or the previous source was a stream
+(e.g. \code{sys.stdin}), the result is left alone. Otherwise, if the
+result is a relative pathname, the directory part of the name of the
+file immediately before it on the source inclusion stack is prepended
+(this behavior is like the way the C preprocessor handles
+\code{\#include "file.h"}). The result of the manipulations is treated
+as a filename, and returned as the first component of the tuple, with
+\function{open()} called on it to yield the second component.
+
+This hook is exposed so that you can use it to implement directory
+search paths, addition of file extensions, and other namespace hacks.
There is no corresponding `close' hook, but a shlex instance will call
-the \code{close()} method of the sourced input stream when it returns EOF.
+the \method{close()} method of the sourced input stream when it
+returns \EOF.
\end{methoddesc}
-\begin{methoddesc}{error_leader}{\optional{file}, \optional{line}}
+\begin{methoddesc}{error_leader}{\optional{file\optional{, line}}}
This method generates an error message leader in the format of a
-Unix C compiler error label; the format is '"\%s", line \%d: ',
-where the \%s is replaced with the name of the current source file and
-the \%d with the current input line number (the optional arguments
-can be used to override these).
-
-This convenience is provided to encourage shlex users to generate
-error messages in the standard, parseable format understood by Emacs
-and other Unix tools.
+\UNIX{} C compiler error label; the format is '"\%s", line \%d: ',
+where the \samp{\%s} is replaced with the name of the current source
+file and the \samp{\%d} with the current input line number (the
+optional arguments can be used to override these).
+
+This convenience is provided to encourage \module{shlex} users to
+generate error messages in the standard, parseable format understood
+by Emacs and other \UNIX{} tools.
\end{methoddesc}
Instances of \class{shlex} subclasses have some public instance
-variables which either control lexical analysis or can be used
-for debugging:
+variables which either control lexical analysis or can be used for
+debugging:
\begin{memberdesc}{commenters}
The string of characters that are recognized as comment beginners.
\begin{memberdesc}{wordchars}
The string of characters that will accumulate into multi-character
-tokens. By default, includes all \ASCII{} alphanumerics and
+tokens. By default, includes all \ASCII{} alphanumerics and
underscore.
\end{memberdesc}
\end{memberdesc}
\begin{memberdesc}{instream}
-The input stream from which this shlex instance is reading characters.
+The input stream from which this \class{shlex} instance is reading
+characters.
\end{memberdesc}
\begin{memberdesc}{source}
-This member is None by default. If you assign a string to it, that
-string will be recognized as a lexical-level inclusion request similar
-to the `source' keyword in various shells. That is, the immediately
-following token will opened as a filename and input taken from that
-stream until EOF, at which point the \code{close()} method of that
-stream will be called and the input source will again become the
-original input stream. Source requests may be stacked any number of
-levels deep.
+This member is \code{None} by default. If you assign a string to it,
+that string will be recognized as a lexical-level inclusion request
+similar to the \samp{source} keyword in various shells. That is, the
+immediately following token will opened as a filename and input taken
+from that stream until \EOF, at which point the \method{close()}
+method of that stream will be called and the input source will again
+become the original input stream. Source requests may be stacked any
+number of levels deep.
\end{memberdesc}
\begin{memberdesc}{debug}
-If this member is numeric and 1 or more, a shlex instance will print
-verbose progress output on its behavior. If you need to use this,
-you can read the module source code to learn the details.
+If this member is numeric and \code{1} or more, a \class{shlex}
+instance will print verbose progress output on its behavior. If you
+need to use this, you can read the module source code to learn the
+details.
\end{memberdesc}
Note that any character not declared to be a word character,
# Module and documentation by Eric S. Raymond, 21 Dec 1998
# Input stacking and error message cleanup added by ESR, March 2000
+import os.path
import sys
+
class shlex:
"A lexical analyzer class for simple shell-like syntaxes."
def __init__(self, instream=None, infile=None):
self.filestack = []
self.source = None
if self.debug:
- print 'shlex: reading from %s, line %d' % (self.instream,self.lineno)
+ print 'shlex: reading from %s, line %d' \
+ % (self.instream, self.lineno)
def push_token(self, tok):
"Push a token onto the stack popped by the get_token method"
# Handle inclusions
while raw == self.source:
(newfile, newstream) = self.sourcehook(self.read_token())
- self.filestack = [(self.infile,self.instream,self.lineno)] + self.filestack
+ self.filestack.insert(0, (self.infile, self.instream, self.lineno))
self.infile = newfile
self.instream = newstream
self.lineno = 1
(self.infile, self.instream, self.lineno) = self.filestack[0]
self.filestack = self.filestack[1:]
if self.debug:
- print 'shlex: popping to %s, line %d' % (self.instream, self.lineno)
+ print 'shlex: popping to %s, line %d' \
+ % (self.instream, self.lineno)
self.state = ' '
raw = self.get_token()
# Neither inclusion nor EOF
if nextchar == '\n':
self.lineno = self.lineno + 1
if self.debug >= 3:
- print "shlex: in state " + repr(self.state) + " I see character: " + repr(nextchar)
+ print "shlex: in state", repr(self.state), \
+ "I see character:", repr(nextchar)
if self.state == None:
self.token = ''; # past end of file
break
"Hook called on a filename to be sourced."
if newfile[0] == '"':
newfile = newfile[1:-1]
+ # This implements cpp-like semantics for relative-path inclusion.
+ if type(self.infile) == type("") and not os.path.isabs(newfile):
+ newfile = os.path.join(os.path.dirname(self.infile), newfile)
return (newfile, open(newfile, "r"))
def error_leader(self, infile=None, lineno=None):
lineno = self.lineno
return "\"%s\", line %d: " % (infile, lineno)
-if __name__ == '__main__':
- lexer = shlex()
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ lexer = shlex()
+ else:
+ file = sys.argv[1]
+ lexer = shlex(open(file), file)
while 1:
tt = lexer.get_token()
- print "Token: " + repr(tt)
- if not tt:
+ if tt:
+ print "Token: " + repr(tt)
+ else:
break
-