empy-3.3.orig/0000755000175000017500000000000010277005444011353 5ustar anaanaempy-3.3.orig/em.py0000555000175000017500000033714507747162541012356 0ustar anaana#!/usr/local/bin/python # # $Id: //projects/empy/em.py#146 $ $Date: 2003/10/27 $ """ A system for processing Python as markup embedded in text. """ __program__ = 'empy' __version__ = '3.3' __url__ = 'http://www.alcyone.com/software/empy/' __author__ = 'Erik Max Francis ' __copyright__ = 'Copyright (C) 2002-2003 Erik Max Francis' __license__ = 'LGPL' import copy import getopt import os import re import string import sys import types try: # The equivalent of import cStringIO as StringIO. import cStringIO StringIO = cStringIO del cStringIO except ImportError: import StringIO # For backward compatibility, we can't assume these are defined. False, True = 0, 1 # Some basic defaults. FAILURE_CODE = 1 DEFAULT_PREFIX = '@' DEFAULT_PSEUDOMODULE_NAME = 'empy' DEFAULT_SCRIPT_NAME = '?' SIGNIFICATOR_RE_SUFFIX = r"%(\S+)\s*(.*)\s*$" SIGNIFICATOR_RE_STRING = DEFAULT_PREFIX + SIGNIFICATOR_RE_SUFFIX BANGPATH = '#!' DEFAULT_CHUNK_SIZE = 8192 DEFAULT_ERRORS = 'strict' # Character information. IDENTIFIER_FIRST_CHARS = '_abcdefghijklmnopqrstuvwxyz' \ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' IDENTIFIER_CHARS = IDENTIFIER_FIRST_CHARS + '0123456789.' ENDING_CHARS = {'(': ')', '[': ']', '{': '}'} # Environment variable names. OPTIONS_ENV = 'EMPY_OPTIONS' PREFIX_ENV = 'EMPY_PREFIX' PSEUDO_ENV = 'EMPY_PSEUDO' FLATTEN_ENV = 'EMPY_FLATTEN' RAW_ENV = 'EMPY_RAW_ERRORS' INTERACTIVE_ENV = 'EMPY_INTERACTIVE' BUFFERED_ENV = 'EMPY_BUFFERED_OUTPUT' NO_OVERRIDE_ENV = 'EMPY_NO_OVERRIDE' UNICODE_ENV = 'EMPY_UNICODE' INPUT_ENCODING_ENV = 'EMPY_UNICODE_INPUT_ENCODING' OUTPUT_ENCODING_ENV = 'EMPY_UNICODE_OUTPUT_ENCODING' INPUT_ERRORS_ENV = 'EMPY_UNICODE_INPUT_ERRORS' OUTPUT_ERRORS_ENV = 'EMPY_UNICODE_OUTPUT_ERRORS' # Interpreter options. BANGPATH_OPT = 'processBangpaths' # process bangpaths as comments? BUFFERED_OPT = 'bufferedOutput' # fully buffered output? RAW_OPT = 'rawErrors' # raw errors? EXIT_OPT = 'exitOnError' # exit on error? FLATTEN_OPT = 'flatten' # flatten pseudomodule namespace? OVERRIDE_OPT = 'override' # override sys.stdout with proxy? CALLBACK_OPT = 'noCallbackError' # is no custom callback an error? # Usage info. OPTION_INFO = [ ("-V --version", "Print version and exit"), ("-h --help", "Print usage and exit"), ("-H --extended-help", "Print extended usage and exit"), ("-k --suppress-errors", "Do not exit on errors; go interactive"), ("-p --prefix=", "Change prefix to something other than @"), (" --no-prefix", "Do not do any markup processing at all"), ("-m --module=", "Change the internal pseudomodule name"), ("-f --flatten", "Flatten the members of pseudmodule to start"), ("-r --raw-errors", "Show raw Python errors"), ("-i --interactive", "Go into interactive mode after processing"), ("-n --no-override-stdout", "Do not override sys.stdout with proxy"), ("-o --output=", "Specify file for output as write"), ("-a --append=", "Specify file for output as append"), ("-b --buffered-output", "Fully buffer output including open"), (" --binary", "Treat the file as a binary"), (" --chunk-size=", "Use this chunk size for reading binaries"), ("-P --preprocess=", "Interpret EmPy file before main processing"), ("-I --import=", "Import Python modules before processing"), ("-D --define=", "Execute Python assignment statement"), ("-E --execute=", "Execute Python statement before processing"), ("-F --execute-file=", "Execute Python file before processing"), (" --pause-at-end", "Prompt at the ending of processing"), (" --relative-path", "Add path of EmPy script to sys.path"), (" --no-callback-error", "Custom markup without callback is error"), (" --no-bangpath-processing", "Suppress bangpaths as comments"), ("-u --unicode", "Enable Unicode subsystem (Python 2+ only)"), (" --unicode-encoding=", "Set both input and output encodings"), (" --unicode-input-encoding=", "Set input encoding"), (" --unicode-output-encoding=", "Set output encoding"), (" --unicode-errors=", "Set both input and output error handler"), (" --unicode-input-errors=", "Set input error handler"), (" --unicode-output-errors=", "Set output error handler"), ] USAGE_NOTES = """\ Notes: Whitespace immediately inside parentheses of @(...) are ignored. Whitespace immediately inside braces of @{...} are ignored, unless ... spans multiple lines. Use @{ ... }@ to suppress newline following expansion. Simple expressions ignore trailing dots; `@x.' means `@(x).'. A #! at the start of a file is treated as a @# comment.""" MARKUP_INFO = [ ("@# ... NL", "Comment; remove everything up to newline"), ("@? NAME NL", "Set the current context name"), ("@! INTEGER NL", "Set the current context line number"), ("@ WHITESPACE", "Remove following whitespace; line continuation"), ("@\\ ESCAPE_CODE", "A C-style escape sequence"), ("@@", "Literal @; @ is escaped (duplicated prefix)"), ("@), @], @}", "Literal close parenthesis, bracket, brace"), ("@ STRING_LITERAL", "Replace with string literal contents"), ("@( EXPRESSION )", "Evaluate expression and substitute with str"), ("@( TEST [? THEN [! ELSE]] )", "If test is true, evaluate then, otherwise else"), ("@( TRY $ CATCH )", "Expand try expression, or catch if it raises"), ("@ SIMPLE_EXPRESSION", "Evaluate simple expression and substitute;\n" "e.g., @x, @x.y, @f(a, b), @l[i], etc."), ("@` EXPRESSION `", "Evaluate expression and substitute with repr"), ("@: EXPRESSION : [DUMMY] :", "Evaluates to @:...:expansion:"), ("@{ STATEMENTS }", "Statements are executed for side effects"), ("@[ CONTROL ]", "Control markups: if E; elif E; for N in E;\n" "while E; try; except E, N; finally; continue;\n" "break; end X"), ("@%% KEY WHITESPACE VALUE NL", "Significator form of __KEY__ = VALUE"), ("@< CONTENTS >", "Custom markup; meaning provided by user"), ] ESCAPE_INFO = [ ("@\\0", "NUL, null"), ("@\\a", "BEL, bell"), ("@\\b", "BS, backspace"), ("@\\dDDD", "three-digit decimal code DDD"), ("@\\e", "ESC, escape"), ("@\\f", "FF, form feed"), ("@\\h", "DEL, delete"), ("@\\n", "LF, linefeed, newline"), ("@\\N{NAME}", "Unicode character named NAME"), ("@\\oOOO", "three-digit octal code OOO"), ("@\\qQQQQ", "four-digit quaternary code QQQQ"), ("@\\r", "CR, carriage return"), ("@\\s", "SP, space"), ("@\\t", "HT, horizontal tab"), ("@\\uHHHH", "16-bit hexadecimal Unicode HHHH"), ("@\\UHHHHHHHH", "32-bit hexadecimal Unicode HHHHHHHH"), ("@\\v", "VT, vertical tab"), ("@\\xHH", "two-digit hexadecimal code HH"), ("@\\z", "EOT, end of transmission"), ] PSEUDOMODULE_INFO = [ ("VERSION", "String representing EmPy version"), ("SIGNIFICATOR_RE_STRING", "Regular expression matching significators"), ("SIGNIFICATOR_RE_SUFFIX", "The above stub, lacking the prefix"), ("interpreter", "Currently-executing interpreter instance"), ("argv", "The EmPy script name and command line arguments"), ("args", "The command line arguments only"), ("identify()", "Identify top context as name, line"), ("setContextName(name)", "Set the name of the current context"), ("setContextLine(line)", "Set the line number of the current context"), ("atExit(callable)", "Invoke no-argument function at shutdown"), ("getGlobals()", "Retrieve this interpreter's globals"), ("setGlobals(dict)", "Set this interpreter's globals"), ("updateGlobals(dict)", "Merge dictionary into interpreter's globals"), ("clearGlobals()", "Start globals over anew"), ("saveGlobals([deep])", "Save a copy of the globals"), ("restoreGlobals([pop])", "Restore the most recently saved globals"), ("defined(name, [loc])", "Find if the name is defined"), ("evaluate(expression, [loc])", "Evaluate the expression"), ("serialize(expression, [loc])", "Evaluate and serialize the expression"), ("execute(statements, [loc])", "Execute the statements"), ("single(source, [loc])", "Execute the 'single' object"), ("atomic(name, value, [loc])", "Perform an atomic assignment"), ("assign(name, value, [loc])", "Perform an arbitrary assignment"), ("significate(key, [value])", "Significate the given key, value pair"), ("include(file, [loc])", "Include filename or file-like object"), ("expand(string, [loc])", "Explicitly expand string and return"), ("string(data, [name], [loc])", "Process string-like object"), ("quote(string)", "Quote prefixes in provided string and return"), ("flatten([keys])", "Flatten module contents into globals namespace"), ("getPrefix()", "Get current prefix"), ("setPrefix(char)", "Set new prefix"), ("stopDiverting()", "Stop diverting; data sent directly to output"), ("createDiversion(name)", "Create a diversion but do not divert to it"), ("retrieveDiversion(name)", "Retrieve the actual named diversion object"), ("startDiversion(name)", "Start diverting to given diversion"), ("playDiversion(name)", "Recall diversion and then eliminate it"), ("replayDiversion(name)", "Recall diversion but retain it"), ("purgeDiversion(name)", "Erase diversion"), ("playAllDiversions()", "Stop diverting and play all diversions in order"), ("replayAllDiversions()", "Stop diverting and replay all diversions"), ("purgeAllDiversions()", "Stop diverting and purge all diversions"), ("getFilter()", "Get current filter"), ("resetFilter()", "Reset filter; no filtering"), ("nullFilter()", "Install null filter"), ("setFilter(shortcut)", "Install new filter or filter chain"), ("attachFilter(shortcut)", "Attach single filter to end of current chain"), ("areHooksEnabled()", "Return whether or not hooks are enabled"), ("enableHooks()", "Enable hooks (default)"), ("disableHooks()", "Disable hook invocation"), ("getHooks()", "Get all the hooks"), ("clearHooks()", "Clear all hooks"), ("addHook(hook, [i])", "Register the hook (optionally insert)"), ("removeHook(hook)", "Remove an already-registered hook from name"), ("invokeHook(name_, ...)", "Manually invoke hook"), ("getCallback()", "Get interpreter callback"), ("registerCallback(callback)", "Register callback with interpreter"), ("deregisterCallback()", "Deregister callback from interpreter"), ("invokeCallback(contents)", "Invoke the callback directly"), ("Interpreter", "The interpreter class"), ] ENVIRONMENT_INFO = [ (OPTIONS_ENV, "Specified options will be included"), (PREFIX_ENV, "Specify the default prefix: -p "), (PSEUDO_ENV, "Specify name of pseudomodule: -m "), (FLATTEN_ENV, "Flatten empy pseudomodule if defined: -f"), (RAW_ENV, "Show raw errors if defined: -r"), (INTERACTIVE_ENV, "Enter interactive mode if defined: -i"), (BUFFERED_ENV, "Fully buffered output if defined: -b"), (NO_OVERRIDE_ENV, "Do not override sys.stdout if defined: -n"), (UNICODE_ENV, "Enable Unicode subsystem: -n"), (INPUT_ENCODING_ENV, "Unicode input encoding"), (OUTPUT_ENCODING_ENV, "Unicode output encoding"), (INPUT_ERRORS_ENV, "Unicode input error handler"), (OUTPUT_ERRORS_ENV, "Unicode output error handler"), ] class Error(Exception): """The base class for all EmPy errors.""" pass EmpyError = EmPyError = Error # DEPRECATED class DiversionError(Error): """An error related to diversions.""" pass class FilterError(Error): """An error related to filters.""" pass class StackUnderflowError(Error): """A stack underflow.""" pass class SubsystemError(Error): """An error associated with the Unicode subsystem.""" pass class FlowError(Error): """An exception related to control flow.""" pass class ContinueFlow(FlowError): """A continue control flow.""" pass class BreakFlow(FlowError): """A break control flow.""" pass class ParseError(Error): """A parse error occurred.""" pass class TransientParseError(ParseError): """A parse error occurred which may be resolved by feeding more data. Such an error reaching the toplevel is an unexpected EOF error.""" pass class MetaError(Exception): """A wrapper around a real Python exception for including a copy of the context.""" def __init__(self, contexts, exc): Exception.__init__(self, exc) self.contexts = contexts self.exc = exc def __str__(self): backtrace = map(lambda x: str(x), self.contexts) return "%s: %s (%s)" % (self.exc.__class__, self.exc, \ (string.join(backtrace, ', '))) class Subsystem: """The subsystem class defers file creation so that it can create Unicode-wrapped files if desired (and possible).""" def __init__(self): self.useUnicode = False self.inputEncoding = None self.outputEncoding = None self.errors = None def initialize(self, inputEncoding=None, outputEncoding=None, \ inputErrors=None, outputErrors=None): self.useUnicode = True try: unicode import codecs except (NameError, ImportError): raise SubsystemError, "Unicode subsystem unavailable" defaultEncoding = sys.getdefaultencoding() if inputEncoding is None: inputEncoding = defaultEncoding self.inputEncoding = inputEncoding if outputEncoding is None: outputEncoding = defaultEncoding self.outputEncoding = outputEncoding if inputErrors is None: inputErrors = DEFAULT_ERRORS self.inputErrors = inputErrors if outputErrors is None: outputErrors = DEFAULT_ERRORS self.outputErrors = outputErrors def assertUnicode(self): if not self.useUnicode: raise SubsystemError, "Unicode subsystem unavailable" def open(self, name, mode=None): if self.useUnicode: return self.unicodeOpen(name, mode) else: return self.defaultOpen(name, mode) def defaultOpen(self, name, mode=None): if mode is None: mode = 'r' return open(name, mode) def unicodeOpen(self, name, mode=None): import codecs if mode is None: mode = 'rb' if mode.find('w') >= 0 or mode.find('a') >= 0: encoding = self.outputEncoding errors = self.outputErrors else: encoding = self.inputEncoding errors = self.inputErrors return codecs.open(name, mode, encoding, errors) theSubsystem = Subsystem() class Stack: """A simple stack that behaves as a sequence (with 0 being the top of the stack, not the bottom).""" def __init__(self, seq=None): if seq is None: seq = [] self.data = seq def top(self): """Access the top element on the stack.""" try: return self.data[-1] except IndexError: raise StackUnderflowError, "stack is empty for top" def pop(self): """Pop the top element off the stack and return it.""" try: return self.data.pop() except IndexError: raise StackUnderflowError, "stack is empty for pop" def push(self, object): """Push an element onto the top of the stack.""" self.data.append(object) def filter(self, function): """Filter the elements of the stack through the function.""" self.data = filter(function, self.data) def purge(self): """Purge the stack.""" self.data = [] def clone(self): """Create a duplicate of this stack.""" return self.__class__(self.data[:]) def __nonzero__(self): return len(self.data) != 0 def __len__(self): return len(self.data) def __getitem__(self, index): return self.data[-(index + 1)] def __repr__(self): return '<%s instance at 0x%x [%s]>' % \ (self.__class__, id(self), \ string.join(map(repr, self.data), ', ')) class AbstractFile: """An abstracted file that, when buffered, will totally buffer the file, including even the file open.""" def __init__(self, filename, mode='w', buffered=False): # The calls below might throw, so start off by marking this # file as "done." This way destruction of a not-completely- # initialized AbstractFile will generate no further errors. self.done = True self.filename = filename self.mode = mode self.buffered = buffered if buffered: self.bufferFile = StringIO.StringIO() else: self.bufferFile = theSubsystem.open(filename, mode) # Okay, we got this far, so the AbstractFile is initialized. # Flag it as "not done." self.done = False def __del__(self): self.close() def write(self, data): self.bufferFile.write(data) def writelines(self, data): self.bufferFile.writelines(data) def flush(self): self.bufferFile.flush() def close(self): if not self.done: self.commit() self.done = True def commit(self): if self.buffered: file = theSubsystem.open(self.filename, self.mode) file.write(self.bufferFile.getvalue()) file.close() else: self.bufferFile.close() def abort(self): if self.buffered: self.bufferFile = None else: self.bufferFile.close() self.bufferFile = None self.done = True class Diversion: """The representation of an active diversion. Diversions act as (writable) file objects, and then can be recalled either as pure strings or (readable) file objects.""" def __init__(self): self.file = StringIO.StringIO() # These methods define the writable file-like interface for the # diversion. def write(self, data): self.file.write(data) def writelines(self, lines): for line in lines: self.write(line) def flush(self): self.file.flush() def close(self): self.file.close() # These methods are specific to diversions. def asString(self): """Return the diversion as a string.""" return self.file.getvalue() def asFile(self): """Return the diversion as a file.""" return StringIO.StringIO(self.file.getvalue()) class Stream: """A wrapper around an (output) file object which supports diversions and filtering.""" def __init__(self, file): self.file = file self.currentDiversion = None self.diversions = {} self.filter = file self.done = False def write(self, data): if self.currentDiversion is None: self.filter.write(data) else: self.diversions[self.currentDiversion].write(data) def writelines(self, lines): for line in lines: self.write(line) def flush(self): self.filter.flush() def close(self): if not self.done: self.undivertAll(True) self.filter.close() self.done = True def shortcut(self, shortcut): """Take a filter shortcut and translate it into a filter, returning it. Sequences don't count here; these should be detected independently.""" if shortcut == 0: return NullFilter() elif type(shortcut) is types.FunctionType or \ type(shortcut) is types.BuiltinFunctionType or \ type(shortcut) is types.BuiltinMethodType or \ type(shortcut) is types.LambdaType: return FunctionFilter(shortcut) elif type(shortcut) is types.StringType: return StringFilter(filter) elif type(shortcut) is types.DictType: raise NotImplementedError, "mapping filters not yet supported" else: # Presume it's a plain old filter. return shortcut def last(self): """Find the last filter in the current filter chain, or None if there are no filters installed.""" if self.filter is None: return None thisFilter, lastFilter = self.filter, None while thisFilter is not None and thisFilter is not self.file: lastFilter = thisFilter thisFilter = thisFilter.next() return lastFilter def install(self, shortcut=None): """Install a new filter; None means no filter. Handle all the special shortcuts for filters here.""" # Before starting, execute a flush. self.filter.flush() if shortcut is None or shortcut == [] or shortcut == (): # Shortcuts for "no filter." self.filter = self.file else: if type(shortcut) in (types.ListType, types.TupleType): shortcuts = list(shortcut) else: shortcuts = [shortcut] # Run through the shortcut filter names, replacing them with # full-fledged instances of Filter. filters = [] for shortcut in shortcuts: filters.append(self.shortcut(shortcut)) if len(filters) > 1: # If there's more than one filter provided, chain them # together. lastFilter = None for filter in filters: if lastFilter is not None: lastFilter.attach(filter) lastFilter = filter lastFilter.attach(self.file) self.filter = filters[0] else: # If there's only one filter, assume that it's alone or it's # part of a chain that has already been manually chained; # just find the end. filter = filters[0] lastFilter = filter.last() lastFilter.attach(self.file) self.filter = filter def attach(self, shortcut): """Attached a solitary filter (no sequences allowed here) at the end of the current filter chain.""" lastFilter = self.last() if lastFilter is None: # Just install it from scratch if there is no active filter. self.install(shortcut) else: # Attach the last filter to this one, and this one to the file. filter = self.shortcut(shortcut) lastFilter.attach(filter) filter.attach(self.file) def revert(self): """Reset any current diversions.""" self.currentDiversion = None def create(self, name): """Create a diversion if one does not already exist, but do not divert to it yet.""" if name is None: raise DiversionError, "diversion name must be non-None" if not self.diversions.has_key(name): self.diversions[name] = Diversion() def retrieve(self, name): """Retrieve the given diversion.""" if name is None: raise DiversionError, "diversion name must be non-None" if self.diversions.has_key(name): return self.diversions[name] else: raise DiversionError, "nonexistent diversion: %s" % name def divert(self, name): """Start diverting.""" if name is None: raise DiversionError, "diversion name must be non-None" self.create(name) self.currentDiversion = name def undivert(self, name, purgeAfterwards=False): """Undivert a particular diversion.""" if name is None: raise DiversionError, "diversion name must be non-None" if self.diversions.has_key(name): diversion = self.diversions[name] self.filter.write(diversion.asString()) if purgeAfterwards: self.purge(name) else: raise DiversionError, "nonexistent diversion: %s" % name def purge(self, name): """Purge the specified diversion.""" if name is None: raise DiversionError, "diversion name must be non-None" if self.diversions.has_key(name): del self.diversions[name] if self.currentDiversion == name: self.currentDiversion = None def undivertAll(self, purgeAfterwards=True): """Undivert all pending diversions.""" if self.diversions: self.revert() # revert before undiverting! names = self.diversions.keys() names.sort() for name in names: self.undivert(name) if purgeAfterwards: self.purge(name) def purgeAll(self): """Eliminate all existing diversions.""" if self.diversions: self.diversions = {} self.currentDiversion = None class NullFile: """A simple class that supports all the file-like object methods but simply does nothing at all.""" def __init__(self): pass def write(self, data): pass def writelines(self, lines): pass def flush(self): pass def close(self): pass class UncloseableFile: """A simple class which wraps around a delegate file-like object and lets everything through except close calls.""" def __init__(self, delegate): self.delegate = delegate def write(self, data): self.delegate.write(data) def writelines(self, lines): self.delegate.writelines(data) def flush(self): self.delegate.flush() def close(self): """Eat this one.""" pass class ProxyFile: """The proxy file object that is intended to take the place of sys.stdout. The proxy can manage a stack of file objects it is writing to, and an underlying raw file object.""" def __init__(self, bottom): self.stack = Stack() self.bottom = bottom def current(self): """Get the current stream to write to.""" if self.stack: return self.stack[-1][1] else: return self.bottom def push(self, interpreter): self.stack.push((interpreter, interpreter.stream())) def pop(self, interpreter): result = self.stack.pop() assert interpreter is result[0] def clear(self, interpreter): self.stack.filter(lambda x, i=interpreter: x[0] is not i) def write(self, data): self.current().write(data) def writelines(self, lines): self.current().writelines(lines) def flush(self): self.current().flush() def close(self): """Close the current file. If the current file is the bottom, then close it and dispose of it.""" current = self.current() if current is self.bottom: self.bottom = None current.close() def _testProxy(self): pass class Filter: """An abstract filter.""" def __init__(self): if self.__class__ is Filter: raise NotImplementedError self.sink = None def next(self): """Return the next filter/file-like object in the sequence, or None.""" return self.sink def write(self, data): """The standard write method; this must be overridden in subclasses.""" raise NotImplementedError def writelines(self, lines): """Standard writelines wrapper.""" for line in lines: self.write(line) def _flush(self): """The _flush method should always flush the sink and should not be overridden.""" self.sink.flush() def flush(self): """The flush method can be overridden.""" self._flush() def close(self): """Close the filter. Do an explicit flush first, then close the sink.""" self.flush() self.sink.close() def attach(self, filter): """Attach a filter to this one.""" if self.sink is not None: # If it's already attached, detach it first. self.detach() self.sink = filter def detach(self): """Detach a filter from its sink.""" self.flush() self._flush() # do a guaranteed flush to just to be safe self.sink = None def last(self): """Find the last filter in this chain.""" this, last = self, self while this is not None: last = this this = this.next() return last class NullFilter(Filter): """A filter that never sends any output to its sink.""" def write(self, data): pass class FunctionFilter(Filter): """A filter that works simply by pumping its input through a function which maps strings into strings.""" def __init__(self, function): Filter.__init__(self) self.function = function def write(self, data): self.sink.write(self.function(data)) class StringFilter(Filter): """A filter that takes a translation string (256 characters) and filters any incoming data through it.""" def __init__(self, table): if not (type(table) == types.StringType and len(table) == 256): raise FilterError, "table must be 256-character string" Filter.__init__(self) self.table = table def write(self, data): self.sink.write(string.translate(data, self.table)) class BufferedFilter(Filter): """A buffered filter is one that doesn't modify the source data sent to the sink, but instead holds it for a time. The standard variety only sends the data along when it receives a flush command.""" def __init__(self): Filter.__init__(self) self.buffer = '' def write(self, data): self.buffer = self.buffer + data def flush(self): if self.buffer: self.sink.write(self.buffer) self._flush() class SizeBufferedFilter(BufferedFilter): """A size-buffered filter only in fixed size chunks (excepting the final chunk).""" def __init__(self, bufferSize): BufferedFilter.__init__(self) self.bufferSize = bufferSize def write(self, data): BufferedFilter.write(self, data) while len(self.buffer) > self.bufferSize: chunk, self.buffer = \ self.buffer[:self.bufferSize], self.buffer[self.bufferSize:] self.sink.write(chunk) class LineBufferedFilter(BufferedFilter): """A line-buffered filter only lets data through when it sees whole lines.""" def __init__(self): BufferedFilter.__init__(self) def write(self, data): BufferedFilter.write(self, data) chunks = string.split(self.buffer, '\n') for chunk in chunks[:-1]: self.sink.write(chunk + '\n') self.buffer = chunks[-1] class MaximallyBufferedFilter(BufferedFilter): """A maximally-buffered filter only lets its data through on the final close. It ignores flushes.""" def __init__(self): BufferedFilter.__init__(self) def flush(self): pass def close(self): if self.buffer: BufferedFilter.flush(self) self.sink.close() class Context: """An interpreter context, which encapsulates a name, an input file object, and a parser object.""" DEFAULT_UNIT = 'lines' def __init__(self, name, line=0, units=DEFAULT_UNIT): self.name = name self.line = line self.units = units self.pause = False def bump(self, quantity=1): if self.pause: self.pause = False else: self.line = self.line + quantity def identify(self): return self.name, self.line def __str__(self): if self.units == self.DEFAULT_UNIT: return "%s:%s" % (self.name, self.line) else: return "%s:%s[%s]" % (self.name, self.line, self.units) class Hook: """The base class for implementing hooks.""" def __init__(self): self.interpreter = None def register(self, interpreter): self.interpreter = interpreter def deregister(self, interpreter): if interpreter is not self.interpreter: raise Error, "hook not associated with this interpreter" self.interpreter = None def push(self): self.interpreter.push() def pop(self): self.interpreter.pop() def null(self): pass def atStartup(self): pass def atReady(self): pass def atFinalize(self): pass def atShutdown(self): pass def atParse(self, scanner, locals): pass def atToken(self, token): pass def atHandle(self, meta): pass def atInteract(self): pass def beforeInclude(self, name, file, locals): pass def afterInclude(self): pass def beforeExpand(self, string, locals): pass def afterExpand(self, result): pass def beforeFile(self, name, file, locals): pass def afterFile(self): pass def beforeBinary(self, name, file, chunkSize, locals): pass def afterBinary(self): pass def beforeString(self, name, string, locals): pass def afterString(self): pass def beforeQuote(self, string): pass def afterQuote(self, result): pass def beforeEscape(self, string, more): pass def afterEscape(self, result): pass def beforeControl(self, type, rest, locals): pass def afterControl(self): pass def beforeSignificate(self, key, value, locals): pass def afterSignificate(self): pass def beforeAtomic(self, name, value, locals): pass def afterAtomic(self): pass def beforeMulti(self, name, values, locals): pass def afterMulti(self): pass def beforeImport(self, name, locals): pass def afterImport(self): pass def beforeClause(self, catch, locals): pass def afterClause(self, exception, variable): pass def beforeSerialize(self, expression, locals): pass def afterSerialize(self): pass def beforeDefined(self, name, locals): pass def afterDefined(self, result): pass def beforeLiteral(self, text): pass def afterLiteral(self): pass def beforeEvaluate(self, expression, locals): pass def afterEvaluate(self, result): pass def beforeExecute(self, statements, locals): pass def afterExecute(self): pass def beforeSingle(self, source, locals): pass def afterSingle(self): pass class VerboseHook(Hook): """A verbose hook that reports all information received by the hook interface. This class dynamically scans the Hook base class to ensure that all hook methods are properly represented.""" EXEMPT_ATTRIBUTES = ['register', 'deregister', 'push', 'pop'] def __init__(self, output=sys.stderr): Hook.__init__(self) self.output = output self.indent = 0 class FakeMethod: """This is a proxy method-like object.""" def __init__(self, hook, name): self.hook = hook self.name = name def __call__(self, **keywords): self.hook.output.write("%s%s: %s\n" % \ (' ' * self.hook.indent, \ self.name, repr(keywords))) for attribute in dir(Hook): if attribute[:1] != '_' and \ attribute not in self.EXEMPT_ATTRIBUTES: self.__dict__[attribute] = FakeMethod(self, attribute) class Token: """An element of expansion.""" def run(self, interpreter, locals): raise NotImplementedError def string(self): raise NotImplementedError def __str__(self): return self.string() class NullToken(Token): """A chunk of data not containing markups.""" def __init__(self, data): self.data = data def run(self, interpreter, locals): interpreter.write(self.data) def string(self): return self.data class ExpansionToken(Token): """A token that involves an expansion.""" def __init__(self, prefix, first): self.prefix = prefix self.first = first def scan(self, scanner): pass def run(self, interpreter, locals): pass class WhitespaceToken(ExpansionToken): """A whitespace markup.""" def string(self): return '%s%s' % (self.prefix, self.first) class LiteralToken(ExpansionToken): """A literal markup.""" def run(self, interpreter, locals): interpreter.write(self.first) def string(self): return '%s%s' % (self.prefix, self.first) class PrefixToken(ExpansionToken): """A prefix markup.""" def run(self, interpreter, locals): interpreter.write(interpreter.prefix) def string(self): return self.prefix * 2 class CommentToken(ExpansionToken): """A comment markup.""" def scan(self, scanner): loc = scanner.find('\n') if loc >= 0: self.comment = scanner.chop(loc, 1) else: raise TransientParseError, "comment expects newline" def string(self): return '%s#%s\n' % (self.prefix, self.comment) class ContextNameToken(ExpansionToken): """A context name change markup.""" def scan(self, scanner): loc = scanner.find('\n') if loc >= 0: self.name = string.strip(scanner.chop(loc, 1)) else: raise TransientParseError, "context name expects newline" def run(self, interpreter, locals): context = interpreter.context() context.name = self.name class ContextLineToken(ExpansionToken): """A context line change markup.""" def scan(self, scanner): loc = scanner.find('\n') if loc >= 0: try: self.line = int(scanner.chop(loc, 1)) except ValueError: raise ParseError, "context line requires integer" else: raise TransientParseError, "context line expects newline" def run(self, interpreter, locals): context = interpreter.context() context.line = self.line context.pause = True class EscapeToken(ExpansionToken): """An escape markup.""" def scan(self, scanner): try: code = scanner.chop(1) result = None if code in '()[]{}\'\"\\': # literals result = code elif code == '0': # NUL result = '\x00' elif code == 'a': # BEL result = '\x07' elif code == 'b': # BS result = '\x08' elif code == 'd': # decimal code decimalCode = scanner.chop(3) result = chr(string.atoi(decimalCode, 10)) elif code == 'e': # ESC result = '\x1b' elif code == 'f': # FF result = '\x0c' elif code == 'h': # DEL result = '\x7f' elif code == 'n': # LF (newline) result = '\x0a' elif code == 'N': # Unicode character name theSubsystem.assertUnicode() import unicodedata if scanner.chop(1) != '{': raise ParseError, r"Unicode name escape should be \N{...}" i = scanner.find('}') name = scanner.chop(i, 1) try: result = unicodedata.lookup(name) except KeyError: raise SubsystemError, \ "unknown Unicode character name: %s" % name elif code == 'o': # octal code octalCode = scanner.chop(3) result = chr(string.atoi(octalCode, 8)) elif code == 'q': # quaternary code quaternaryCode = scanner.chop(4) result = chr(string.atoi(quaternaryCode, 4)) elif code == 'r': # CR result = '\x0d' elif code in 's ': # SP result = ' ' elif code == 't': # HT result = '\x09' elif code in 'u': # Unicode 16-bit hex literal theSubsystem.assertUnicode() hexCode = scanner.chop(4) result = unichr(string.atoi(hexCode, 16)) elif code in 'U': # Unicode 32-bit hex literal theSubsystem.assertUnicode() hexCode = scanner.chop(8) result = unichr(string.atoi(hexCode, 16)) elif code == 'v': # VT result = '\x0b' elif code == 'x': # hexadecimal code hexCode = scanner.chop(2) result = chr(string.atoi(hexCode, 16)) elif code == 'z': # EOT result = '\x04' elif code == '^': # control character controlCode = string.upper(scanner.chop(1)) if controlCode >= '@' and controlCode <= '`': result = chr(ord(controlCode) - ord('@')) elif controlCode == '?': result = '\x7f' else: raise ParseError, "invalid escape control code" else: raise ParseError, "unrecognized escape code" assert result is not None self.code = result except ValueError: raise ParseError, "invalid numeric escape code" def run(self, interpreter, locals): interpreter.write(self.code) def string(self): return '%s\\x%02x' % (self.prefix, ord(self.code)) class SignificatorToken(ExpansionToken): """A significator markup.""" def scan(self, scanner): loc = scanner.find('\n') if loc >= 0: line = scanner.chop(loc, 1) if not line: raise ParseError, "significator must have nonblank key" if line[0] in ' \t\v\n': raise ParseError, "no whitespace between % and key" # Work around a subtle CPython-Jython difference by stripping # the string before splitting it: 'a '.split(None, 1) has two # elements in Jython 2.1). fields = string.split(string.strip(line), None, 1) if len(fields) == 2 and fields[1] == '': fields.pop() self.key = fields[0] if len(fields) < 2: fields.append(None) self.key, self.valueCode = fields else: raise TransientParseError, "significator expects newline" def run(self, interpreter, locals): value = self.valueCode if value is not None: value = interpreter.evaluate(string.strip(value), locals) interpreter.significate(self.key, value) def string(self): if self.valueCode is None: return '%s%%%s\n' % (self.prefix, self.key) else: return '%s%%%s %s\n' % (self.prefix, self.key, self.valueCode) class ExpressionToken(ExpansionToken): """An expression markup.""" def scan(self, scanner): z = scanner.complex('(', ')', 0) try: q = scanner.next('$', 0, z, True) except ParseError: q = z try: i = scanner.next('?', 0, q, True) try: j = scanner.next('!', i, q, True) except ParseError: try: j = scanner.next(':', i, q, True) # DEPRECATED except ParseError: j = q except ParseError: i = j = q code = scanner.chop(z, 1) self.testCode = code[:i] self.thenCode = code[i + 1:j] self.elseCode = code[j + 1:q] self.exceptCode = code[q + 1:z] def run(self, interpreter, locals): try: result = interpreter.evaluate(self.testCode, locals) if self.thenCode: if result: result = interpreter.evaluate(self.thenCode, locals) else: if self.elseCode: result = interpreter.evaluate(self.elseCode, locals) else: result = None except SyntaxError: # Don't catch syntax errors; let them through. raise except: if self.exceptCode: result = interpreter.evaluate(self.exceptCode, locals) else: raise if result is not None: interpreter.write(str(result)) def string(self): result = self.testCode if self.thenCode: result = result + '?' + self.thenCode if self.elseCode: result = result + '!' + self.elseCode if self.exceptCode: result = result + '$' + self.exceptCode return '%s(%s)' % (self.prefix, result) class StringLiteralToken(ExpansionToken): """A string token markup.""" def scan(self, scanner): scanner.retreat() assert scanner[0] == self.first i = scanner.quote() self.literal = scanner.chop(i) def run(self, interpreter, locals): interpreter.literal(self.literal) def string(self): return '%s%s' % (self.prefix, self.literal) class SimpleExpressionToken(ExpansionToken): """A simple expression markup.""" def scan(self, scanner): i = scanner.simple() self.code = self.first + scanner.chop(i) def run(self, interpreter, locals): interpreter.serialize(self.code, locals) def string(self): return '%s%s' % (self.prefix, self.code) class ReprToken(ExpansionToken): """A repr markup.""" def scan(self, scanner): i = scanner.next('`', 0) self.code = scanner.chop(i, 1) def run(self, interpreter, locals): interpreter.write(repr(interpreter.evaluate(self.code, locals))) def string(self): return '%s`%s`' % (self.prefix, self.code) class InPlaceToken(ExpansionToken): """An in-place markup.""" def scan(self, scanner): i = scanner.next(':', 0) j = scanner.next(':', i + 1) self.code = scanner.chop(i, j - i + 1) def run(self, interpreter, locals): interpreter.write("%s:%s:" % (interpreter.prefix, self.code)) try: interpreter.serialize(self.code, locals) finally: interpreter.write(":") def string(self): return '%s:%s::' % (self.prefix, self.code) class StatementToken(ExpansionToken): """A statement markup.""" def scan(self, scanner): i = scanner.complex('{', '}', 0) self.code = scanner.chop(i, 1) def run(self, interpreter, locals): interpreter.execute(self.code, locals) def string(self): return '%s{%s}' % (self.prefix, self.code) class CustomToken(ExpansionToken): """A custom markup.""" def scan(self, scanner): i = scanner.complex('<', '>', 0) self.contents = scanner.chop(i, 1) def run(self, interpreter, locals): interpreter.invokeCallback(self.contents) def string(self): return '%s<%s>' % (self.prefix, self.contents) class ControlToken(ExpansionToken): """A control token.""" PRIMARY_TYPES = ['if', 'for', 'while', 'try', 'def'] SECONDARY_TYPES = ['elif', 'else', 'except', 'finally'] TERTIARY_TYPES = ['continue', 'break'] GREEDY_TYPES = ['if', 'elif', 'for', 'while', 'def', 'end'] END_TYPES = ['end'] IN_RE = re.compile(r"\bin\b") def scan(self, scanner): scanner.acquire() i = scanner.complex('[', ']', 0) self.contents = scanner.chop(i, 1) fields = string.split(string.strip(self.contents), ' ', 1) if len(fields) > 1: self.type, self.rest = fields else: self.type = fields[0] self.rest = None self.subtokens = [] if self.type in self.GREEDY_TYPES and self.rest is None: raise ParseError, "control '%s' needs arguments" % self.type if self.type in self.PRIMARY_TYPES: self.subscan(scanner, self.type) self.kind = 'primary' elif self.type in self.SECONDARY_TYPES: self.kind = 'secondary' elif self.type in self.TERTIARY_TYPES: self.kind = 'tertiary' elif self.type in self.END_TYPES: self.kind = 'end' else: raise ParseError, "unknown control markup: '%s'" % self.type scanner.release() def subscan(self, scanner, primary): """Do a subscan for contained tokens.""" while True: token = scanner.one() if token is None: raise TransientParseError, \ "control '%s' needs more tokens" % primary if isinstance(token, ControlToken) and \ token.type in self.END_TYPES: if token.rest != primary: raise ParseError, \ "control must end with 'end %s'" % primary break self.subtokens.append(token) def build(self, allowed=None): """Process the list of subtokens and divide it into a list of 2-tuples, consisting of the dividing tokens and the list of subtokens that follow them. If allowed is specified, it will represent the list of the only secondary markup types which are allowed.""" if allowed is None: allowed = SECONDARY_TYPES result = [] latest = [] result.append((self, latest)) for subtoken in self.subtokens: if isinstance(subtoken, ControlToken) and \ subtoken.kind == 'secondary': if subtoken.type not in allowed: raise ParseError, \ "control unexpected secondary: '%s'" % subtoken.type latest = [] result.append((subtoken, latest)) else: latest.append(subtoken) return result def run(self, interpreter, locals): interpreter.invoke('beforeControl', type=self.type, rest=self.rest, \ locals=locals) if self.type == 'if': info = self.build(['elif', 'else']) elseTokens = None if info[-1][0].type == 'else': elseTokens = info.pop()[1] for secondary, subtokens in info: if secondary.type not in ('if', 'elif'): raise ParseError, \ "control 'if' unexpected secondary: '%s'" % secondary.type if interpreter.evaluate(secondary.rest, locals): self.subrun(subtokens, interpreter, locals) break else: if elseTokens: self.subrun(elseTokens, interpreter, locals) elif self.type == 'for': sides = self.IN_RE.split(self.rest, 1) if len(sides) != 2: raise ParseError, "control expected 'for x in seq'" iterator, sequenceCode = sides info = self.build(['else']) elseTokens = None if info[-1][0].type == 'else': elseTokens = info.pop()[1] if len(info) != 1: raise ParseError, "control 'for' expects at most one 'else'" sequence = interpreter.evaluate(sequenceCode, locals) for element in sequence: try: interpreter.assign(iterator, element, locals) self.subrun(info[0][1], interpreter, locals) except ContinueFlow: continue except BreakFlow: break else: if elseTokens: self.subrun(elseTokens, interpreter, locals) elif self.type == 'while': testCode = self.rest info = self.build(['else']) elseTokens = None if info[-1][0].type == 'else': elseTokens = info.pop()[1] if len(info) != 1: raise ParseError, "control 'while' expects at most one 'else'" atLeastOnce = False while True: try: if not interpreter.evaluate(testCode, locals): break atLeastOnce = True self.subrun(info[0][1], interpreter, locals) except ContinueFlow: continue except BreakFlow: break if not atLeastOnce and elseTokens: self.subrun(elseTokens, interpreter, locals) elif self.type == 'try': info = self.build(['except', 'finally']) if len(info) == 1: raise ParseError, "control 'try' needs 'except' or 'finally'" type = info[-1][0].type if type == 'except': for secondary, _tokens in info[1:]: if secondary.type != 'except': raise ParseError, \ "control 'try' cannot have 'except' and 'finally'" else: assert type == 'finally' if len(info) != 2: raise ParseError, \ "control 'try' can only have one 'finally'" if type == 'except': try: self.subrun(info[0][1], interpreter, locals) except FlowError: raise except Exception, e: for secondary, tokens in info[1:]: exception, variable = interpreter.clause(secondary.rest) if variable is not None: interpreter.assign(variable, e) if isinstance(e, exception): self.subrun(tokens, interpreter, locals) break else: raise else: try: self.subrun(info[0][1], interpreter, locals) finally: self.subrun(info[1][1], interpreter, locals) elif self.type == 'continue': raise ContinueFlow, "control 'continue' without 'for', 'while'" elif self.type == 'break': raise BreakFlow, "control 'break' without 'for', 'while'" elif self.type == 'def': signature = self.rest definition = self.substring() code = 'def %s:\n' \ ' r"""%s"""\n' \ ' return %s.expand(r"""%s""", locals())\n' % \ (signature, definition, interpreter.pseudo, definition) interpreter.execute(code, locals) elif self.type == 'end': raise ParseError, "control 'end' requires primary markup" else: raise ParseError, \ "control '%s' cannot be at this level" % self.type interpreter.invoke('afterControl') def subrun(self, tokens, interpreter, locals): """Execute a sequence of tokens.""" for token in tokens: token.run(interpreter, locals) def substring(self): return string.join(map(str, self.subtokens), '') def string(self): if self.kind == 'primary': return '%s[%s]%s%s[end %s]' % \ (self.prefix, self.contents, self.substring(), \ self.prefix, self.type) else: return '%s[%s]' % (self.prefix, self.contents) class Scanner: """A scanner holds a buffer for lookahead parsing and has the ability to scan for special symbols and indicators in that buffer.""" # This is the token mapping table that maps first characters to # token classes. TOKEN_MAP = [ (None, PrefixToken), (' \t\v\r\n', WhitespaceToken), (')]}', LiteralToken), ('\\', EscapeToken), ('#', CommentToken), ('?', ContextNameToken), ('!', ContextLineToken), ('%', SignificatorToken), ('(', ExpressionToken), (IDENTIFIER_FIRST_CHARS, SimpleExpressionToken), ('\'\"', StringLiteralToken), ('`', ReprToken), (':', InPlaceToken), ('[', ControlToken), ('{', StatementToken), ('<', CustomToken), ] def __init__(self, prefix, data=''): self.prefix = prefix self.pointer = 0 self.buffer = data self.lock = 0 def __nonzero__(self): return self.pointer < len(self.buffer) def __len__(self): return len(self.buffer) - self.pointer def __getitem__(self, index): return self.buffer[self.pointer + index] def __getslice__(self, start, stop): if stop > len(self): stop = len(self) return self.buffer[self.pointer + start:self.pointer + stop] def advance(self, count=1): """Advance the pointer count characters.""" self.pointer = self.pointer + count def retreat(self, count=1): self.pointer = self.pointer - count if self.pointer < 0: raise ParseError, "can't retreat back over synced out chars" def set(self, data): """Start the scanner digesting a new batch of data; start the pointer over from scratch.""" self.pointer = 0 self.buffer = data def feed(self, data): """Feed some more data to the scanner.""" self.buffer = self.buffer + data def chop(self, count=None, slop=0): """Chop the first count + slop characters off the front, and return the first count. If count is not specified, then return everything.""" if count is None: assert slop == 0 count = len(self) if count > len(self): raise TransientParseError, "not enough data to read" result = self[:count] self.advance(count + slop) return result def acquire(self): """Lock the scanner so it doesn't destroy data on sync.""" self.lock = self.lock + 1 def release(self): """Unlock the scanner.""" self.lock = self.lock - 1 def sync(self): """Sync up the buffer with the read head.""" if self.lock == 0 and self.pointer != 0: self.buffer = self.buffer[self.pointer:] self.pointer = 0 def unsync(self): """Undo changes; reset the read head.""" if self.pointer != 0: self.lock = 0 self.pointer = 0 def rest(self): """Get the remainder of the buffer.""" return self[:] def read(self, i=0, count=1): """Read count chars starting from i; raise a transient error if there aren't enough characters remaining.""" if len(self) < i + count: raise TransientParseError, "need more data to read" else: return self[i:i + count] def check(self, i, archetype=None): """Scan for the next single or triple quote, with the specified archetype. Return the found quote or None.""" quote = None if self[i] in '\'\"': quote = self[i] if len(self) - i < 3: for j in range(i, len(self)): if self[i] == quote: return quote else: raise TransientParseError, "need to scan for rest of quote" if self[i + 1] == self[i + 2] == quote: quote = quote * 3 if quote is not None: if archetype is None: return quote else: if archetype == quote: return quote elif len(archetype) < len(quote) and archetype[0] == quote[0]: return archetype else: return None else: return None def find(self, sub, start=0, end=None): """Find the next occurrence of the character, or return -1.""" if end is not None: return string.find(self.rest(), sub, start, end) else: return string.find(self.rest(), sub, start) def last(self, char, start=0, end=None): """Find the first character that is _not_ the specified character.""" if end is None: end = len(self) i = start while i < end: if self[i] != char: return i i = i + 1 else: raise TransientParseError, "expecting other than %s" % char def next(self, target, start=0, end=None, mandatory=False): """Scan for the next occurrence of one of the characters in the target string; optionally, make the scan mandatory.""" if mandatory: assert end is not None quote = None if end is None: end = len(self) i = start while i < end: newQuote = self.check(i, quote) if newQuote: if newQuote == quote: quote = None else: quote = newQuote i = i + len(newQuote) else: c = self[i] if quote: if c == '\\': i = i + 1 else: if c in target: return i i = i + 1 else: if mandatory: raise ParseError, "expecting %s, not found" % target else: raise TransientParseError, "expecting ending character" def quote(self, start=0, end=None, mandatory=False): """Scan for the end of the next quote.""" assert self[start] in '\'\"' quote = self.check(start) if end is None: end = len(self) i = start + len(quote) while i < end: newQuote = self.check(i, quote) if newQuote: i = i + len(newQuote) if newQuote == quote: return i else: c = self[i] if c == '\\': i = i + 1 i = i + 1 else: if mandatory: raise ParseError, "expecting end of string literal" else: raise TransientParseError, "expecting end of string literal" def nested(self, enter, exit, start=0, end=None): """Scan from i for an ending sequence, respecting entries and exits only.""" depth = 0 if end is None: end = len(self) i = start while i < end: c = self[i] if c == enter: depth = depth + 1 elif c == exit: depth = depth - 1 if depth < 0: return i i = i + 1 else: raise TransientParseError, "expecting end of complex expression" def complex(self, enter, exit, start=0, end=None, skip=None): """Scan from i for an ending sequence, respecting quotes, entries and exits.""" quote = None depth = 0 if end is None: end = len(self) last = None i = start while i < end: newQuote = self.check(i, quote) if newQuote: if newQuote == quote: quote = None else: quote = newQuote i = i + len(newQuote) else: c = self[i] if quote: if c == '\\': i = i + 1 else: if skip is None or last != skip: if c == enter: depth = depth + 1 elif c == exit: depth = depth - 1 if depth < 0: return i last = c i = i + 1 else: raise TransientParseError, "expecting end of complex expression" def word(self, start=0): """Scan from i for a simple word.""" length = len(self) i = start while i < length: if not self[i] in IDENTIFIER_CHARS: return i i = i + 1 else: raise TransientParseError, "expecting end of word" def phrase(self, start=0): """Scan from i for a phrase (e.g., 'word', 'f(a, b, c)', 'a[i]', or combinations like 'x[i](a)'.""" # Find the word. i = self.word(start) while i < len(self) and self[i] in '([{': enter = self[i] if enter == '{': raise ParseError, "curly braces can't open simple expressions" exit = ENDING_CHARS[enter] i = self.complex(enter, exit, i + 1) + 1 return i def simple(self, start=0): """Scan from i for a simple expression, which consists of one more phrases separated by dots.""" i = self.phrase(start) length = len(self) while i < length and self[i] == '.': i = self.phrase(i) # Make sure we don't end with a trailing dot. while i > 0 and self[i - 1] == '.': i = i - 1 return i def one(self): """Parse and return one token, or None if the scanner is empty.""" if not self: return None if not self.prefix: loc = -1 else: loc = self.find(self.prefix) if loc < 0: # If there's no prefix in the buffer, then set the location to # the end so the whole thing gets processed. loc = len(self) if loc == 0: # If there's a prefix at the beginning of the buffer, process # an expansion. prefix = self.chop(1) assert prefix == self.prefix first = self.chop(1) if first == self.prefix: first = None for firsts, factory in self.TOKEN_MAP: if firsts is None: if first is None: break elif first in firsts: break else: raise ParseError, "unknown markup: %s%s" % (self.prefix, first) token = factory(self.prefix, first) try: token.scan(self) except TransientParseError: # If a transient parse error occurs, reset the buffer pointer # so we can (conceivably) try again later. self.unsync() raise else: # Process everything up to loc as a null token. data = self.chop(loc) token = NullToken(data) self.sync() return token class Interpreter: """An interpreter can process chunks of EmPy code.""" # Constants. VERSION = __version__ SIGNIFICATOR_RE_SUFFIX = SIGNIFICATOR_RE_SUFFIX SIGNIFICATOR_RE_STRING = None # Types. Interpreter = None # define this below to prevent a circular reference Hook = Hook # DEPRECATED Filter = Filter # DEPRECATED NullFilter = NullFilter # DEPRECATED FunctionFilter = FunctionFilter # DEPRECATED StringFilter = StringFilter # DEPRECATED BufferedFilter = BufferedFilter # DEPRECATED SizeBufferedFilter = SizeBufferedFilter # DEPRECATED LineBufferedFilter = LineBufferedFilter # DEPRECATED MaximallyBufferedFilter = MaximallyBufferedFilter # DEPRECATED # Tables. ESCAPE_CODES = {0x00: '0', 0x07: 'a', 0x08: 'b', 0x1b: 'e', 0x0c: 'f', \ 0x7f: 'h', 0x0a: 'n', 0x0d: 'r', 0x09: 't', 0x0b: 'v', \ 0x04: 'z'} ASSIGN_TOKEN_RE = re.compile(r"[_a-zA-Z][_a-zA-Z0-9]*|\(|\)|,") DEFAULT_OPTIONS = {BANGPATH_OPT: True, BUFFERED_OPT: False, RAW_OPT: False, EXIT_OPT: True, FLATTEN_OPT: False, OVERRIDE_OPT: True, CALLBACK_OPT: False} _wasProxyInstalled = False # was a proxy installed? # Construction, initialization, destruction. def __init__(self, output=None, argv=None, prefix=DEFAULT_PREFIX, \ pseudo=None, options=None, globals=None, hooks=None): self.interpreter = self # DEPRECATED # Set up the stream. if output is None: output = UncloseableFile(sys.__stdout__) self.output = output self.prefix = prefix if pseudo is None: pseudo = DEFAULT_PSEUDOMODULE_NAME self.pseudo = pseudo if argv is None: argv = [DEFAULT_SCRIPT_NAME] self.argv = argv self.args = argv[1:] if options is None: options = {} self.options = options # Initialize any hooks. self.hooksEnabled = None # special sentinel meaning "false until added" self.hooks = [] if hooks is None: hooks = [] for hook in hooks: self.register(hook) # Initialize callback. self.callback = None # Finalizers. self.finals = [] # The interpreter stacks. self.contexts = Stack() self.streams = Stack() # Now set up the globals. self.globals = globals self.fix() self.history = Stack() # Install a proxy stdout if one hasn't been already. self.installProxy() # Finally, reset the state of all the stacks. self.reset() # Okay, now flatten the namespaces if that option has been set. if self.options.get(FLATTEN_OPT, False): self.flatten() # Set up old pseudomodule attributes. if prefix is None: self.SIGNIFICATOR_RE_STRING = None else: self.SIGNIFICATOR_RE_STRING = prefix + self.SIGNIFICATOR_RE_SUFFIX self.Interpreter = self.__class__ # Done. Now declare that we've started up. self.invoke('atStartup') def __del__(self): self.shutdown() def __repr__(self): return '<%s pseudomodule/interpreter at 0x%x>' % \ (self.pseudo, id(self)) def ready(self): """Declare the interpreter ready for normal operations.""" self.invoke('atReady') def fix(self): """Reset the globals, stamping in the pseudomodule.""" if self.globals is None: self.globals = {} # Make sure that there is no collision between two interpreters' # globals. if self.globals.has_key(self.pseudo): if self.globals[self.pseudo] is not self: raise Error, "interpreter globals collision" self.globals[self.pseudo] = self def unfix(self): """Remove the pseudomodule (if present) from the globals.""" UNWANTED_KEYS = [self.pseudo, '__builtins__'] for unwantedKey in UNWANTED_KEYS: if self.globals.has_key(unwantedKey): del self.globals[unwantedKey] def update(self, other): """Update the current globals dictionary with another dictionary.""" self.globals.update(other) self.fix() def clear(self): """Clear out the globals dictionary with a brand new one.""" self.globals = {} self.fix() def save(self, deep=True): if deep: copyMethod = copy.deepcopy else: copyMethod = copy.copy """Save a copy of the current globals on the history stack.""" self.unfix() self.history.push(copyMethod(self.globals)) self.fix() def restore(self, destructive=True): """Restore the topmost historic globals.""" if destructive: fetchMethod = self.history.pop else: fetchMethod = self.history.top self.unfix() self.globals = fetchMethod() self.fix() def shutdown(self): """Declare this interpreting session over; close the stream file object. This method is idempotent.""" if self.streams is not None: try: self.finalize() self.invoke('atShutdown') while self.streams: stream = self.streams.pop() stream.close() finally: self.streams = None def ok(self): """Is the interpreter still active?""" return self.streams is not None # Writeable file-like methods. def write(self, data): self.stream().write(data) def writelines(self, stuff): self.stream().writelines(stuff) def flush(self): self.stream().flush() def close(self): self.shutdown() # Stack-related activity. def context(self): return self.contexts.top() def stream(self): return self.streams.top() def reset(self): self.contexts.purge() self.streams.purge() self.streams.push(Stream(self.output)) if self.options.get(OVERRIDE_OPT, True): sys.stdout.clear(self) def push(self): if self.options.get(OVERRIDE_OPT, True): sys.stdout.push(self) def pop(self): if self.options.get(OVERRIDE_OPT, True): sys.stdout.pop(self) # Higher-level operations. def include(self, fileOrFilename, locals=None): """Do an include pass on a file or filename.""" if type(fileOrFilename) is types.StringType: # Either it's a string representing a filename ... filename = fileOrFilename name = filename file = theSubsystem.open(filename, 'r') else: # ... or a file object. file = fileOrFilename name = "<%s>" % str(file.__class__) self.invoke('beforeInclude', name=name, file=file, locals=locals) self.file(file, name, locals) self.invoke('afterInclude') def expand(self, data, locals=None): """Do an explicit expansion on a subordinate stream.""" outFile = StringIO.StringIO() stream = Stream(outFile) self.invoke('beforeExpand', string=data, locals=locals) self.streams.push(stream) try: self.string(data, '', locals) stream.flush() expansion = outFile.getvalue() self.invoke('afterExpand', result=expansion) return expansion finally: self.streams.pop() def quote(self, data): """Quote the given string so that if it were expanded it would evaluate to the original.""" self.invoke('beforeQuote', string=data) scanner = Scanner(self.prefix, data) result = [] i = 0 try: j = scanner.next(self.prefix, i) result.append(data[i:j]) result.append(self.prefix * 2) i = j + 1 except TransientParseError: pass result.append(data[i:]) result = string.join(result, '') self.invoke('afterQuote', result=result) return result def escape(self, data, more=''): """Escape a string so that nonprintable characters are replaced with compatible EmPy expansions.""" self.invoke('beforeEscape', string=data, more=more) result = [] for char in data: if char < ' ' or char > '~': charOrd = ord(char) if Interpreter.ESCAPE_CODES.has_key(charOrd): result.append(self.prefix + '\\' + \ Interpreter.ESCAPE_CODES[charOrd]) else: result.append(self.prefix + '\\x%02x' % charOrd) elif char in more: result.append(self.prefix + '\\' + char) else: result.append(char) result = string.join(result, '') self.invoke('afterEscape', result=result) return result # Processing. def wrap(self, callable, args): """Wrap around an application of a callable and handle errors. Return whether no error occurred.""" try: apply(callable, args) self.reset() return True except KeyboardInterrupt, e: # Handle keyboard interrupts specially: we should always exit # from these. self.fail(e, True) except Exception, e: # A standard exception (other than a keyboard interrupt). self.fail(e) except: # If we get here, then either it's an exception not derived from # Exception or it's a string exception, so get the error type # from the sys module. e = sys.exc_type self.fail(e) # An error occurred if we leak through to here, so do cleanup. self.reset() return False def interact(self): """Perform interaction.""" self.invoke('atInteract') done = False while not done: result = self.wrap(self.file, (sys.stdin, '')) if self.options.get(EXIT_OPT, True): done = True else: if result: done = True else: self.reset() def fail(self, error, fatal=False): """Handle an actual error that occurred.""" if self.options.get(BUFFERED_OPT, False): try: self.output.abort() except AttributeError: # If the output file object doesn't have an abort method, # something got mismatched, but it's too late to do # anything about it now anyway, so just ignore it. pass meta = self.meta(error) self.handle(meta) if self.options.get(RAW_OPT, False): raise if fatal or self.options.get(EXIT_OPT, True): sys.exit(FAILURE_CODE) def file(self, file, name='', locals=None): """Parse the entire contents of a file-like object, line by line.""" context = Context(name) self.contexts.push(context) self.invoke('beforeFile', name=name, file=file, locals=locals) scanner = Scanner(self.prefix) first = True done = False while not done: self.context().bump() line = file.readline() if first: if self.options.get(BANGPATH_OPT, True) and self.prefix: # Replace a bangpath at the beginning of the first line # with an EmPy comment. if string.find(line, BANGPATH) == 0: line = self.prefix + '#' + line[2:] first = False if line: scanner.feed(line) else: done = True self.safe(scanner, done, locals) self.invoke('afterFile') self.contexts.pop() def binary(self, file, name='', chunkSize=0, locals=None): """Parse the entire contents of a file-like object, in chunks.""" if chunkSize <= 0: chunkSize = DEFAULT_CHUNK_SIZE context = Context(name, units='bytes') self.contexts.push(context) self.invoke('beforeBinary', name=name, file=file, \ chunkSize=chunkSize, locals=locals) scanner = Scanner(self.prefix) done = False while not done: chunk = file.read(chunkSize) if chunk: scanner.feed(chunk) else: done = True self.safe(scanner, done, locals) self.context().bump(len(chunk)) self.invoke('afterBinary') self.contexts.pop() def string(self, data, name='', locals=None): """Parse a string.""" context = Context(name) self.contexts.push(context) self.invoke('beforeString', name=name, string=data, locals=locals) context.bump() scanner = Scanner(self.prefix, data) self.safe(scanner, True, locals) self.invoke('afterString') self.contexts.pop() def safe(self, scanner, final=False, locals=None): """Do a protected parse. Catch transient parse errors; if final is true, then make a final pass with a terminator, otherwise ignore the transient parse error (more data is pending).""" try: self.parse(scanner, locals) except TransientParseError: if final: # If the buffer doesn't end with a newline, try tacking on # a dummy terminator. buffer = scanner.rest() if buffer and buffer[-1] != '\n': scanner.feed(self.prefix + '\n') # A TransientParseError thrown from here is a real parse # error. self.parse(scanner, locals) def parse(self, scanner, locals=None): """Parse and run as much from this scanner as possible.""" self.invoke('atParse', scanner=scanner, locals=locals) while True: token = scanner.one() if token is None: break self.invoke('atToken', token=token) token.run(self, locals) # Medium-level evaluation and execution. def tokenize(self, name): """Take an lvalue string and return a name or a (possibly recursive) list of names.""" result = [] stack = [result] for garbage in self.ASSIGN_TOKEN_RE.split(name): garbage = string.strip(garbage) if garbage: raise ParseError, "unexpected assignment token: '%s'" % garbage tokens = self.ASSIGN_TOKEN_RE.findall(name) # While processing, put a None token at the start of any list in which # commas actually appear. for token in tokens: if token == '(': stack.append([]) elif token == ')': top = stack.pop() if len(top) == 1: top = top[0] # no None token means that it's not a 1-tuple elif top[0] is None: del top[0] # remove the None token for real tuples stack[-1].append(top) elif token == ',': if len(stack[-1]) == 1: stack[-1].insert(0, None) else: stack[-1].append(token) # If it's a 1-tuple at the top level, turn it into a real subsequence. if result and result[0] is None: result = [result[1:]] if len(result) == 1: return result[0] else: return result def significate(self, key, value=None, locals=None): """Declare a significator.""" self.invoke('beforeSignificate', key=key, value=value, locals=locals) name = '__%s__' % key self.atomic(name, value, locals) self.invoke('afterSignificate') def atomic(self, name, value, locals=None): """Do an atomic assignment.""" self.invoke('beforeAtomic', name=name, value=value, locals=locals) if locals is None: self.globals[name] = value else: locals[name] = value self.invoke('afterAtomic') def multi(self, names, values, locals=None): """Do a (potentially recursive) assignment.""" self.invoke('beforeMulti', names=names, values=values, locals=locals) # No zip in 1.5, so we have to do it manually. i = 0 try: values = tuple(values) except TypeError: raise TypeError, "unpack non-sequence" if len(names) != len(values): raise ValueError, "unpack tuple of wrong size" for i in range(len(names)): name = names[i] if type(name) is types.StringType: self.atomic(name, values[i], locals) else: self.multi(name, values[i], locals) self.invoke('afterMulti') def assign(self, name, value, locals=None): """Do a potentially complex (including tuple unpacking) assignment.""" left = self.tokenize(name) # The return value of tokenize can either be a string or a list of # (lists of) strings. if type(left) is types.StringType: self.atomic(left, value, locals) else: self.multi(left, value, locals) def import_(self, name, locals=None): """Do an import.""" self.invoke('beforeImport', name=name, locals=locals) self.execute('import %s' % name, locals) self.invoke('afterImport') def clause(self, catch, locals=None): """Given the string representation of an except clause, turn it into a 2-tuple consisting of the class name, and either a variable name or None.""" self.invoke('beforeClause', catch=catch, locals=locals) if catch is None: exceptionCode, variable = None, None elif string.find(catch, ',') >= 0: exceptionCode, variable = string.split(string.strip(catch), ',', 1) variable = string.strip(variable) else: exceptionCode, variable = string.strip(catch), None if not exceptionCode: exception = Exception else: exception = self.evaluate(exceptionCode, locals) self.invoke('afterClause', exception=exception, variable=variable) return exception, variable def serialize(self, expression, locals=None): """Do an expansion, involving evaluating an expression, then converting it to a string and writing that string to the output if the evaluation is not None.""" self.invoke('beforeSerialize', expression=expression, locals=locals) result = self.evaluate(expression, locals) if result is not None: self.write(str(result)) self.invoke('afterSerialize') def defined(self, name, locals=None): """Return a Boolean indicating whether or not the name is defined either in the locals or the globals.""" self.invoke('beforeDefined', name=name, local=local) if locals is not None: if locals.has_key(name): result = True else: result = False elif self.globals.has_key(name): result = True else: result = False self.invoke('afterDefined', result=result) def literal(self, text): """Process a string literal.""" self.invoke('beforeLiteral', text=text) self.serialize(text) self.invoke('afterLiteral') # Low-level evaluation and execution. def evaluate(self, expression, locals=None): """Evaluate an expression.""" if expression in ('1', 'True'): return True if expression in ('0', 'False'): return False self.push() try: self.invoke('beforeEvaluate', \ expression=expression, locals=locals) if locals is not None: result = eval(expression, self.globals, locals) else: result = eval(expression, self.globals) self.invoke('afterEvaluate', result=result) return result finally: self.pop() def execute(self, statements, locals=None): """Execute a statement.""" # If there are any carriage returns (as opposed to linefeeds/newlines) # in the statements code, then remove them. Even on DOS/Windows # platforms, if string.find(statements, '\r') >= 0: statements = string.replace(statements, '\r', '') # If there are no newlines in the statements code, then strip any # leading or trailing whitespace. if string.find(statements, '\n') < 0: statements = string.strip(statements) self.push() try: self.invoke('beforeExecute', \ statements=statements, locals=locals) if locals is not None: exec statements in self.globals, locals else: exec statements in self.globals self.invoke('afterExecute') finally: self.pop() def single(self, source, locals=None): """Execute an expression or statement, just as if it were entered into the Python interactive interpreter.""" self.push() try: self.invoke('beforeSingle', \ source=source, locals=locals) code = compile(source, '', 'single') if locals is not None: exec code in self.globals, locals else: exec code in self.globals self.invoke('afterSingle') finally: self.pop() # Hooks. def register(self, hook, prepend=False): """Register the provided hook.""" hook.register(self) if self.hooksEnabled is None: # A special optimization so that hooks can be effectively # disabled until one is added or they are explicitly turned on. self.hooksEnabled = True if prepend: self.hooks.insert(0, hook) else: self.hooks.append(hook) def deregister(self, hook): """Remove an already registered hook.""" hook.deregister(self) self.hooks.remove(hook) def invoke(self, _name, **keywords): """Invoke the hook(s) associated with the hook name, should they exist.""" if self.hooksEnabled: for hook in self.hooks: hook.push() try: method = getattr(hook, _name) apply(method, (), keywords) finally: hook.pop() def finalize(self): """Execute any remaining final routines.""" self.push() self.invoke('atFinalize') try: # Pop them off one at a time so they get executed in reverse # order and we remove them as they're executed in case something # bad happens. while self.finals: final = self.finals.pop() final() finally: self.pop() # Error handling. def meta(self, exc=None): """Construct a MetaError for the interpreter's current state.""" return MetaError(self.contexts.clone(), exc) def handle(self, meta): """Handle a MetaError.""" first = True self.invoke('atHandle', meta=meta) for context in meta.contexts: if first: if meta.exc is not None: desc = "error: %s: %s" % (meta.exc.__class__, meta.exc) else: desc = "error" else: desc = "from this context" first = False sys.stderr.write('%s: %s\n' % (context, desc)) def installProxy(self): """Install a proxy if necessary.""" # Unfortunately, there's no surefire way to make sure that installing # a sys.stdout proxy is idempotent, what with different interpreters # running from different modules. The best we can do here is to try # manipulating the proxy's test function ... try: sys.stdout._testProxy() except AttributeError: # ... if the current stdout object doesn't have one, then check # to see if we think _this_ particularly Interpreter class has # installed it before ... if Interpreter._wasProxyInstalled: # ... and if so, we have a proxy problem. raise Error, "interpreter stdout proxy lost" else: # Otherwise, install the proxy and set the flag. sys.stdout = ProxyFile(sys.stdout) Interpreter._wasProxyInstalled = True # # Pseudomodule routines. # # Identification. def identify(self): """Identify the topmost context with a 2-tuple of the name and line number.""" return self.context().identify() def atExit(self, callable): """Register a function to be called at exit.""" self.finals.append(callable) # Context manipulation. def pushContext(self, name='', line=0): """Create a new context and push it.""" self.contexts.push(Context(name, line)) def popContext(self): """Pop the top context.""" self.contexts.pop() def setContextName(self, name): """Set the name of the topmost context.""" context = self.context() context.name = name def setContextLine(self, line): """Set the name of the topmost context.""" context = self.context() context.line = line setName = setContextName # DEPRECATED setLine = setContextLine # DEPRECATED # Globals manipulation. def getGlobals(self): """Retrieve the globals.""" return self.globals def setGlobals(self, globals): """Set the globals to the specified dictionary.""" self.globals = globals self.fix() def updateGlobals(self, otherGlobals): """Merge another mapping object into this interpreter's globals.""" self.update(otherGlobals) def clearGlobals(self): """Clear out the globals with a brand new dictionary.""" self.clear() def saveGlobals(self, deep=True): """Save a copy of the globals off onto the history stack.""" self.save(deep) def restoreGlobals(self, destructive=True): """Restore the most recently saved copy of the globals.""" self.restore(destructive) # Hook support. def areHooksEnabled(self): """Return whether or not hooks are presently enabled.""" if self.hooksEnabled is None: return True else: return self.hooksEnabled def enableHooks(self): """Enable hooks.""" self.hooksEnabled = True def disableHooks(self): """Disable hooks.""" self.hooksEnabled = False def getHooks(self): """Get the current hooks.""" return self.hooks[:] def clearHooks(self): """Clear all hooks.""" self.hooks = [] def addHook(self, hook, prepend=False): """Add a new hook; optionally insert it rather than appending it.""" self.register(hook, prepend) def removeHook(self, hook): """Remove a preexisting hook.""" self.deregister(hook) def invokeHook(self, _name, **keywords): """Manually invoke a hook.""" apply(self.invoke, (_name,), keywords) # Callbacks. def getCallback(self): """Get the callback registered with this interpreter, or None.""" return self.callback def registerCallback(self, callback): """Register a custom markup callback with this interpreter.""" self.callback = callback def deregisterCallback(self): """Remove any previously registered callback with this interpreter.""" self.callback = None def invokeCallback(self, contents): """Invoke the callback.""" if self.callback is None: if self.options.get(CALLBACK_OPT, False): raise Error, "custom markup invoked with no defined callback" else: self.callback(contents) # Pseudomodule manipulation. def flatten(self, keys=None): """Flatten the contents of the pseudo-module into the globals namespace.""" if keys is None: keys = self.__dict__.keys() + self.__class__.__dict__.keys() dict = {} for key in keys: # The pseudomodule is really a class instance, so we need to # fumble use getattr instead of simply fumbling through the # instance's __dict__. dict[key] = getattr(self, key) # Stomp everything into the globals namespace. self.globals.update(dict) # Prefix. def getPrefix(self): """Get the current prefix.""" return self.prefix def setPrefix(self, prefix): """Set the prefix.""" self.prefix = prefix # Diversions. def stopDiverting(self): """Stop any diverting.""" self.stream().revert() def createDiversion(self, name): """Create a diversion (but do not divert to it) if it does not already exist.""" self.stream().create(name) def retrieveDiversion(self, name): """Retrieve the diversion object associated with the name.""" return self.stream().retrieve(name) def startDiversion(self, name): """Start diverting to the given diversion name.""" self.stream().divert(name) def playDiversion(self, name): """Play the given diversion and then purge it.""" self.stream().undivert(name, True) def replayDiversion(self, name): """Replay the diversion without purging it.""" self.stream().undivert(name, False) def purgeDiversion(self, name): """Eliminate the given diversion.""" self.stream().purge(name) def playAllDiversions(self): """Play all existing diversions and then purge them.""" self.stream().undivertAll(True) def replayAllDiversions(self): """Replay all existing diversions without purging them.""" self.stream().undivertAll(False) def purgeAllDiversions(self): """Purge all existing diversions.""" self.stream().purgeAll() def getCurrentDiversion(self): """Get the name of the current diversion.""" return self.stream().currentDiversion def getAllDiversions(self): """Get the names of all existing diversions.""" names = self.stream().diversions.keys() names.sort() return names # Filter. def resetFilter(self): """Reset the filter so that it does no filtering.""" self.stream().install(None) def nullFilter(self): """Install a filter that will consume all text.""" self.stream().install(0) def getFilter(self): """Get the current filter.""" filter = self.stream().filter if filter is self.stream().file: return None else: return filter def setFilter(self, shortcut): """Set the filter.""" self.stream().install(shortcut) def attachFilter(self, shortcut): """Attach a single filter to the end of the current filter chain.""" self.stream().attach(shortcut) class Document: """A representation of an individual EmPy document, as used by a processor.""" def __init__(self, ID, filename): self.ID = ID self.filename = filename self.significators = {} class Processor: """An entity which is capable of processing a hierarchy of EmPy files and building a dictionary of document objects associated with them describing their significator contents.""" DEFAULT_EMPY_EXTENSIONS = ('.em',) SIGNIFICATOR_RE = re.compile(SIGNIFICATOR_RE_STRING) def __init__(self, factory=Document): self.factory = factory self.documents = {} def identifier(self, pathname, filename): return filename def clear(self): self.documents = {} def scan(self, basename, extensions=DEFAULT_EMPY_EXTENSIONS): if type(extensions) is types.StringType: extensions = (extensions,) def _noCriteria(x): return True def _extensionsCriteria(pathname, extensions=extensions): if extensions: for extension in extensions: if pathname[-len(extension):] == extension: return True return False else: return True self.directory(basename, _noCriteria, _extensionsCriteria, None) self.postprocess() def postprocess(self): pass def directory(self, basename, dirCriteria, fileCriteria, depth=None): if depth is not None: if depth <= 0: return else: depth = depth - 1 filenames = os.listdir(basename) for filename in filenames: pathname = os.path.join(basename, filename) if os.path.isdir(pathname): if dirCriteria(pathname): self.directory(pathname, dirCriteria, fileCriteria, depth) elif os.path.isfile(pathname): if fileCriteria(pathname): documentID = self.identifier(pathname, filename) document = self.factory(documentID, pathname) self.file(document, open(pathname)) self.documents[documentID] = document def file(self, document, file): while True: line = file.readline() if not line: break self.line(document, line) def line(self, document, line): match = self.SIGNIFICATOR_RE.search(line) if match: key, valueS = match.groups() valueS = string.strip(valueS) if valueS: value = eval(valueS) else: value = None document.significators[key] = value def expand(_data, _globals=None, \ _argv=None, _prefix=DEFAULT_PREFIX, _pseudo=None, _options=None, \ **_locals): """Do an atomic expansion of the given source data, creating and shutting down an interpreter dedicated to the task. The sys.stdout object is saved off and then replaced before this function returns.""" if len(_locals) == 0: # If there were no keyword arguments specified, don't use a locals # dictionary at all. _locals = None output = NullFile() interpreter = Interpreter(output, argv=_argv, prefix=_prefix, \ pseudo=_pseudo, options=_options, \ globals=_globals) if interpreter.options.get(OVERRIDE_OPT, True): oldStdout = sys.stdout try: result = interpreter.expand(_data, _locals) finally: interpreter.shutdown() if _globals is not None: interpreter.unfix() # remove pseudomodule to prevent clashes if interpreter.options.get(OVERRIDE_OPT, True): sys.stdout = oldStdout return result def environment(name, default=None): """Get data from the current environment. If the default is True or False, then presume that we're only interested in the existence or non-existence of the environment variable.""" if os.environ.has_key(name): # Do the True/False test by value for future compatibility. if default == False or default == True: return True else: return os.environ[name] else: return default def info(table): DEFAULT_LEFT = 28 maxLeft = 0 maxRight = 0 for left, right in table: if len(left) > maxLeft: maxLeft = len(left) if len(right) > maxRight: maxRight = len(right) FORMAT = ' %%-%ds %%s\n' % max(maxLeft, DEFAULT_LEFT) for left, right in table: if right.find('\n') >= 0: for right in right.split('\n'): sys.stderr.write(FORMAT % (left, right)) left = '' else: sys.stderr.write(FORMAT % (left, right)) def usage(verbose=True): """Print usage information.""" programName = sys.argv[0] def warn(line=''): sys.stderr.write("%s\n" % line) warn("""\ Usage: %s [options] [ [...]] Welcome to EmPy version %s.""" % (programName, __version__)) warn() warn("Valid options:") info(OPTION_INFO) if verbose: warn() warn("The following markups are supported:") info(MARKUP_INFO) warn() warn("Valid escape sequences are:") info(ESCAPE_INFO) warn() warn("The %s pseudomodule contains the following attributes:" % \ DEFAULT_PSEUDOMODULE_NAME) info(PSEUDOMODULE_INFO) warn() warn("The following environment variables are recognized:") info(ENVIRONMENT_INFO) warn() warn(USAGE_NOTES) else: warn() warn("Type %s -H for more extensive help." % programName) def invoke(args): """Run a standalone instance of an EmPy interpeter.""" # Initialize the options. _output = None _options = {BUFFERED_OPT: environment(BUFFERED_ENV, False), RAW_OPT: environment(RAW_ENV, False), EXIT_OPT: True, FLATTEN_OPT: environment(FLATTEN_ENV, False), OVERRIDE_OPT: not environment(NO_OVERRIDE_ENV, False), CALLBACK_OPT: False} _preprocessing = [] _prefix = environment(PREFIX_ENV, DEFAULT_PREFIX) _pseudo = environment(PSEUDO_ENV, None) _interactive = environment(INTERACTIVE_ENV, False) _extraArguments = environment(OPTIONS_ENV) _binary = -1 # negative for not, 0 for default size, positive for size _unicode = environment(UNICODE_ENV, False) _unicodeInputEncoding = environment(INPUT_ENCODING_ENV, None) _unicodeOutputEncoding = environment(OUTPUT_ENCODING_ENV, None) _unicodeInputErrors = environment(INPUT_ERRORS_ENV, None) _unicodeOutputErrors = environment(OUTPUT_ERRORS_ENV, None) _hooks = [] _pauseAtEnd = False _relativePath = False if _extraArguments is not None: _extraArguments = string.split(_extraArguments) args = _extraArguments + args # Parse the arguments. pairs, remainder = getopt.getopt(args, 'VhHvkp:m:frino:a:buBP:I:D:E:F:', ['version', 'help', 'extended-help', 'verbose', 'null-hook', 'suppress-errors', 'prefix=', 'no-prefix', 'module=', 'flatten', 'raw-errors', 'interactive', 'no-override-stdout', 'binary', 'chunk-size=', 'output=' 'append=', 'preprocess=', 'import=', 'define=', 'execute=', 'execute-file=', 'buffered-output', 'pause-at-end', 'relative-path', 'no-callback-error', 'no-bangpath-processing', 'unicode', 'unicode-encoding=', 'unicode-input-encoding=', 'unicode-output-encoding=', 'unicode-errors=', 'unicode-input-errors=', 'unicode-output-errors=']) for option, argument in pairs: if option in ('-V', '--version'): sys.stderr.write("%s version %s\n" % (__program__, __version__)) return elif option in ('-h', '--help'): usage(False) return elif option in ('-H', '--extended-help'): usage(True) return elif option in ('-v', '--verbose'): _hooks.append(VerboseHook()) elif option in ('--null-hook',): _hooks.append(Hook()) elif option in ('-k', '--suppress-errors'): _options[EXIT_OPT] = False _interactive = True # suppress errors implies interactive mode elif option in ('-m', '--module'): _pseudo = argument elif option in ('-f', '--flatten'): _options[FLATTEN_OPT] = True elif option in ('-p', '--prefix'): _prefix = argument elif option in ('--no-prefix',): _prefix = None elif option in ('-r', '--raw-errors'): _options[RAW_OPT] = True elif option in ('-i', '--interactive'): _interactive = True elif option in ('-n', '--no-override-stdout'): _options[OVERRIDE_OPT] = False elif option in ('-o', '--output'): _output = argument, 'w', _options[BUFFERED_OPT] elif option in ('-a', '--append'): _output = argument, 'a', _options[BUFFERED_OPT] elif option in ('-b', '--buffered-output'): _options[BUFFERED_OPT] = True elif option in ('-B',): # DEPRECATED _options[BUFFERED_OPT] = True elif option in ('--binary',): _binary = 0 elif option in ('--chunk-size',): _binary = int(argument) elif option in ('-P', '--preprocess'): _preprocessing.append(('pre', argument)) elif option in ('-I', '--import'): for module in string.split(argument, ','): module = string.strip(module) _preprocessing.append(('import', module)) elif option in ('-D', '--define'): _preprocessing.append(('define', argument)) elif option in ('-E', '--execute'): _preprocessing.append(('exec', argument)) elif option in ('-F', '--execute-file'): _preprocessing.append(('file', argument)) elif option in ('-u', '--unicode'): _unicode = True elif option in ('--pause-at-end',): _pauseAtEnd = True elif option in ('--relative-path',): _relativePath = True elif option in ('--no-callback-error',): _options[CALLBACK_OPT] = True elif option in ('--no-bangpath-processing',): _options[BANGPATH_OPT] = False elif option in ('--unicode-encoding',): _unicodeInputEncoding = _unicodeOutputEncoding = argument elif option in ('--unicode-input-encoding',): _unicodeInputEncoding = argument elif option in ('--unicode-output-encoding',): _unicodeOutputEncoding = argument elif option in ('--unicode-errors',): _unicodeInputErrors = _unicodeOutputErrors = argument elif option in ('--unicode-input-errors',): _unicodeInputErrors = argument elif option in ('--unicode-output-errors',): _unicodeOutputErrors = argument # Set up the Unicode subsystem if required. if _unicode or \ _unicodeInputEncoding or _unicodeOutputEncoding or \ _unicodeInputErrors or _unicodeOutputErrors: theSubsystem.initialize(_unicodeInputEncoding, \ _unicodeOutputEncoding, \ _unicodeInputErrors, _unicodeOutputErrors) # Now initialize the output file if something has already been selected. if _output is not None: _output = apply(AbstractFile, _output) # Set up the main filename and the argument. if not remainder: remainder.append('-') filename, arguments = remainder[0], remainder[1:] # Set up the interpreter. if _options[BUFFERED_OPT] and _output is None: raise ValueError, "-b only makes sense with -o or -a arguments" if _prefix == 'None': _prefix = None if _prefix and type(_prefix) is types.StringType and len(_prefix) != 1: raise Error, "prefix must be single-character string" interpreter = Interpreter(output=_output, \ argv=remainder, \ prefix=_prefix, \ pseudo=_pseudo, \ options=_options, \ hooks=_hooks) try: # Execute command-line statements. i = 0 for which, thing in _preprocessing: if which == 'pre': command = interpreter.file target = theSubsystem.open(thing, 'r') name = thing elif which == 'define': command = interpreter.string if string.find(thing, '=') >= 0: target = '%s{%s}' % (_prefix, thing) else: target = '%s{%s = None}' % (_prefix, thing) name = '' % i elif which == 'exec': command = interpreter.string target = '%s{%s}' % (_prefix, thing) name = '' % i elif which == 'file': command = interpreter.string name = '' % (i, thing) target = '%s{execfile("""%s""")}' % (_prefix, thing) elif which == 'import': command = interpreter.string name = '' % i target = '%s{import %s}' % (_prefix, thing) else: assert 0 interpreter.wrap(command, (target, name)) i = i + 1 # Now process the primary file. interpreter.ready() if filename == '-': if not _interactive: name = '' path = '' file = sys.stdin else: name, file = None, None else: name = filename file = theSubsystem.open(filename, 'r') path = os.path.split(filename)[0] if _relativePath: sys.path.insert(0, path) if file is not None: if _binary < 0: interpreter.wrap(interpreter.file, (file, name)) else: chunkSize = _binary interpreter.wrap(interpreter.binary, (file, name, chunkSize)) # If we're supposed to go interactive afterwards, do it. if _interactive: interpreter.interact() finally: interpreter.shutdown() # Finally, if we should pause at the end, do it. if _pauseAtEnd: try: raw_input() except EOFError: pass def main(): invoke(sys.argv[1:]) if __name__ == '__main__': main() empy-3.3.orig/COPYING0000644000175000017500000006347607740712770012436 0ustar anaana GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! empy-3.3.orig/README0000644000175000017500000032144007747162550012251 0ustar anaanaSummary A powerful and robust templating system for Python. Overview EmPy is a system for embedding Python expressions and statements in template text; it takes an EmPy source file, processes it, and produces output. This is accomplished via expansions, which are special signals to the EmPy system and are set off by a special prefix (by default the at sign, '@'). EmPy can expand arbitrary Python expressions and statements in this way, as well as a variety of special forms. Textual data not explicitly delimited in this way is sent unaffected to the output, allowing Python to be used in effect as a markup language. Also supported are callbacks via hooks, recording and playback via diversions, and dynamic, chainable filters. The system is highly configurable via command line options and embedded commands. Expressions are embedded in text with the '@(...)' notation; variations include conditional expressions with '@(...?...!...)' and the ability to handle thrown exceptions with '@(...$...)'. As a shortcut, simple variables and expressions can be abbreviated as '@variable', '@object.attribute', '@function(arguments)', '@sequence' [index], and combinations. Full-fledged statements are embedded with '@{...}'. Control flow in terms of conditional or repeated expansion is available with '@[...]'. A '@' followed by a whitespace character (including a newline) expands to nothing, allowing string concatenations and line continuations. Comments are indicated with '@#' and consume the rest of the line, up to and including the trailing newline. '@%' indicate "significators," which are special forms of variable assignment intended to specify per-file identification information in a format which is easy to parse externally. Context name and line number changes can be done with '@?' and '@!' respectively. '@<...>' markups are customizeable by the user and can be used for any desired purpose. Escape sequences analogous to those in C can be specified with '@\...', and finally a '@@' sequence expands to a single literal at sign. Getting the software The current version of empy is 3.3. The latest version of the software is available in a tarball here: "http://www.alcyone.com/software/empy/empy-latest.tar.gz", http://www.alcyone.com/software/empy/empy-latest.tar.gz. The official URL for this Web site is "http://www.alcyone.com/software/empy/", http://www.alcyone.com/software/empy/. Requirements EmPy should work with any version of Python from 1.5.2 onward. It has been tested with all major versions of CPython from 1.5 up, and Jython from 2.0 up (using Java runtimes 1.3 and 1.4). The included test script is intended to run on Unix-like systems with a Bourne shell. License This code is released under the "LGPL", http://www.gnu.org/copyleft/lesser.html. Mailing lists There are two EmPy related mailing lists available. The first is a receive-only, very low volume list for important announcements (including releases). To subscribe, send an email to "empy-announce-list-subscribe@alcyone.com", mailto:empy-announce-list-subscribe@alcyone.com. The second is a general discussion list for topics related to EmPy, and is open for everyone to contribute; announcements related to EmPy will also be made on this list. The author of EmPy (and any future developers) will also be on the list, so it can be used not only to discuss EmPy features with other users, but also to ask questions of the author(s). To subscribe, send an email to "empy-list-subscribe@alcyone.com", mailto:empy-list-subscribe@alcyone.com. Basics EmPy is intended for embedding Python code in otherwise unprocessed text. Source files are processed, and the results are written to an output file. Normal text is sent to the output unchanged, but markups are processed, expanded to their results, and then written to the output file as strings (that is, with the 'str' function, not 'repr'). The act of processing EmPy source and handling markups is called "expansion." Code that is processed is executed exactly as if it were entered into the Python interpreter; that is, it is executed with the equivalent of 'eval' (for expressions) and 'exec' (for statements). EmPy is intended to be a very thin (though powerful) layer on top of a running Python system; Python and EmPy files can be mixed together (via command line options) without complications. By default the embedding prefix is the at sign ('@'), which appears neither in valid Python code nor commonly in arbitrary texts; it can be overridden with the -p option (or with the 'empy.setPrefix' function). The prefix indicates to the EmPy interpreter that a special sequence follows and should be processed rather than sent to the output untouched (to indicate a literal at sign, it can be doubled as in '@@'). When the interpreter starts processing its target file, no modules are imported by default, save the 'empy' pseudomodule (see below), which is placed in the globals; the 'empy' pseudomodule is associated with a particular interpreter -- in fact, they are the same object -- and it is important that it not be removed from that interpreter's globals, nor that it be shared with other interpreters running concurrently (a name other than 'empy' can be specified with the -m option). The globals are not cleared or reset in any way. It is perfectly legal to set variables or explicitly import modules and then use them in later markups, *e.g.*, '@{import time} ... @time.time()'. Scoping rules are as in normal Python, although all defined variables and objects are taken to be in the global namespace. Indentation is significant in Python, and therefore is also significant in EmPy. EmPy statement markups ('@{...}'), when spanning multiple lines, must be flush with the left margin. This is because (multiline) statement markups are not treated specially in EmPy and are simply passed to the Python interpreter, where indentation is significant. Activities you would like to be done before any processing of the main EmPy file can be specified with the -I, -D, -E, -F, and -P options. -I imports modules, -D executes a Python variable assignment, -E executes an arbitrary Python (not EmPy) statement, -F executes a Python (not EmPy) file, and -P processes an EmPy (not Python) file. These operations are done in the order they appear on the command line; any number of each (including, of course, zero) can be used. Expansions The following markups are supported. For concreteness below, '@' is taken for the sake of argument to be the prefix character, although this can be changed. **'@# COMMENT NEWLINE'** -- A comment. Comments, including the trailing newline, are stripped out completely. Comments should only be present outside of expansions. The comment itself is not processed in any way: It is completely discarded. This allows '@#' comments to be used to disable markups. *Note:* As special support for "bangpaths" in Unix-like operating systems, if the first line of a file (or indeed any context) begins with '#!', and the interpreter has a 'processBangpaths' option set to true (default), it is treated as a '@#' comment. A '#!' sequence appearing anywhere else will be handled literally and unaltered in the expansion. Example:: @# This line is a comment. @# This will NOT be expanded: @x. **'@? NAME NEWLINE'** -- Set the name of the current context to be the given string. Variables are not allowed here; the name is treated as a literal. (If you wish to use arbitrary expressions, use the 'empy.setContextName' function instead.) Example:: @?NewName The context name is now @empy.identify()[0] (NewName). **'@! INTEGER NEWLINE'** -- Set the line number of the current context to be the given integer value; this is similar to the '#line' C preprocessor directive. This is done in such a way that the *next* line will have the specified numeric value, not the current one. Expressions are not allowed here; the number must be a literal integer. (If you wish to use arbitrary expressions, use the 'empy.setContextLine' function instead.) Example:: @!100 The context line is now @empy.identify()[1] (100). **'@ WHITESPACE'** -- A '@' followed by one whitespace character (a space, horizontal tab, vertical tab, carriage return, or newline) is expanded to nothing; it serves as a way to explicitly separate two elements which might otherwise be interpreted as being the same symbol (such as '@name@ s' to mean '@(name)s' -- see below). Also, since a newline qualifies as whitespace here, the lone '@' at the end of a line represents a line continuation, similar to the backslash in other languages. Coupled with statement expansion below, spurious newlines can be eliminated in statement expansions by use of the '@{...}@' construct. Example:: This will appear as one word: salt@ water. This is a line continuation; @ this text will appear on the same line. **'@\ ESCAPE_CODE'** -- An escape code. Escape codes in EmPy are similar to C-style escape codes, although they all begin with the prefix character. Valid escape codes include: '@\0' -- NUL, null '@\a' -- BEL, bell '@\b' -- BS, backspace '@\d' -- three-digital decimal code DDD '@\e' -- ESC, escape '@\f' -- FF, form feed '@\h' -- DEL, delete '@\n' -- LF, linefeed character, newline '@\oOOO' -- three-digit octal code OOO '@\qQQQQ' -- four-digit quaternary code QQQQ '@\r' -- CR, carriage return '@\s' -- SP, space '@\t' -- HT, horizontal tab '@\v' -- VT, vertical tab '@\xHH' -- two-digit hexadecimal code HH '@\z' -- EOT, end of transmission '@^X' -- the control character ^X Unlike in C-style escape codes, escape codes taking some number of digits afterward always take the same number to prevent ambiguities. Furthermore, unknown escape codes are treated as parse errors to discourage potential subtle mistakes. Note that, while '@\0' represents the NUL character, to represent an octal code, one must use '@\o...', in contrast to C. Example:: This embeds a newline.@\nThis is on the following line. This beeps!@\a There is a tab here:@\tSee? This is the character with octal code 141: @\o141. **'@@'** -- A literal at sign ('@'). To embed two adjacent at signs, use '@@@@', and so on. Any literal at sign that you wish to appear in your text must be written this way, so that it will not be processed by the system. *Note:* If a prefix other than '@' has been chosen via the command line option, one expresses that literal prefix by doubling it, not by appending a '@'. Example:: The prefix character is @@. To get the expansion of x you would write @@x. **'@)', '@]', '@}'** -- These expand to literal close parentheses, close brackets, and close braces, respectively; these are included for completeness and explicitness only. Example:: This is a close parenthesis: @). **'@"..."', '@"""..."""', etc.** -- These string literals expand to the literals themselves, so '@"test"' expands to 'test'. Since they are inherently no-operations, the only reason for their use is to override their behavior with hooks. **'@( EXPRESSION )'** -- Evaluate an expression, and expand with the string (via a call to 'str') representation evaluation of that expression. Whitespace immediately inside the parentheses is ignored; '@( expression )' is equivalent to '@(expression)'. If the expression evaluates to 'None', nothing is expanded in its place; this allows function calls that depend on side effects (such as printing) to be called as expressions. (If you really *do* want a 'None' to appear in the output, then use the Python string '"None"'.) *Note:* If an expression prints something to 'sys.stdout' as a side effect, then that printing will be spooled to the output *before* the expression's return value is. Example:: 2 + 2 is @(2 + 2). 4 squared is @(4**2). The value of the variable x is @(x). This will be blank: @(None). **'@( TEST ? THEN (! ELSE)_opt ($ EXCEPT)_opt )'** -- A special form of expression evaluation representing conditional and protected evaluation. Evaluate the "test" expression; if it evaluates to true (in the Pythonic sense), then evaluate the "then" section as an expression and expand with the 'str' of that result. If false, then the "else" section is evaluated and similarly expanded. The "else" section is optional and, if omitted, is equivalent to 'None' (that is, no expansion will take place). *Note*: For backward compatibility, the "else" section delimiter, '!', may be expressed as a ':'. This behavior is supported but deprecated. If the "except" section is present, then if any of the prior expressions raises an exception when evaluated, the expansion will be replaced with the evaluation of the except expression. (If the "except" expression itself raises, then that exception will be propagated normally.) The except section is optional and, if omitted, is equivalent to 'None' (that is, no expansion will take place). An exception (cough) to this is if one of these first expressions raises a SyntaxError; in that case the protected evaluation lets the error through without evaluating the "except" expression. The intent of this construct is to except runtime errors, and if there is actually a syntax error in the "try" code, that is a problem that should probably be diagnosed rather than hidden. Example:: What is x? x is @(x ? "true" ! "false"). Pluralization: How many words? @x word@(x != 1 ? 's'). The value of foo is @(foo $ "undefined"). Division by zero is @(x/0 $ "illegal"). **'@ SIMPLE_EXPRESSION'** -- As a shortcut for the '@(...)' notation, the parentheses can be omitted if it is followed by a "simple expression." A simple expression consists of a name followed by a series of function applications, array subscriptions, or attribute resolutions, with no intervening whitespace. For example: - a name, possibly with qualifying attributes (*e.g.*, '@value', '@os.environ'). - a straightforward function call (*e.g.*, '@min(2, 3)', '@time.ctime()'), with no space between the function name and the open parenthesis. - an array subscription (*e.g.*, '@array[index]', '@os.environ[name]', with no space between the name and the open bracket. - any combination of the above (*e.g.*, '@function(args).attr[sub].other[i](foo)'). In essence, simple expressions are expressions that can be written ambiguously from text, without intervening space. Note that trailing dots are not considered part of the expansion (*e.g.*, '@x.' is equivalent to '@(x).', not '@(x.)', which would be illegal anyway). Also, whitespace is allowed within parentheses or brackets since it is unambiguous, but not between identifiers and parentheses, brackets, or dots. Explicit '@(...)' notation can be used instead of the abbreviation when concatenation is what one really wants (*e.g.*, '@(word)s' for simple pluralization of the contents of the variable 'word'). As above, if the expression evaluates to the 'None' object, nothing is expanded. Note that since a curly appearing where EmPy would expect an open parenthesis or bracket in is meaningless in Python, it is treated as a parse error (*e.g.*, '@x{1, 2}' results in an error). Example:: The value of x is @x. The ith value of a is @a[i]. The result of calling f with q is @f(q). The attribute a of x is @x.a. The current time is @time.ctime(time.time()). The current year is @time.localtime(time.time())[0]. These are the same: @min(2,3) and @min(2, 3). But these are not the same: @min(2, 3) vs. @min (2, 3). The plural of @name is @(name)s, or @name@ s. **'@` EXPRESSION `'** -- Evaluate a expression, and expand with the 'repr' (instead of the 'str' which is the default) of the evaluation of that expression. This expansion is primarily intended for debugging and is unlikely to be useful in actual practice. That is, a '@`...`' is identical to '@(repr(...))'. Example:: The repr of the value of x is @`x`. This print the Python repr of a module: @`time`. This actually does print None: @`None`. **'@: EXPRESSION : DUMMY :'** -- Evaluate an expression and then expand to a '@:', the original expression, a ':', the evaluation of the expression, and then a ':'. The current contents of the dummy area are ignored in the new expansion. In this sense it is self-evaluating; the syntax is available for use in situations where the same text will be sent through the EmPy processor multiple times. Example:: This construct allows self-evaluation: @:2 + 2:this will get replaced with 4: **'@{ STATEMENTS }'** -- Execute a (potentially compound) statement; statements have no return value, so the expansion is not replaced with anything. Multiple statements can either be separated on different lines, or with semicolons; indentation is significant, just as in normal Python code. Statements, however, can have side effects, including printing; output to 'sys.stdout' (explicitly or via a 'print' statement) is collected by the interpreter and sent to the output (unless this behavior is suppressed with the -n option). The usual Python indentation rules must be followed, although if the statement consists of only one statement, leading and trailing whitespace is ignored (*e.g.*, '@{ print time.time() }' is equivalent to '@{print time.time()}'). Example:: @{x = 123} @{a = 1; b = 2} @{print time.time()} @# Note that extra newlines will appear above because of the @# newlines trailing the close braces. To suppress them @# use a @ before the newline: @{ for i in range(10): print "i is %d" % i }@ @{print "Welcome to EmPy."}@ **'@% KEY (WHITESPACE VALUE)_opt NEWLINE'** -- Declare a significator. Significators consume the whole line (including the trailing newline), and consist of a key string containing no whitespace, and than optional value prefixed by whitespace. The key may not start with or contain internal whitespace, but the value may; preceding or following whitespace in the value is stripped. Significators are totally optional, and are intended to be used for easy external (that is, outside of EmPy) identification when used in large scale environments with many EmPy files to be processed. The purpose of significators is to provide identification information about each file in a special, easy-to-parse form so that external programs can process the significators and build databases, independently of EmPy. Inside of EmPy, when a significator is encountered, its key, value pair is translated into a simple assignment of the form '__KEY__ = VALUE' , where "__KEY__" is the key string with two underscores on either side and "VALUE" is a Python expression. Example:: @%title "Gravitation" @%author "Misner", "Thorne", "Wheeler" @%publisher "W.H. Freeman and Company" @%pages 1279 @%keywords 'physics', 'gravity', 'Einstein', 'relativity' @%copyright 1970, 1971 **'@< CONTENTS >'** -- Invoke a custom markup. The custom markup is a special markup reserved for use by the user; it has no prescribed meaning on its own. If 'contents' is a string representing what appears in between the angle brackets, then expanding this markup is equivalent to 'empy.invokeCallback(contents)'. See the "Custom markup" section for more information. Control EmPy version 3 and above includes the ability to direct conditional and repeated expansion of blocks of EmPy code with control markups (the obsolescent "substitution" markups are unavailable as of version 3.0). Control markups have analogs to control flow structures in Python such as 'if/elif/else', 'for', and 'while'. Control markups are set off with the '@[...]' notation. Control markups are designed to be used in precisely the same way that their internal Python analogues are used, except that the control markups are intended to be used where there is much more markup than control structure. Some control markups are considered "primary," (*e.g.*, 'if', 'for', 'while') as they begin a control markup. Others are considered "secondary," since they can only appear inside control flow markups delineated by primary markups (*e.g.*, 'elif', 'else', 'continue', 'break'). Since EmPy, unlike Python, cannot use indentation to determine where control structures begin and end, all primary control markups *must* be followed by a corresponding terminating control markup:: @[PRIMARY ...]...@[end PRIMARY] (where 'PRIMARY' represents one of the primary keywords). The end markup is mandatory, as is the space between the 'end' and the starting keyword. For instance:: @# If `person' is alive, show their age. @person.name is @ @[if person.isAlive]@person.age@[else]dead@[end if]. All primary markups must be terminated in this way, and the keyword appearing in the appropriate 'end' markup must match the primary markup it corresponds to; if either of these conditions are not satisfied, the result is a parse error. Everything between the starting control flow marker ('@[PRIMARY ...]') and the ending marker ('@[end PRIMARY]') -- including other markups, even control markups -- is considered part of the markup. Control markups can be nested:: @# Print all non-false elements on separate lines. @[for elem in elements]@[if elem]@elem@\n@[end if]@[end for] Three major types of primary control markups are available: conditional (*e.g.*, 'if', 'try'), looping (*e.g.*, 'for', 'while'), and definitional (*e.g.*, 'def', discussed below). Conditional control markups conditionally expand their contents, whereas looping control markups repeatedly expand their contents. The third type, definitional markups, will define new objects in the globals relating to their contents. Conditional and looping markups also differ in one substantial respect: Looping constructs support '@[continue]' and '@[break]' markups which, like their Python equivalents, continue with the next iteration or break out of the innermost looping construct, respectively ('@[continue]' and '@[break]' markups have no meaning inside conditional markups and are an error). Also like their Python equivalents, '@[continue]' and '@[break]' may appear inside nested markups, so long as they ultimately are contained by at least one looping control markup:: @# Walk a long a linked list, printing each element. @[while 1]@ @node @{node = node.next}@ @[if not node]@[break]@[end if]@ @[end while] The provided markups are designed to mimic the internal Python control structures as closely as possible. The supported control markups are (the phrases in all uppercase are intended to signify user-selectable patterns):: @[if CONDITION1]...@[elif CONDITION2]...@[else]...@[end if] @[try]...@[except ...]...@[except ...]...@[end try] @[try]...@[finally]...@[end try] @[for VARIABLE in SEQUENCE]...@[else]...@[end for] @[while CONDITION]...@[else]...@[end while] @[def SIGNATURE]...@[end def] All recognizable forms behave like their Python equivalents; 'if' can contain multiple 'elif' secondary markups within it; the 'else' markups are optional (but must appear at the end), the 'try' form with the 'except' clause can contain multiple ones which are handled in sequence, the 'try' form can either contain one or more 'except' clauses or one 'finally' clause (but not both), and the 'for' and 'while' structures can contain 'continue' or 'break' clauses internally (even if contained within other markups). The third type of primary control markup is "definitional," in that they create objects in the globals for later use (*e.g.*, 'def'). This allows the definition of a callable object which, when called, will expand the contained markup (which can in turn, of course, contain further markups). The argument to the markup can be any legal Python function signature:: @[def f(x, y, z=2, *args, **keywords)]...@[end def] would define a function in the globals named 'f' that takes the given arguments. A macro markup of the form '@[def SIGNATURE]CODE@[end def]' is equivalent to the Python code:: def SIGNATURE: r"""CODE""" # so it is a doc string empy.expand(r"""CODE""", locals()) That is, it creates a Python function with the same name and function arguments, whose docstring is the contents of the EmPy markup that will be expanded when called. And, when called, it will expand those contents, with the locals passed in. Unicode support EmPy version 3.1 and above includes intrinsic Unicode support. EmPy's Unicode support defers to Python's internal Unicode support, available in Python 2.0 and up, in order to allow seamless and transparent translation of different encodings to the native Python Unicode format. Knowledge of Python's Unicode support is expected, although not completely required, to gain full benefit of EmPy's Unicode features. To enable Unicode support, start EmPy with the -u/--unicode option. EmPy will then transparently encode from the input stream, process markups internally with native Unicode, and then decode transparently to the output stream. By default, Python sets 'sys.stdin' and 'sys.stdout' with a default encoding which is accessible via 'sys.getdefaultencoding()'; encodings are represented by string names. These streams have encodings set by the system and *cannot* be changed. However, encodings for newly created files (files to be read when specified on the command line, and/or files to be written when used with the -o and -a arguments) can be specified for EmPy via command line options. The --unicode-encoding option simultaneously indicates the encoding to be used for both input and output, whereas the --unicode-input-encoding and --unicode-output-encoding options can each be used to specify different encodings for both input and output. (If an encoding is not explicitly indicated, it resorts to the system default in 'sys.getdefaultencoding()', which is locale dependent.) Python's Unicode implementation has the concept of error handlers, registered with the 'codecs' module, which can be specified to determine what action should take place if input cannot be decoded into Unicode, or Unicode cannot be encoded into output. EmPy uses these same "errors," as they are called, and can be specified via command line options. The three most common error handlers are: 'ignore', where invalid sequences are simply ignored; 'replace', where invalid sequences are replaced with an encoding-specific indicator, usually a question mark; and 'strict', where invalid sequences raise an error. The --unicode-errors command line option specifies the same error handler to be used for both input and output, and the --unicode-input-errors and --unicode-output-errors options can specify different error handlers for input and output. If an error handler is not explicitly specified, the 'strict' handler (which will raise errors) is used. Remember, to specify input encodings or errors that will take effect, one cannot take input from 'sys.stdin' and must explicitly specify an EmPy file to process on the command line. Similarly, for output encodings or errors, 'sys.stdout' cannot be used and an explicit output file must be specified with the -o or -a options. It is perfectly valid to enable the Unicode subsystem (-u option) while using 'sys.stdin' and 'sys.stdout', but the encodings and errors of these preexisting streams cannot be changed. Combined with the --no-prefix option, which disables all markup processing, EmPy can act merely as an encoding translator, relying on Python's Unicode facilities:: em.py --no-prefix \ --unicode-input-encoding=utf-8 \ --unicode-output-encoding=latin-1 \ -o filename.Latin-1 filename.UTF-8 Significators Significators, introduced in EmPy version 1.2, are intended to represent special assignment in a form that is easy to externally parse. For instance, if one has a system that contains many EmPy files, each of which has its own title, one could use a 'title' significator in each file and use a simple regular expression to find this significator in each file and organize a database of the EmPy files to be built. This is an easier proposition than, for instance, attempting to grep for a normal Python assignment (inside a '@{...}' expansion) of the desired variable. Significators look like the following:: @%KEY VALUE including the trailing newline, where "key" is a name and "value" is a Python expression, and are separated by any whitespace. This is equivalent to the following Python code:: __KEY__ = VALUE That is to say, a significator key translates to a Python variable consisting of that key surrounded by double underscores on either side. The value may contain spaces, but the key may not. So:: @%title "All Roads Lead to Rome" translates to the Python code:: __title__ = "All Roads Lead to Rome" but obviously in a way that easier to detect externally than if this Python code were to appear somewhere in an expansion. Since significator keys are surrounded by double underscores, significator keys can be any sequence of alphanumeric and underscore characters; choosing '123' is perfectly valid for a significator (although straight), since it maps to the name '__123__' which is a legal Python identifier. Note the value can be any Python expression. The value can be omitted; if missing, it is treated as 'None'. Significators are completely optional; it is completely legal for a EmPy file or files to be processed without containing any significators. Significators can appear anywhere within a file outside of other markups, but typically they are placed near the top of the file to make them easy to spot and edit by humans. A regular expression string designed to match significators (with the default prefix) is available as 'empy.SIGNIFICATOR_RE_STRING', and also is a toplevel definition in the 'em' module itself. Diversions EmPy supports an extended form of diversions, which are a mechanism for deferring and recalling output on demand, similar to the functionality included in m4. Multiple "streams" of output can be diverted (deferred) and undiverted (recalled) in this manner. A diversion is identified with a name, which is any immutable object such an integer or string. When recalled, diverted code is *not* resent through the EmPy interpreter (although a filter could be set up to do this). By default, no diversions take place. When no diversion is in effect, processing output goes directly to the specified output file. This state can be explicitly requested at any time by calling the 'empy.stopDiverting' function. It is always legal to call this function. When diverted, however, output goes to a deferred location which can then be recalled later. Output is diverted with the 'empy.startDiversion' function, which takes an argument that is the name of the diversion. If there is no diversion by that name, a new diversion is created and output will be sent to that diversion; if the diversion already exists, output will be appended to that preexisting diversion. Output send to diversions can be recalled in two ways. The first is through the 'empy.playDiversion' function, which takes the name of the diversion as an argument. This recalls the named diversion, sends it to the output, and then erases that diversion. A variant of this behavior is the 'empy.replayDiversion', which recalls the named diversion but does not eliminate it afterwards; 'empy.replayDiversion' can be repeatedly called with the same diversion name, and will replay that diversion repeatedly. 'empy.createDiversion' create a diversion without actually diverting to it, for cases where you want to make sure a diversion exists but do not yet want to send anything to it. The diversion object itself can be retrieved with 'empy.retrieveDiversion'. Diversions act as writable file-objects, supporting the usual 'write', 'writelines', 'flush', and 'close' methods. The data that has been diverted to them can be retrieved in one of two ways; either through the 'asString' method, which returns the entire contents of the diversion as a single strong, or through the 'asFile' method, which returns the contents of the diversion as a readable (not writable) file-like object. Diversions can also be explicitly deleted without recalling them with the 'empy.purgeDiversion' function, which takes the desired diversion name as an argument. Additionally there are three functions which will apply the above operations to all existing diversions: 'empy.playAllDiversions', 'empy.replayAllDiversions', and 'empy.purgeAllDiversions'. All three will do the equivalent of a 'empy.stopDiverting' call before they do their thing. The name of the current diversion can be requested with the 'empy.getCurrentDiversion' function; also, the names of all existing diversions (in sorted order) can be retrieved with 'empy.getAllDiversions'. When all processing is finished, the equivalent of a call to 'empy.playAllDiversions' is done. Filters EmPy also supports dynamic filters, introduced in version 1.3. Filters are put in place right "before" the final output file, and so are only invoked after all other processing has taken place (including interpreting and diverting). Filters take input, remap it, and then send it to the output. The current filter can be retrieved with the 'empy.getFilter' function. The filter can be cleared (reset to no filter) with 'empy.resetFilter' and a special "null filter" which does not send any output at all can be installed with 'empy.nullFilter'. A custom filter can be set with the 'empy.setFilter' function; for convenience, specialized shortcuts for filters preexist and can be used in lieu of actual 'empy.Filter' instances for the 'empy.setFilter' or 'empy.attachFilter' argument: - 'None' is a special filter meaning "no filter"; when installed, no filtering whatsoever will take place. 'empy.setFilter(None)' is equivalent to 'empy.resetFilter()'. - '0' (or any other numeric constant equal to zero) is another special filter that represents the null filter; when installed, no output will ever be sent to the filter's sink. - A filter specified as a function (or lambda) is expected to take one string argument and return one string argument; this filter will execute the function on any input and use the return value as output. - A filter that is a string is a 256-character table is substituted with the result of a call to 'string.translate' using that table. - A filter can be an instance of a subclass of 'empy.Filter'. This is the most general form of filter. (In actuality, it can be any object that exhibits a 'Filter' interface, which would include the normal file-like 'write', 'flush', and 'close' methods, as well as 'next', 'attach', and 'detach' methods for filter-specific behavior.) - Finally, the argument to 'empy.setFilter' can be a Python list consisting of one or more of the above objects. In that case, those filters are chained together in the order they appear in the list. An empty list is the equivalent of 'None'; all filters will be uninstalled. Filters are, at their core, simply file-like objects (minimally supporting 'write', 'flush', and 'close' methods that behave in the usual way) which, after performing whatever processing they need to do, send their work to the next file-like object or filter in line, called that filter's "sink." That is to say, filters can be "chained" together; the action of each filter takes place in sequence, with the output of one filter being the input of the next. Additionally, filters support a '_flush' method (note the leading underscore) which will always flush the filter's underlying sink; this method should be not overridden. Filters also support three additional methods, not part of the traditional file interface: 'attach', which takes as an argument a file-like object (perhaps another filter) and sets that as the filter's "sink" -- that is, the next filter/file-like object in line. 'detach' (which takes no arguments) is another method which flushes the filter and removes its sink, leaving it isolated. Finally, 'next' is an accessor method which returns the filter's sink -- or 'None', if the filter does not yet have a sink attached. To create your own filter, you can create an object which supports the above described interface, or simply derive from the 'empy.Filter' class and override its 'write' and possibly 'flush' methods. You can chain filters together by passing them as elements in a list to the 'empy.setFilter' function, or you can chain them together manually with the 'attach' method:: firstFilter.attach(secondFilter) empy.setFilter(firstFilter) or just let EmPy do the chaining for you:: empy.setFilter([firstFilter, secondFilter]) In either case, EmPy will walk the filter chain and find the end and then hook that into the appropriate interpreter stream; you need not do this manually. The function 'empy.attachFilter' can be used to attach a single filter (or shortcut, as above) to the end of a currently existing chain. Note that unlike its cousin 'empy.setFilter', one cannot pass a sequence of filters (or filter shortcuts) to 'empy.attachFilter'. (If there is no existing filter chain installed, 'empy.attachFilter' will behave the same as 'empy.setFilter'.) Subclasses of 'empy.Filter' are already provided with the above null, function, and string functionality described above; they are 'NullFilter', 'FunctionFilter', and 'StringFilter', respectively. In addition, a filter which supports buffering, 'BufferedFilter', is provided. Several variants are included: 'SizeBufferedFilter', a filter which buffers into fixed-sized chunks, 'LineBufferedFilter', a filter which buffers by lines, and 'MaximallyBufferedFilter', a filter which completely buffers its input. Hooks The EmPy system allows for the registry of hooks with a running EmPy interpreter. Originally introduced in version 2.0 and much improved in 3.2, hooks are objects, registered with an interpreter, whose methods represent specific callbacks. Any number of hook objects can be registered with an interpreter, and when a callback is invoked, the associated method on each one of those hook objects will be called by the interpreter in sequence. Hooks are simply instances, nominally derived from the 'empy.Hook' class. The 'empy.Hook' class itself defines a series of methods, with the expected arguments, which would be called by a running EmPy interpreter. This scenario, much improved from the prior implementation in 2.0, allows hooks to keep state and have more direct access to the interpreter they are running in (the 'empy.Hook' instance contains an 'interpreter' attribute). To use a hook, derive a class from 'empy.Hook' and override the desired methods (with the same signatures as they appear in the base class). Create an instance of that subclass, and then register it with a running interpreter with the 'empy.addHook' function. (This same hook instance can be removed with the 'empy.removeHook' function.) More than one hook instance can be registered with an interpreter; in such a case, the appropriate methods are invoked on each instance in the order in which they were registered. To adjust this behavior, an optional 'prepend' argument to the 'empy.addHook' function can be used dictate that the new hook should placed at the *beginning* of the sequence of hooks, rather than at the end (which is the default). All hooks can be enabled and disabled entirely for a given interpreter; this is done with the 'empy.enableHooks' and 'empy.disableHooks' functions. By default hooks are enabled, but obviously if no hooks have been registered no hook callbacks will be made. Whether hooks are enabled or disabled can be determined by calling 'empy.areHooksEnabled'. To get a (copy of) the list of registered hooks, call 'empy.getHooks'. Finally, to invoke a hook manually, use 'empy.invokeHook'. For a list of supported hook callbacks, see the 'empy.Hook' class definition. As a practical example, this sample Python code would print a pound sign followed by the name of every file that is included with 'empy.include':: class IncludeHook(empy.Hook): def beforeInclude(self, name, file, locals): print "# %s" % name empy.addHook(IncludeHook()) Custom markup Since version 3.2.1, the markup '@<...>' is reserved for user-defined use. Unlike the other markups, this markup has no specified meaning on its own, and can be provided a meaning by the user. This meaning is provided with the use of a "custom callback," or just "callback," which can be set, queried, or reset using the pseudomodule function. The custom callback is a callable object which, when invoked, is passed a single argument: a string representing the contents of what was found inside the custom markup '@<...>'. To register a callback, call 'empy.registerCallback'. To remove one, call 'empy.deregisterCallback'. To retrieve the callback (if any) registered with the interpreter, use 'empy.getCallback'. Finally, to invoke the callback just as if the custom markup were encountered, call 'empy.invokeCallback'. For instance, '@' would be equivalent to the call '@empy.invokeCallback("This text")'. By default, to invoke a callback (either explicitly with 'empy.invokeCallback' or by processing a '@<...>' custom markup) when no callback has been registered is an error. This behavior can be changed with the 'CALLBACK_OPT' option, or the --no-callback-error command line option. Pseudomodule The 'empy' pseudomodule is available only in an operating EmPy system. (The name of the module, by default 'empy', can be changed with the -m option or the 'EMPY_PSEUDO' environment variable). It is called a pseudomodule because it is not actually a module, but rather exports a module-like interface. In fact, the pseudmodule is actually the same internal object as the interpreter itself. The pseudomodule contains the following functions and objects (and their signatures, with a suffixed 'opt' indicating an optional argument): First, basic identification: **'VERSION'** -- A constant variable which contains a string representation of the EmPy version. **'SIGNIFICATOR_RE_STRING'** -- A constant variable representing a regular expression string (using the default prefix) that can be used to find significators in EmPy code. **'SIGNIFICATOR_RE_SUFFIX'** -- The portion of the significator regular expression string excluding the prefix, so that those using non-standard prefix can build their own custom regular expression string with 'myPrefix + empy.SIGNIFICATOR_RE_SUFFIX'. **'interpreter'** -- The instance of the interpreter that is currently being used to perform execution. *Note:* This is now obsolete; the pseudomodule is itself the interpreter. Instead of using 'empy.interpreter', simply use 'empy'. **'argv'** -- A list consisting of the name of the primary EmPy script and its command line arguments, in analogue to the 'sys.argv' list. **'args'** -- A list of the command line arguments following the primary EmPy script; this is equivalent to 'empy.argv[1:]'. **'identify() -> string, integer'** -- Retrieve identification information about the current parsing context. Returns a 2-tuple consisting of a filename and a line number; if the file is something other than from a physical file (*e.g.*, an explicit expansion with 'empy.expand', a file-like object within Python, or via the -E or -F command line options), a string representation is presented surrounded by angle brackets. Note that the context only applies to the *EmPy* context, not the Python context. **'atExit(callable)'** -- Register a callable object (such as a function) taking no arguments which will be called at the end of a normal shutdown. Callable objects registered in this way are called in the reverse order in which they are added, so the first callable registered with 'empy.atExit' is the last one to be called. Note that although the functionality is related to hooks, 'empy.atExit' does no work via the hook mechanism, and you are guaranteed that the interpreter and stdout will be in a consistent state when the callable is invoked. Context manipulation: **'pushContext(name_opt, line_opt)'** -- Create a new context with the given name and line and push it on the stack. **'popContext()'** -- Pop the top context and dispose of it. **'setContextName(name)'** -- Manually set the name of the current context. **'setContextLine(line)'** -- Manually set the line number of the current context; line must be a numeric value. Note that afterward the line number will increment by one for each newline that is encountered, as before. Globals manipulation: **'getGlobals()'** -- Retrieve the globals dictionary for this interpreter. Unlike when calling 'globals()' in Python, this dictionary *can* be manipulated and you *can* expect changes you make to it to be reflected in the interpreter that holds it. **'setGlobals(globals)'** -- Reseat the globals dictionary associated with this interpreter to the provided mapping type. **'updateGlobals(globals)'** -- Merge the given dictionary into this interpreter's globals. **'clearGlobals(globals_opt)'** -- Clear out the globals (restoring, of course, the 'empy' pseudomodule). Optionally, instead of starting with a refresh dictionary, use the dictionary provided. **'saveGlobals(deep=True)'** -- Save a copy of the globals onto an internal history stack from which it can be restored later. The optional 'deep' argument indicates whether or not the copying should be a deep copy (default) or a shallow one. Copying is done with 'copy.deepcopy' or 'copy.copy', respectively. **'restoreGlobals(destructive=True)'** -- Restore the most recently saved globals from the history stack to as the current globals for this instance. The optional 'destructive' argument indicates whether or not the restore should remove the restored globals from the history stack (default), or whether it should be left there for subsequent restores. Types: **'Interpreter'** -- The actual interpreter class. The following functions allow direct execution; optional 'locals' arguments, if specified, are treated as the locals dictionary in evaluation and execution: **'defined(name, locals_opt)'** -- Return true if the given name is defined either in the (optional) locals or the interpreter globals; return false otherwise. **'evaluate(expression, locals_opt)'** -- Evaluate the given expression. **'serialize(expression, locals_opt)'** -- Serialize the expression, just as the interpreter would: If it is not None, convert it to a string with the 'str' builtin function, and then write out the result. If it evaluates to None, do nothing. **'execute(statements, locals_opt)'** -- Execute the given statement(s). **'single(source, locals_opt)'** -- Interpret the "single" source code, just as the Python interactive interpreter would. **'import_(name, locals_opt)'** -- Import a module. **'atomic(name, value, locals_opt)'** -- Perform a single, atomic assignment. In this case name is the string denoating the name of the (single) variable to be assigned to, and value is a Python object which the name is to be bound to. **'assign(name, value, locals_opt)'** -- Perform general assignment. This decays to atomic assignment (above) in the normal case, but supports "tuple unpacking" in the sense that if name string contains commas, it is treated as a sequence of names and memberwise assignment with each member of the value (still a Python object, but which must be a sequence). This function will raise a 'TypeError' or 'ValueError' just like Python would if tuple unpacking is not possible (that is, if the value is not a sequence or is of an incompatible length, respectively). This only supports the assignment of Python identifiers, not arbitrary Python lvalues. **'significate(key, value_opt, locals_opt)'** -- Do a manual signification. If 'value' is not specified, it is treated as 'None'. The following functions relate to source manipulation: **'include(file_or_filename, locals_opt)'** -- Include another EmPy file, by processing it in place. The argument can either be a filename (which is then opened with 'open' in text mode) or a file object, which is used as is. Once the included file is processed, processing of the current file continues. Includes can be nested. The call also takes an optional locals dictionary which will be passed into the evaluation function. **'expand(string, locals_opt)' -> string** -- Explicitly invoke the EmPy parsing system to process the given string and return its expansion. This allows multiple levels of expansion, *e.g.*, '@(empy.expand("@(2 + 2)"))'. The call also takes an optional locals dictionary which will be passed into the evaluation function. This is necessary when text is being expanded inside a function definition and it is desired that the function arguments (or just plain local variables) are available to be referenced within the expansion. **'quote(string) -> string'** -- The inverse process of 'empy.expand', this will take a string and return a new string that, when expanded, would expand to the original string. In practice, this means that appearances of the prefix character are doubled, except when they appear inside a string literal. **'escape(string, more_opt) -> string'** -- Given a string, quote the nonprintable characters contained within it with EmPy escapes. The optional 'more' argument specifies additional characters that should be escaped. **'flush()'** -- Do an explicit flush on the underlying stream. **'string(string, name_opt, locals_opt)'** -- Explicitly process a string-like object. This differs from 'empy.expand' in that the string is directly processed into the EmPy system, rather than being evaluated in an isolated context and then returned as a string. Changing the behavior of the pseudomodule itself: **'flatten(keys_opt)'** -- Perform the equivalent of 'from empy import ...' in code (which is not directly possible because 'empy' is a pseudomodule). If keys is omitted, it is taken as being everything in the 'empy' pseudomodule. Each of the elements of this pseudomodule is flattened into the globals namespace; after a call to 'empy.flatten', they can be referred to simple as globals, *e.g.*, '@divert(3)' instead of '@empy.divert(3)'. If any preexisting variables are bound to these names, they are silently overridden. Doing this is tantamount to declaring an 'from ... import ...' which is often considered bad form in Python. Prefix-related functions: **'getPrefix() -> char'** -- Return the current prefix. **'setPrefix(char)'** -- Set a new prefix. Immediately after this call finishes, the prefix will be changed. Changing the prefix affects only the current interpreter; any other created interpreters are unaffected. Setting the prefix to None or the null string means that no further markups will be processed, equivalent to specifying the --no-prefix command line argument. Diversions: **'stopDiverting()'** -- Any diversions that are currently taking place are stopped; thereafter, output will go directly to the output file as normal. It is never illegal to call this function. **'createDiversion(name)'** -- Create a diversion, but do not begin diverting to it. This is the equivalent of starting a diversion and then immediately stopping diversion; it is used in cases where you want to make sure that a diversion will exist for future replaying but may be empty. **'startDiversion(name)'** -- Start diverting to the specified diversion name. If such a diversion does not already exist, it is created; if it does, then additional material will be appended to the preexisting diversions. **'playDiversion(name)'** -- Recall the specified diversion and then purge it. The provided diversion name must exist. **'replayDiversion(name)'** -- Recall the specified diversion without purging it. The provided diversion name must exist. **'purgeDiversion(name)'** -- Purge the specified diversion without recalling it. The provided diversion name must exist. **'playAllDiversions()'** -- Play (and purge) all existing diversions in the sorted order of their names. This call does an implicit 'empy.stopDiverting' before executing. **'replayAllDiversions()'** -- Replay (without purging) all existing diversions in the sorted order of their names. This call does an implicit 'empy.stopDiverting' before executing. **'purgeAllDiversions()'** -- Purge all existing diversions without recalling them. This call does an implicit 'empy.stopDiverting' before executing. **'getCurrentDiversion() -> diversion'** -- Return the name of the current diversion. **'getAllDiversions() -> sequence'** -- Return a sorted list of all existing diversions. Filters: **'getFilter() -> filter'** -- Retrieve the current filter. 'None' indicates no filter is installed. **'resetFilter()'** -- Reset the filter so that no filtering is done. **'nullFilter()'** -- Install a special null filter, one which consumes all text and never sends any text to the output. **'setFilter(shortcut)'** -- Install a new filter. A filter is 'None' or an empty sequence representing no filter, or '0' for a null filter, a function for a function filter, a string for a string filter, or an instance of 'empy.Filter' (or a workalike object). If filter is a list of the above things, they will be chained together manually; if it is only one, it will be presumed to be solitary or to have already been manually chained together. See the "Filters" section for more information. **'attachFilter(shortcut)'** -- Attach a single filter (sequences are not allowed here) to the end of a currently existing filter chain, or if there is no current chain, install it as 'empy.setFilter' would. As with 'empy.setFilter', the shortcut versions of filters are also allowed here. Hooks: **'areHooksEnabled()'** -- Return whether or not hooks are presently enabled. **'enableHooks()'** -- Enable invocation of hooks. By default hooks are enabled. **'disableHooks()'** -- Disable invocation of hooks. Hooks can still be added, removed, and queried, but invocation of hooks will not occur (even explicit invocation with 'empy.invokeHook'). **'getHooks()'** -- Get a (copy of the) list of the hooks currently registered. **'clearHooks()'** -- Clear all the hooks registered with this interpreter. **'addHook(hook, prepend_opt)'** -- Add this hook to the hooks associated with this interpreter. By default, the hook is appended to the end of the existing hooks, if any; if the optional insert argument is present and true, it will be prepended to the list instead. **'removeHook(hook)'** -- Remove this hook from the hooks associated with this interpreter. **'invokeHook(_name, ...)'** -- Manually invoke a hook method. The remaining arguments are treated as keyword arguments and the resulting dictionary is passed in as the second argument to the hooks. Custom markup callback: **'getCallback() -> callback'** -- Retrieve the current callback associated with this interpreter, or 'None' if it does not yet have one. **'registerCallback(callback)'** -- Register a callback to be called whenever a custom markup ('@<...>') is encountered. When encountered, 'invokeCallback' is called. **'deregisterCallback()'** -- Clear any callback previously registered with the interpreter for being called when a custom markup is encountered. **'invokeCallback(contents)'** -- Invoke a custom callback. This function is called whenever a custom markup ('@<...>') is encountered. It in turn calls the registered callback, with a single argument, 'contents', which is a string representing of the contents of the custom markup. Invocation Basic invocation involves running the interpreter on an EmPy file and some optional arguments. If no file are specified, or the file is named '-', EmPy takes its input from stdin. One can suppress option evaluation (to, say, specify a file that begins with a dash) by using the canonical '--' option. **'-h'/'--help'** -- Print usage and exit. **'-H'/'--extended-help'** -- Print extended usage and exit. Extended usage includes a rundown of all the legal expansions, escape sequences, pseudomodule contents, used hooks, and supported environment variables. **'-v'/'--verbose'** -- The EmPy system will print all manner of details about what it is doing and what it is processing to stderr. **'-V'/'--version'** -- Print version and exit. **'-a'/'--append' (filename)** -- Open the specified file for append instead of using stdout. **'-b'/'--buffered-output'** -- Fully buffer processing output, including the file open itself. This is helpful when, should an error occur, you wish that no output file be generated at all (for instance, when using EmPy in conjunction with make). When specified, either the -o or -a options must be specified, and the -b option must precede them. This can also be specified through the existence of the 'EMPY_BUFFERED_OUTPUT' environment variable. **'-f'/'--flatten'** -- Before processing, move the contents of the 'empy' pseudomodule into the globals, just as if 'empy.flatten()' were executed immediately after starting the interpreter. That is, *e.g.*, 'empy.include' can be referred to simply as 'include' when this flag is specified on the command line. This can also be specified through the existence of the 'EMPY_FLATTEN' environment variable. **'-i'/'--interactive'** -- After the main EmPy file has been processed, the state of the interpreter is left intact and further processing is done from stdin. This is analogous to the Python interpreter's -i option, which allows interactive inspection of the state of the system after a main module is executed. This behaves as expected when the main file is stdin itself. This can also be specified through the existence of the 'EMPY_INTERACTIVE' environment variable. **'-k'/'--suppress-errors'** -- Normally when an error is encountered, information about its location is printed and the EmPy interpreter exits. With this option, when an error is encountered (except for keyboard interrupts), processing stops and the interpreter enters interactive mode, so the state of affairs can be assessed. This is also helpful, for instance, when experimenting with EmPy in an interactive manner. -k implies -i. **'-n'/'--no-override-stdout'** -- Do not override 'sys.stdout' with a proxy object which the EmPy system interacts with. If suppressed, this means that side effect printing will not be captured and routed through the EmPy system. However, if this option is specified, EmPy can support multithreading. **'-o'/'--output' (filename)** -- Open the specified file for output instead of using stdout. If a file with that name already exists it is overwritten. **'-p'/'--prefix' (prefix)** -- Change the prefix used to detect expansions. The argument is the one-character string that will be used as the prefix. Note that whatever it is changed to, the way to represent the prefix literally is to double it, so if '$' is the prefix, a literal dollar sign is represented with '$$'. Note that if the prefix is changed to one of the secondary characters (those that immediately follow the prefix to indicate the type of action EmPy should take), it will not be possible to represent literal prefix characters by doubling them (*e.g.*, if the prefix were inadvisedly changed to '#' then '##' would already have to represent a comment, so '##' could not represent a literal '#'). This can also be specified through the 'EMPY_PREFIX' environment variable. **'-r'/'--raw-errors'** -- Normally, EmPy catches Python exceptions and prints them alongside an error notation indicating the EmPy context in which it occurred. This option causes EmPy to display the full Python traceback; this is sometimes helpful for debugging. This can also be specified through the existence of the 'EMPY_RAW_ERRORS' environment variable. **'-u'/'--unicode'** -- Enable the Unicode subsystem. This option only need be present if you wish to enable the Unicode subsystem with the defaults; any other Unicode-related option (starting with --unicode...) will also enable the Unicode subsystem. **'-D'/'--define' (assignment)** -- Execute a Python assignment of the form 'variable = expression'. If only a variable name is provided (*i.e.*, the statement does not contain an '=' sign), then it is taken as being assigned to None. The -D option is simply a specialized -E option that special cases the lack of an assignment operator. Multiple -D options can be specified. **'-E'/'--execute' (statement)** -- Execute the Python (not EmPy) statement before processing any files. Multiple -E options can be specified. **'-F'/'--execute-file' (filename)** -- Execute the Python (not EmPy) file before processing any files. This is equivalent to '-E execfile("filename")' but provides a more readable context. Multiple -F options can be specified. **'-I'/'--import' (module)** -- Imports the specified module name before processing any files. Multiple modules can be specified by separating them by commas, or by specifying multiple -I options. **'-P'/'--preprocess' (filename)** -- Process the EmPy file before processing the primary EmPy file on the command line. **'--binary'** -- Treat the file as a binary file, and read in chunks rather than line by line. In this mode, the "line" indicator represents the number of bytes read, not the number of lines processed. **'--no-prefix'** -- Disable the prefixing system entirely; when specified, EmPy will not expand any markups. This allows EmPy to merely act as a Unicode encoding translator.. **'--pause-at-end'** -- If present, then 'raw_input' will be called at the end of processing. Useful in systems where the output window would otherwise be closed by the operating system/window manager immediately after EmPy exited. **'--relative-path'** -- When present, the path the EmPy script being invoked is contained in will be prepended to 'sys.path'. This is analogous to Python's internal handling of 'sys.path' and scripts. If input is from stdin ('-' for a filename or no filename is specified), then nothing is added to the path. **'--no-callback-error'** -- Do not consider it an error if the custom markup is invoked '@<...>' and there is no callback function registered for it. **'--chunk-size' (chunk)** -- Use the specific binary chunk size rather than the default; implies --binary. **'--unicode-encoding' (encoding)** -- Specify the Unicode encoding to be used for both input and output. **'--unicode-input-encoding' (encoding)** -- Specify the Unicode encoding to be used for input. **'--unicode-output-encoding' (encoding)** -- Specify the Unicode encoding to be used for output. **'--unicode-input-errors (errors)** -- Specify the Unicode error handling to be used for input. **'--unicode-errors (errors)** -- Specify the Unicode error handling to be used for both input and output. **'--unicode-output-errors (errors)** -- Specify the Unicode error handling to be used for output. Environment variables EmPy also supports a few environment variables to predefine certain behaviors. The settings chosen by environment variables can be overridden via command line arguments. The following environment variables have meaning to EmPy: **'EMPY_OPTIONS'** -- If present, the contents of this environment variable will be treated as options, just as if they were entered on the command line, *before* the actual command line arguments are processed. Note that these arguments are *not* processed by the shell, so quoting, filename globbing, and the like, will not work. **'EMPY_PREFIX'** -- If present, the value of this environment variable represents the prefix that will be used; this is equivalent to the -p command line option. **'EMPY_PSEUDO'** -- If present, the value of this environment variable represents the name of the pseudomodule that will be incorporated into every running EmPy system; this is equivalent to the -m command line option. **'EMPY_FLATTEN'** -- If defined, this is equivalent to including -f on the command line. **'EMPY_RAW_ERRORS'** -- If defined, this is equivalent to including -r on the command line. **'EMPY_INTERACTIVE'** -- If defined, this is equivalent to including -i on the command line. **'EMPY_BUFFERED_OUTPUT'** -- If defined, this is equivalent to including -b on the command line. **'EMPY_UNICODE'** -- If defined, this is equivalent to including -u on the command line. **'EMPY_UNICODE_INPUT_ENCODING'** -- If present, the value of this environment variable indicates the name of the Unicode input encoding to be used. This is equivalent to the --unicode-input-encoding command line option. **'EMPY_UNICODE_OUTPUT_ENCODING'** -- If present, the value of this environment variable indicates the name of the Unicode output encoding to be used. This is equivalent to the --unicode-output-encoding command line option. **'EMPY_UNICODE_INPUT_ERRORS'** -- If present, the value of this environment variable indicates the name of the error handler to be used for input. This is equivalent to the --unicode-input-errors command line option. **'EMPY_UNICODE_OUTPUT_ERRORS'** -- If present, the value of this environment variable indicates the name of the error handler to be used for output. This is equivalent to the --unicode-output-errors command line option. Examples and testing EmPy See the sample EmPy file 'sample.em' which is included with the distribution. Run EmPy on it by typing something like:: ./em.py sample.em and compare the results and the sample source file side by side. The sample content is intended to be self-documenting, and even an introduction to the basic features of EmPy while simultaneously exercising them. The file 'sample.bench' is the benchmark output of the sample. Running the EmPy interpreter on the provided 'sample.em' file should produce precisely the same results. You can run the provided test script to see if your EmPy environment is behaving as expected (presuming a Unix-like operating system):: ./test.sh By default this will test with the first Python interpreter available in the path; if you want to test with another interpreter, you can provide it as the first argument on the command line, *e.g.*:: ./test.sh python2.1 ./test.sh /usr/bin/python1.5 ./test.sh jython A more comprehensive test suite and set of real-world examples is planned for a future version. Embedding EmPy For atomic applications, the 'expand' function is provided (the extra keyword arguments passed in are treated as locals):: import em print em.expand("@x + @y is @(x + y).", x=2, y=3) One can specify a globals dictionary and all the other interpreter options (below) as well. One can specify a globals dictionary that will be used if one wants persistence:: import em g = {} em.expand("@{x = 10}", g) print em.expand("x is @x.", g) The standalone 'expand' function, however, creates and destroys an 'Interpreter' instance each time it is called. For repeated expansions, this can be expensive. Instead, you will probably want to use the full-fledged features of embedding. An EmPy interpreter can be created with as code as simple as:: import em interpreter = em.Interpreter() # The following prints the results to stdout: interpreter.string("@{x = 123}@x\n") # This expands to the same thing, but puts the results as a # string in the variable result: result = interpreter.expand("@{x = 123}@x\n") # This just prints the value of x directly: print interpreter.globals['x'] # Process an actual file (and output to stdout): interpreter.file(open('/path/to/some/file')) interpreter.shutdown() # this is important; see below One can capture the output of a run in something other than stdout by specifying the *output* parameter:: import em, StringIO output = StringIO.StringIO() interpreter = em.Interpreter(output=output) # Do something. interpreter.file(open('/path/to/some/file')) interpreter.shutdown() # again, this is important; see below print output.getvalue() # this is the result from the session When you are finished with your interpreter, it is important to call its shutdown method:: interpreter.shutdown() This will ensure that the interpreter cleans up all its overhead, entries in the 'sys.stdout' proxy, and so forth. It is usually advisable that this be used in a try...finally clause:: interpreter = em.Interpreter(...) try: ... finally: interpreter.shutdown() The 'em.Interpreter' constructor takes the following arguments; all are optional. Since options may be added in the future, it is highly recommended that the constructor be invoked via keyword arguments, rather than assuming their order. The arguments are: *output* -- The output file which the interpreter will be sending all its processed data to. This need only be a file-like object; it need not be an actual file. If omitted, 'sys.__stdout__' is used. *argv* -- An argument list analogous to 'sys.argv', consisting of the script name and zero or more arguments. These are available to executing interpreters via 'empy.argv' and 'empy.args'. If omitted, a non-descript script name is used with no arguments. *prefix* -- The prefix (a single-character string). Defaults to '@'. It is an error for this to be anything other than one character. *pseudo* -- The name (string) of the pseudmodule. Defaults to 'empy'. *options* -- A dictionary of options that can override the default behavior of the interpreter. The names of the options are constant names ending in '_OPT' and their defaults are given in 'Interpreter.DEFAULT_OPTIONS'. *globals* -- By default, interpreters begin with a pristine dictionary of globals (except, of course, for the 'empy' pseudomodule). Specifying this argument will allow the globals to start with more. *hooks* -- A sequence of hooks (or 'None' for none) to register with the interpreter at startup. Hooks can, of course, be added after the fact, but this allows the hooks to intercept the 'atStartup' event (otherwise, the startup event would already have occurred by the time new hooks could be registered).. Many things can be done with EmPy interpreters; for the full developer documentation, see the generated documentation for the 'em' module. Interpreter options The following options (passed in as part of the options dictionary to the Interpreter constructor) have the following meanings. The defaults are shown below and are also indicated in an 'Interpreter.DEFAULT_OPTIONS' dictionary. **'BANGPATH_OPT'** -- Should a bangpath ('#!') as the first line of an EmPy file be treated as if it were an EmPy comment? Note that '#!' sequences starting lines or appearing anywhere else in the file are untouched regardless of the value of this option. Default: true. **'BUFFERED_OPT'** -- Should an 'abort' method be called upon failure? This relates to the fully-buffered option, where all output can be buffered including the file open; this option only relates to the interpreter's behavior *after* that proxy file object has been created. Default: false. **'RAW_OPT'** -- Should errors be displayed as raw Python errors (that is, the exception is allowed to propagate through to the toplevel so that the user gets a standard Python traceback)? Default: false. **'EXIT_OPT'** -- Upon an error, should execution continue (although the interpreter stacks will be purged)? Note that even in the event this is set, the interpreter will halt upon receiving a 'KeyboardInterrupt'. Default: true. **'FLATTEN_OPT'** -- Upon initial startup, should the 'empy' pseudomodule namespace be flattened, *i.e.*, should 'empy.flatten' be called? Note this option only has an effect when the interpreter is first created; thereafter it is ignored. Default: false. **'OVERRIDE_OPT'** -- Should the 'sys.stdout' object be overridden with a proxy object? If not, side effect output cannot be captured by the EmPy system, but EmPy will support multithreading. Default: true. **'CALLBACK_OPT'** -- If a callback is invoked when none has yet been registered, should an error be raised or should the situation be ignored? Default: true. Data flow **input -> interpreter -> diversions -> filters -> output** Here, in summary, is how data flows through a working EmPy system: 1. Input comes from a source, such an .em file on the command line, or via an 'empy.include' statement. 2. The interpreter processes this material as it comes in, expanding EmPy expansions as it goes. 3. After interpretation, data is then sent through the diversion layer, which may allow it directly through (if no diversion is in progress) or defer it temporarily. Diversions that are recalled initiate from this point. 4. Any filters in place are then used to filter the data and produce filtered data as output. 5. Finally, any material surviving this far is sent to the output stream. That stream is stdout by default, but can be changed with the -o or -a options, or may be fully buffered with the -b option (that is, the output file would not even be opened until the entire system is finished). Author's notes I originally conceived EmPy as a replacement for my "Web templating system", http://www.alcyone.com/max/info/m4.html which uses "m4", http://www.seindal.dk/rene/gnu/ (a general macroprocessing system for Unix). Most of my Web sites include a variety of m4 files, some of which are dynamically generated from databases, which are then scanned by a cataloging tool to organize them hierarchically (so that, say, a particular m4 file can understand where it is in the hierarchy, or what the titles of files related to it are without duplicating information); the results of the catalog are then written in database form as an m4 file (which every other m4 file implicitly includes), and then GNU make converts each m4 to an HTML file by processing it. As the Web sites got more complicated, the use of m4 (which I had originally enjoyed for the challenge and abstractness) really started to become an impediment to serious work; while I am very knowledgeable about m4 -- having used it for for so many years -- getting even simple things done with it is awkward and difficult. Worse yet, as I started to use Python more and more over the years, the cataloging programs which scanned the m4 and built m4 databases were migrated to Python and made almost trivial, but writing out huge awkward tables of m4 definitions simply to make them accessible in other m4 scripts started to become almost farcical -- especially when coupled with the difficulty in getting simple things done in m4. It occurred to me what I really wanted was an all-Python solution. But replacing what used to be the m4 files with standalone Python programs would result in somewhat awkward programs normally consisting mostly of unprocessed text punctuated by small portions where variables and small amounts of code need to be substituted. Thus the idea was a sort of inverse of a Python interpreter: a program that normally would just pass text through unmolested, but when it found a special signifier would execute Python code in a normal environment. I looked at existing Python templating systems, and didn't find anything that appealed to me -- I wanted something where the desired markups were simple and unobtrusive. After considering between choices of signifiers, I settled on '@' and EmPy was born. As I developed the tool, I realized it could have general appeal, even to those with widely varying problems to solve, provided the core tool they needed was an interpreter that could embed Python code inside templated text. As I continue to use the tool, I have been adding features as unintrusively as possible as I see areas that can be improved. A design goal of EmPy is that its feature set should work on several levels; at each level, if the user does not wish or need to use features from another level, they are under no obligation to do so. If you have no need of diversions, for instance, you are under no obligation to use them. If significators will not help you organize a set of EmPy scripts globally, then you need not use them. New features that are being added are whenever possible transparently backward compatible; if you do not need them, their introduction should not affect you in any way. The use of unknown prefix sequences results in errors, guaranteeing that they are reserved for future use. Glossary **control** -- A control markup, used to direct high-level control flow within an EmPy session. Control markups are expressed with the '@[...]' notation. **diversion** -- A process by which output is deferred, and can be recalled later on demand, multiple times if necessary. **document** -- The abstraction of an EmPy document as used by a processor. **escape** -- A markup designed to expand to a single (usually non-printable) character, similar to escape sequences in C or other languages. **expansion** -- The process of processing EmPy markups and producing output. **expression** -- An expression markup represents a Python expression to be evaluated, and replaced with the 'str' of its value. Expression markups are expressed with the '@(...)' notation. **filter** -- A file-like object which can be chained to other objects (primarily the final stream) and can buffer, alter, or manipulate in any way the data sent. Filters can also be chained together in arbitrary order. **globals** -- The dictionary (or dictionary-like object) which resides inside the interpreter and holds the currently-defined variables. **hook** -- A callable object that can be registered in a dictionary, and which will be invoked before, during, or after certain internal operations, identified by name with a string. **interpreter** -- The application (or class instance) which processes EmPy markup. **markup** -- EmPy substitutions set off with a prefix and appropriate delimeters. **output** -- The final destination of the result of processing an EmPy file. **prefix** -- The ASCII character used to set off an expansions. By default, '@'. **processor** -- An extensible system which processes a group of EmPy files, usually arranged in a filesystem, and scans them for significators. **pseudomodule** -- The module-like object named 'empy' which is exposed internally inside every EmPy system. **shortcut** -- A special object which takes the place of an instance of the 'Filter' class, to represent a special form of filter. These include 0 for a null filter, a callable (function or lambda) to represent a callable filter, or a 256-character string which represents a translation filter. **significator** -- A special form of an assignment markup in EmPy which can be easily parsed externally, primarily designed for representing uniform assignment across a collection of files. Significators are indicated with the '@%' markup. **statement** -- A line of code that needs to be executed; statements do not have return values. In EmPy, statements are set off with '@{...}'. Acknowledgements Questions, suggestions, bug reports, evangelism, and even complaints from many people have helped make EmPy what it is today. Some, but by no means all, of these people are (in alphabetical order by surname): - Biswapesh Chattopadhyay - Beni Cherniavsky - Dr. S. Candelaria de Ram - Eric Eide - Dinu Gherman - Grzegorz Adam Hankiewicz - Bohdan Kushnir - Robert Kroeger - Kouichi Takahashi - Ville Vainio Known issues and caveats - EmPy was primarily intended for static processing of documents, rather than dynamic use, and hence speed of processing was not the primary consideration in its design. - EmPy is not threadsafe by default. This is because of the need for EmPy to override the 'sys.stdout' file with a proxy object which can capture effects of 'print' and other spooling to stdout. This proxy can be suppressed with the -n option, which will result in EmPy being unable to do anything meaningful with this output, but will allow EmPy to be threadsafe. - To function properly, EmPy must override 'sys.stdout' with a proxy file object, so that it can capture output of side effects and support diversions for each interpreter instance. It is important that code executed in an environment *not* rebind 'sys.stdout', although it is perfectly legal to invoke it explicitly (*e.g.*, '@sys.stdout.write("Hello world\n")'). If one really needs to access the "true" stdout, then use 'sys.__stdout__' instead (which should also not be rebound). EmPy uses the standard Python error handlers when exceptions are raised in EmPy code, which print to 'sys.stderr'. - Due to Python's curious handling of the 'print' statement -- particularly the form with a trailing comma to suppress the final newline -- mixing statement expansions using prints inline with unexpanded text will often result in surprising behavior, such as extraneous (sometimes even deferred!) spaces. This is a Python "feature," and occurs in non-EmPy applications as well; for finer control over output formatting, use 'sys.stdout.write' or 'empy.interpreter.write' directly. - The 'empy' "module" exposed through the EmPy interface (*e.g.*, '@empy') is an artificial module. It cannot be imported with the 'import' statement (and shouldn't -- it is an artifact of the EmPy processing system and does not correspond to any accessible .py file). - For an EmPy statement expansion all alone on a line, *e.g.*, '@{a = 1}', note that this will expand to a blank line due to the newline following the closing curly brace. To suppress this blank line, use the symmetric convention '@{a = 1}@'. - When using EmPy with make, note that partial output may be created before an error occurs; this is a standard caveat when using make. To avoid this, write to a temporary file and move when complete, delete the file in case of an error, use the -b option to fully buffer output (including the open), or (with GNU make) define a '.DELETE_ON_ERROR' target. - 'empy.identify' tracks the context of executed *EmPy* code, not Python code. This means that blocks of code delimited with '@{' and '}' will identify themselves as appearing on the line at which the '}' appears, and that pure Python code executed via the -D, -E and -F command line arguments will show up as all taking place on line 1. If you're tracking errors and want more information about the location of the errors from the Python code, use the -r command line option, which will provide you with the full Python traceback. - The conditional form of expression expansion '@(...?...!...)' allows the use of a colon instead of an exclamation point, *e.g.*, '@(...?...:...)'. This behavior is supported for backward compatibility, but is deprecated. Due to an oversight, the colon was a poor choice since colons can appear legally in expressions (*e.g.*, dictionary literals or lambda expressions). - The '@[try]' construct only works with Python exceptions derived from 'Exception'. It is not able to catch string exceptions. - The '@[for]' variable specification supports tuples for tuple unpacking, even recursive tuples. However, it is limited in that the names included may only be valid Python identifiers, not arbitrary Python lvalues. Since the internal Python mechanism is very rarely used for this purpose (*e.g.*, 'for (x, l[0], q.a) in sequence'), it is not thought to be a significant limitation. Wish list Here are some random ideas for future revisions of EmPy. If any of these are of particular interest to you, your input would be appreciated. - Some real-world examples should really be included for demonstrating the power and expressiveness of EmPy first-hand. - More extensive help (rather than a ridiculously long README), probably inherently using the EmPy system itself for building to HTML and other formats, thereby acting as a help facility and a demonstration of the working system. - A "trivial" mode, where all the EmPy system does is scan for simple symbols to replace them with evaluations/executions, rather than having to do the contextual scanning it does now. This has the down side of being much less configurable and powerful but the upside of being extremely efficient. - A "debug" mode, where EmPy prints the contents of everything it's about to evaluate (probably to stderr) before it does? - The ability to funnel all code through a configurable 'RExec' for user-controlled security control. This would probably involve abstracting the execution functionality outside of the interpreter. [This suggestion is on hold until the rexec/Bastion exploits are worked out.] - Optimized handling of processing would be nice for the possibility of an Apache module devoted to EmPy processing. - An EmPy emacs mode. - An optimization of offloading diversions to files when they become truly huge. (This is made possible by the abstraction of the 'Diversion' class.) - Support for mapping filters (specified by dictionaries). - Support for some sort of batch processing, where several EmPy files can be listed at once and all of them evaluated with the same initial (presumably expensive) environment. 'empy.saveGlobals' and 'empy.restoreGlobals' have been introduced as a partial solution, but they need to be made more robust. - A more elaborate interactive mode, perhaps with a prompt and readline support. - A StructuredText and/or reStructuredText filter would be quite useful, as would SGML/HTML/XML/XHTML, s-expression, Python, etc. auto-indenter filters. - An indexing filter, which can process text and pick out predefined keywords and thereby setup links to them. - The ability to rerun diverted material back through the interpreter. (This can be done, awkwardly, by manually creating a filter which itself contains an interpreter, but it might be helpful if this was an all-in-one operation.) - A caching system that stores off the compilations of repeated evaluations and executions so that in a persistent environment the same code does not have to be repeatedly evaluated/executed. This would probably be a necessity in an Apache module-based solution. Perhaps caching even to the point of generating pure PyWM bytecode? - An option to change the format of the standard EmPy errors in a traceback. - Support for some manner of implicitly processed /etc/empyrc and/or ~/.empyrc file, and of course an option to inhibit its processing. This can already be accomplished (and with greater control) via use of EMPY_OPTIONS, though. - More uniform handling of the preprocessing directives (-I, -D, -E, -F, and -P), probably mapping directly to methods in the 'Interpreter' class. - Support for integration with mod_python. - In simple expressions, a '{...}' suffix has no meaning in Python (*e.g.*, in Python, '@x(...)' is a call, '@x[...]' is subscription, but '@x{...}' is illegal). This could be exploited by having a '{...}' suffix in a simple expression representing an encapsulation of an expanded string; *e.g.*, '@bullet{There are @count people here}' would be equivalent to '@bullet(empy.expand("There are @count people here", locals()))}'. - A tool to collect significator information from a hierarchy of .em files and put them in a database form available for individual scripts would be extremely useful -- this tool should be extensible so that users can use it to, say, build ordered hierarchies of their EmPy files by detecting contextual information like application-specific links to other EmPy documents. - Extensions of the basic EmPy concepts to projects for other interpreted languages, such as Java, Lua, Ruby, and/or Perl. - Ignore 'SystemExit' when doing error handling, letting the exception progagate up? So far no one seems to worry about this; deliberately exiting early in a template seems to be an unlikely occurrence. (Furthermore, there are the 'os.abort' and 'os._exit' facilities for terminating without exception propagation.) - A new markup which is the equivalent of '$...:...$' in source control systems, where the left-hand portion represents a keyword and the right-hand portion represents its value which is substituted in by the EmPy system. - The ability to obtain the filename (if relevant) and mode of the primary output file. - The ability to redirect multiple streams of output; not diversions, but rather the ability to write to one file and then another. Since output would be under the EmPy script's control, this would imply a useful --no-output option, where by default no output is written. This would also suggest the usefulness of all the output file delegates (diversions, filters, abstract files, etc.) passing unrecognized method calls all the way down to underlying file object. - In addition to the em.py script, an additional support library (non-executable) should be included which includes ancillary functionality for more advanced features, but which is not necessary to use EmPy in its basic form as a standalone executable. Such features would include things like significator processing, metadata scanning, and advanced prompting systems. Release history - 3.3; 2003 Oct 27. Custom markup '@<...>'; remove separate pseudomodule instance for greater transparency; deprecate 'interpreter' attribute of pseudomodule; deprecate auxiliary class name attributes associated with pseudomodule in preparation for separate support library in 4.0; add --no-callback-error and --no-bangpath-processing command line options; add 'atToken' hook. - 3.2; 2003 Oct 7. Reengineer hooks support to use hook instances; add -v option; add --relative-path option; reversed PEP 317 style; modify Unicode support to give less confusing errors in the case of unknown encodings and error handlers; relicensed under LGPL. - 3.1.1; 2003 Sep 20. Add literal '@"..."' markup; add --pause-at-end command line option; fix improper globals collision error via the 'sys.stdout' proxy. - 3.1; 2003 Aug 8. Unicode support (Python 2.0 and above); add Document and Processor helper classes for processing significators; add --no-prefix option for suppressing all markups. - 3.0.4; 2003 Aug 7. Implement somewhat more robust lvalue parsing for '@[for]' construct (thanks to Beni Cherniavsky for inspiration). - 3.0.3; 2003 Jul 9. Fix bug regarding recursive tuple unpacking using '@[for]'; add 'empy.saveGlobals', 'empy.restoreGlobals', and 'empy.defined' functions. - 3.0.2; 2003 Jun 19. '@?' and '@!' markups for changing the current context name and line, respectively; add 'update' method to interpreter; new and renamed context operations, 'empy.setContextName', 'empy.setContextLine', 'empy.pushContext', 'empy.popContext'. - 3.0.1; 2003 Jun 9. Fix simple bug preventing command line preprocessing directives (-I, -D, -E, -F, -P) from executing properly; defensive PEP 317 compliance [defunct]. - 3.0; 2003 Jun 1. Control markups with '@[...]'; remove substitutions (use control markups instead); support '@(...?...!...)' for conditional expressions in addition to the now-deprecated '@(...?...:...)' variety; add acknowledgements and glossary sections to documentation; rename buffering option back to -b; add -m option and 'EMPY_PSEUDO' environment variable for changing the pseudomodule name; add -n option and 'EMPY_NO_OVERRIDE' environment variable for suppressing 'sys.stdout' proxy; rename main error class to 'Error'; add standalone 'expand' function; add --binary and --chunk-size options; reengineer parsing system to use Tokens for easy extensibility; safeguard curly braces in simple expressions (meaningless in Python and thus likely a typographical error) by making them a parse error; fix bug involving custom Interpreter instances ignoring globals argument; distutils support. - 2.3; 2003 Feb 20. Proper and full support for concurrent and recursive interpreters; protection from closing the true stdout file object; detect edge cases of interpreter globals or 'sys.stdout' proxy collisions; add globals manipulation functions 'empy.getGlobals', 'empy.setGlobals', and 'empy.updateGlobals' which properly preserve the 'empy' pseudomodule; separate usage info out into easily accessible lists for easier presentation; have -h option show simple usage and -H show extened usage; add 'NullFile' utility class. - 2.2.6; 2003 Jan 30. Fix a bug in the 'Filter.detach' method (which would not normally be called anyway). - 2.2.5; 2003 Jan 9. Strip carriage returns out of executed code blocks for DOS/Windows compatibility. - 2.2.4; 2002 Dec 23. Abstract Filter interface to use methods only; add '@[noop: ...]' substitution for completeness and block commenting [defunct]. - 2.2.3; 2002 Dec 16. Support compatibility with Jython by working around a minor difference between CPython and Jython in string splitting. - 2.2.2; 2002 Dec 14. Include better docstrings for pseudomodule functions; segue to a dictionary-based options system for interpreters; add 'empy.clearAllHooks' and 'empy.clearGlobals'; include a short documentation section on embedding interpreters; fix a bug in significator regular expression. - 2.2.1; 2002 Nov 30. Tweak test script to avoid writing unnecessary temporary file; add 'Interpreter.single' method; expose 'evaluate', 'execute', 'substitute' [defunct], and 'single' methods to the pseudomodule; add (rather obvious) 'EMPY_OPTIONS' environment variable support; add 'empy.enableHooks' and 'empy.disableHooks'; include optimization to transparently disable hooks until they are actually used. - 2.2; 2002 Nov 21. Switched to -V option for version information; 'empy.createDiversion' for creating initially empty diversion; direct access to diversion objects with 'empy.retrieveDiversion'; environment variable support; removed --raw long argument (use --raw-errors instead); added quaternary escape code (well, why not). - 2.1; 2002 Oct 18. 'empy.atExit' registry separate from hooks to allow for normal interpreter support; include a benchmark sample and test.sh verification script; expose 'empy.string' directly; -D option for explicit defines on command line; remove ill-conceived support for '@else:' separator in '@[if ...]' substitution [defunct] ; handle nested substitutions properly [defunct] ; '@[macro ...]' substitution for creating recallable expansions [defunct]. - 2.0.1; 2002 Oct 8. Fix missing usage information; fix after_evaluate hook not getting called; add 'empy.atExit' call to register values. - 2.0; 2002 Sep 30. Parsing system completely revamped and simplified, eliminating a whole class of context-related bugs; builtin support for buffered filters; support for registering hooks; support for command line arguments; interactive mode with -i; significator value extended to be any valid Python expression. - 1.5.1; 2002 Sep 24. Allow '@]' to represent unbalanced close brackets in '@[...]' markups [defunct]. - 1.5; 2002 Sep 18. Escape codes ('@\...'); conditional and repeated expansion substitutions [defunct] ; replaced with control markups]; fix a few bugs involving files which do not end in newlines. - 1.4; 2002 Sep 7. Fix bug with triple quotes; collapse conditional and protected expression syntaxes into the single generalized '@(...)' notation; 'empy.setName' and 'empy.setLine' functions [deprecated] ; true support for multiple concurrent interpreters with improved sys.stdout proxy; proper support for 'empy.expand' to return a string evaluated in a subinterpreter as intended; merged Context and Parser classes together, and separated out Scanner functionality. - 1.3; 2002 Aug 24. Pseudomodule as true instance; move toward more verbose (and clear) pseudomodule functions; fleshed out diversion model; filters; conditional expressions; protected expressions; preprocessing with -P (in preparation for possible support for command line arguments). - 1.2; 2002 Aug 16. Treat bangpaths as comments; 'empy.quote' for the opposite process of 'empy.expand'; significators ('@%...' sequences); -I option; -f option; much improved documentation. - 1.1.5; 2002 Aug 15. Add a separate 'invoke' function that can be called multiple times with arguments to simulate multiple runs. - 1.1.4; 2002 Aug 12. Handle strings thrown as exceptions properly; use getopt to process command line arguments; cleanup file buffering with AbstractFile; very slight documentation and code cleanup. - 1.1.3; 2002 Aug 9. Support for changing the prefix from within the 'empy' pseudomodule. - 1.1.2; 2002 Aug 5. Renamed buffering option [defunct], added -F option for interpreting Python files from the command line, fixed improper handling of exceptions from command line options (-E, -F). - 1.1.1; 2002 Aug 4. Typo bugfixes; documentation clarification. - 1.1; 2002 Aug 4. Added option for fully buffering output (including file opens), executing commands through the command line; some documentation errors fixed. - 1.0; 2002 Jul 23. Renamed project to EmPy. Documentation and sample tweaks; added 'empy.flatten'. Added -a option. - 0.3; 2002 Apr 14. Extended "simple expression" syntax, interpreter abstraction, proper context handling, better error handling, explicit file inclusion, extended samples. - 0.2; 2002 Apr 13. Bugfixes, support non-expansion of Nones, allow choice of alternate prefix. - 0.1.1; 2002 Apr 12. Bugfixes, support for Python 1.5.x, add -r option. - 0.1; 2002 Apr 12. Initial early access release. Author This module was written by "Erik Max Francis", http://www.alcyone.com/max/. If you use this software, have suggestions for future releases, or bug reports, "I'd love to hear about it", mailto:software@alcyone.com. Even if you try out EmPy for a project and find it unsuitable, I'd like to know what stumbling blocks you ran into so they can potentially be addressed in a future version. Version Version 3.3 $Date: 2003/10/27 $ $Author: max $ empy-3.3.orig/setup.py0000644000175000017500000000244307727233166013102 0ustar anaana#!/usr/bin/env python # # $Id: //projects/empy/setup.py.pre#2 $ $Date: 2003/09/07 $ from distutils.core import setup DESCRIPTION = "A templating system for Python." LONG_DESCRIPTION = """\ EmPy is a system for embedding Python expressions and statements in template text; it takes an EmPy source file, processes it, and produces output. This is accomplished via expansions, which are special signals to the EmPy system and are set off by a special prefix (by default the at sign, '@'). EmPy can expand arbitrary Python expressions and statements in this way, as well as a variety of special forms. Textual data not explicitly delimited in this way is sent unaffected to the output, allowing Python to be used in effect as a markup language. Also supported are "hook" callbacks, recording and playback via diversions, and dynamic, chainable filters. The system is highly configurable via command line options and embedded commands. """ setup( name="empy", version="3.1", author="Erik Max Francis", author_email="software@alcyone.com", url="http://www.alcyone.com/software/empy", license="%LICENSE", py_modules=["em"], platforms=["unix", "linux", "win32"], description=DESCRIPTION, long_description=LONG_DESCRIPTION, ) empy-3.3.orig/doc/0000755000175000017500000000000007747157077012142 5ustar anaanaempy-3.3.orig/doc/index.html0000644000175000017500000036277407747162555014157 0ustar anaana empy

Table of Contents

empy  

Summary

A powerful and robust templating system for Python.

Overview

EmPy is a system for embedding Python expressions and statements in template text; it takes an EmPy source file, processes it, and produces output. This is accomplished via expansions, which are special signals to the EmPy system and are set off by a special prefix (by default the at sign, @). EmPy can expand arbitrary Python expressions and statements in this way, as well as a variety of special forms. Textual data not explicitly delimited in this way is sent unaffected to the output, allowing Python to be used in effect as a markup language. Also supported are callbacks via hooks, recording and playback via diversions, and dynamic, chainable filters. The system is highly configurable via command line options and embedded commands.

Expressions are embedded in text with the @(...) notation; variations include conditional expressions with @(...?...!...) and the ability to handle thrown exceptions with @(...$...). As a shortcut, simple variables and expressions can be abbreviated as @variable, @object.attribute, @function(arguments), @sequence [index], and combinations. Full-fledged statements are embedded with @{...}. Control flow in terms of conditional or repeated expansion is available with @[...]. A @ followed by a whitespace character (including a newline) expands to nothing, allowing string concatenations and line continuations. Comments are indicated with @# and consume the rest of the line, up to and including the trailing newline. @% indicate "significators," which are special forms of variable assignment intended to specify per-file identification information in a format which is easy to parse externally. Context name and line number changes can be done with @? and @! respectively. '@<...>' markups are customizeable by the user and can be used for any desired purpose. Escape sequences analogous to those in C can be specified with @\..., and finally a @@ sequence expands to a single literal at sign.

Getting the software

The current version of empy is 3.3.

The latest version of the software is available in a tarball here: http://www.alcyone.com/software/empy/empy-latest.tar.gz.

The official URL for this Web site is http://www.alcyone.com/software/empy/.

Requirements

EmPy should work with any version of Python from 1.5.2 onward. It has been tested with all major versions of CPython from 1.5 up, and Jython from 2.0 up (using Java runtimes 1.3 and 1.4). The included test script is intended to run on Unix-like systems with a Bourne shell.

License

This code is released under the LGPL.

Mailing lists

There are two EmPy related mailing lists available. The first is a receive-only, very low volume list for important announcements (including releases). To subscribe, send an email to empy-announce-list-subscribe@alcyone.com.

The second is a general discussion list for topics related to EmPy, and is open for everyone to contribute; announcements related to EmPy will also be made on this list. The author of EmPy (and any future developers) will also be on the list, so it can be used not only to discuss EmPy features with other users, but also to ask questions of the author(s). To subscribe, send an email to empy-list-subscribe@alcyone.com.

Basics

EmPy is intended for embedding Python code in otherwise unprocessed text. Source files are processed, and the results are written to an output file. Normal text is sent to the output unchanged, but markups are processed, expanded to their results, and then written to the output file as strings (that is, with the str function, not repr). The act of processing EmPy source and handling markups is called "expansion."

Code that is processed is executed exactly as if it were entered into the Python interpreter; that is, it is executed with the equivalent of eval (for expressions) and exec (for statements). EmPy is intended to be a very thin (though powerful) layer on top of a running Python system; Python and EmPy files can be mixed together (via command line options) without complications.

By default the embedding prefix is the at sign (@), which appears neither in valid Python code nor commonly in arbitrary texts; it can be overridden with the -p option (or with the empy.setPrefix function). The prefix indicates to the EmPy interpreter that a special sequence follows and should be processed rather than sent to the output untouched (to indicate a literal at sign, it can be doubled as in @@).

When the interpreter starts processing its target file, no modules are imported by default, save the empy pseudomodule (see below), which is placed in the globals; the empy pseudomodule is associated with a particular interpreter -- in fact, they are the same object -- and it is important that it not be removed from that interpreter's globals, nor that it be shared with other interpreters running concurrently (a name other than empy can be specified with the -m option). The globals are not cleared or reset in any way. It is perfectly legal to set variables or explicitly import modules and then use them in later markups, e.g., @{import time} ... @time.time(). Scoping rules are as in normal Python, although all defined variables and objects are taken to be in the global namespace.

Indentation is significant in Python, and therefore is also significant in EmPy. EmPy statement markups (@{...}), when spanning multiple lines, must be flush with the left margin. This is because (multiline) statement markups are not treated specially in EmPy and are simply passed to the Python interpreter, where indentation is significant.

Activities you would like to be done before any processing of the main EmPy file can be specified with the -I, -D, -E, -F, and -P options. -I imports modules, -D executes a Python variable assignment, -E executes an arbitrary Python (not EmPy) statement, -F executes a Python (not EmPy) file, and -P processes an EmPy (not Python) file. These operations are done in the order they appear on the command line; any number of each (including, of course, zero) can be used.

Expansions

The following markups are supported. For concreteness below, @ is taken for the sake of argument to be the prefix character, although this can be changed.

@# COMMENT NEWLINE
A comment. Comments, including the trailing newline, are stripped out completely. Comments should only be present outside of expansions. The comment itself is not processed in any way: It is completely discarded. This allows @# comments to be used to disable markups. Note: As special support for "bangpaths" in Unix-like operating systems, if the first line of a file (or indeed any context) begins with #!, and the interpreter has a processBangpaths option set to true (default), it is treated as a @# comment. A #! sequence appearing anywhere else will be handled literally and unaltered in the expansion. Example:
          @# This line is a comment.
          @# This will NOT be expanded: @x.
@? NAME NEWLINE
Set the name of the current context to be the given string. Variables are not allowed here; the name is treated as a literal. (If you wish to use arbitrary expressions, use the empy.setContextName function instead.) Example:
          @?NewName
          The context name is now @empy.identify()[0] (NewName).
@! INTEGER NEWLINE
Set the line number of the current context to be the given integer value; this is similar to the #line C preprocessor directive. This is done in such a way that the next line will have the specified numeric value, not the current one. Expressions are not allowed here; the number must be a literal integer. (If you wish to use arbitrary expressions, use the empy.setContextLine function instead.) Example:
          @!100
          The context line is now @empy.identify()[1] (100).
@ WHITESPACE
A @ followed by one whitespace character (a space, horizontal tab, vertical tab, carriage return, or newline) is expanded to nothing; it serves as a way to explicitly separate two elements which might otherwise be interpreted as being the same symbol (such as @name@ s to mean @(name)s -- see below). Also, since a newline qualifies as whitespace here, the lone @ at the end of a line represents a line continuation, similar to the backslash in other languages. Coupled with statement expansion below, spurious newlines can be eliminated in statement expansions by use of the @{...}@ construct. Example:
          This will appear as one word: salt@ water.
          This is a line continuation; @
          this text will appear on the same line.
@\ ESCAPE_CODE
An escape code. Escape codes in EmPy are similar to C-style escape codes, although they all begin with the prefix character. Valid escape codes include:
@\0
NUL, null
@\a
BEL, bell
@\b
BS, backspace
@\d
three-digital decimal code DDD
@\e
ESC, escape
@\f
FF, form feed
@\h
DEL, delete
@\n
LF, linefeed character, newline
@\oOOO
three-digit octal code OOO
@\qQQQQ
four-digit quaternary code QQQQ
@\r
CR, carriage return
@\s
SP, space
@\t
HT, horizontal tab
@\v
VT, vertical tab
@\xHH
two-digit hexadecimal code HH
@\z
EOT, end of transmission
@^X
the control character ^X

Unlike in C-style escape codes, escape codes taking some number of digits afterward always take the same number to prevent ambiguities. Furthermore, unknown escape codes are treated as parse errors to discourage potential subtle mistakes. Note that, while @\0 represents the NUL character, to represent an octal code, one must use @\o..., in contrast to C. Example:

          This embeds a newline.@\nThis is on the following line.
          This beeps!@\a
          There is a tab here:@\tSee?
          This is the character with octal code 141: @\o141.

@@
A literal at sign (@). To embed two adjacent at signs, use @@@@, and so on. Any literal at sign that you wish to appear in your text must be written this way, so that it will not be processed by the system. Note: If a prefix other than @ has been chosen via the command line option, one expresses that literal prefix by doubling it, not by appending a @. Example:
          The prefix character is @@.
          To get the expansion of x you would write @@x.
@), @], @}
These expand to literal close parentheses, close brackets, and close braces, respectively; these are included for completeness and explicitness only. Example:
          This is a close parenthesis: @).
@"...", @"""...""", etc.
These string literals expand to the literals themselves, so @"test" expands to test. Since they are inherently no-operations, the only reason for their use is to override their behavior with hooks.
@( EXPRESSION )
Evaluate an expression, and expand with the string (via a call to str) representation evaluation of that expression. Whitespace immediately inside the parentheses is ignored; @( expression ) is equivalent to @(expression). If the expression evaluates to None, nothing is expanded in its place; this allows function calls that depend on side effects (such as printing) to be called as expressions. (If you really do want a None to appear in the output, then use the Python string "None".) Note: If an expression prints something to sys.stdout as a side effect, then that printing will be spooled to the output before the expression's return value is. Example:
          2 + 2 is @(2 + 2).
          4 squared is @(4**2).
          The value of the variable x is @(x).
          This will be blank: @(None).
@( TEST ? THEN (! ELSE)_opt ($ EXCEPT)_opt )
A special form of expression evaluation representing conditional and protected evaluation. Evaluate the "test" expression; if it evaluates to true (in the Pythonic sense), then evaluate the "then" section as an expression and expand with the str of that result. If false, then the "else" section is evaluated and similarly expanded. The "else" section is optional and, if omitted, is equivalent to None (that is, no expansion will take place). Note: For backward compatibility, the "else" section delimiter, !, may be expressed as a :. This behavior is supported but deprecated.

If the "except" section is present, then if any of the prior expressions raises an exception when evaluated, the expansion will be replaced with the evaluation of the except expression. (If the "except" expression itself raises, then that exception will be propagated normally.) The except section is optional and, if omitted, is equivalent to None (that is, no expansion will take place). An exception (cough) to this is if one of these first expressions raises a SyntaxError; in that case the protected evaluation lets the error through without evaluating the "except" expression. The intent of this construct is to except runtime errors, and if there is actually a syntax error in the "try" code, that is a problem that should probably be diagnosed rather than hidden. Example:

          What is x? x is @(x ? "true" ! "false").
          Pluralization: How many words? @x word@(x != 1 ? 's').
          The value of foo is @(foo $ "undefined").
          Division by zero is @(x/0 $ "illegal").

@ SIMPLE_EXPRESSION
As a shortcut for the @(...) notation, the parentheses can be omitted if it is followed by a "simple expression." A simple expression consists of a name followed by a series of function applications, array subscriptions, or attribute resolutions, with no intervening whitespace. For example:
  • a name, possibly with qualifying attributes (e.g., @value, @os.environ).

  • a straightforward function call (e.g., @min(2, 3), @time.ctime()), with no space between the function name and the open parenthesis.

  • an array subscription (e.g., '@array[index]', '@os.environ[name]', with no space between the name and the open bracket.

  • any combination of the above (e.g., '@function(args).attr[sub].other[i](foo)').

In essence, simple expressions are expressions that can be written ambiguously from text, without intervening space. Note that trailing dots are not considered part of the expansion (e.g., @x. is equivalent to @(x)., not @(x.), which would be illegal anyway). Also, whitespace is allowed within parentheses or brackets since it is unambiguous, but not between identifiers and parentheses, brackets, or dots. Explicit @(...) notation can be used instead of the abbreviation when concatenation is what one really wants (e.g., @(word)s for simple pluralization of the contents of the variable word). As above, if the expression evaluates to the None object, nothing is expanded. Note that since a curly appearing where EmPy would expect an open parenthesis or bracket in is meaningless in Python, it is treated as a parse error (e.g., @x{1, 2} results in an error). Example:

          The value of x is @x.
          The ith value of a is @a[i].
          The result of calling f with q is @f(q).
          The attribute a of x is @x.a.
          The current time is @time.ctime(time.time()).
          The current year is @time.localtime(time.time())[0].
          These are the same: @min(2,3) and @min(2, 3).
          But these are not the same: @min(2, 3) vs. @min (2, 3).
          The plural of @name is @(name)s, or @name@ s.

@` EXPRESSION `
Evaluate a expression, and expand with the repr (instead of the str which is the default) of the evaluation of that expression. This expansion is primarily intended for debugging and is unlikely to be useful in actual practice. That is, a @`...` is identical to @(repr(...)). Example:
          The repr of the value of x is @`x`.
          This print the Python repr of a module: @`time`.
          This actually does print None: @`None`.
@: EXPRESSION : DUMMY :
Evaluate an expression and then expand to a @:, the original expression, a :, the evaluation of the expression, and then a :. The current contents of the dummy area are ignored in the new expansion. In this sense it is self-evaluating; the syntax is available for use in situations where the same text will be sent through the EmPy processor multiple times. Example:
          This construct allows self-evaluation:
          @:2 + 2:this will get replaced with 4:
@{ STATEMENTS }
Execute a (potentially compound) statement; statements have no return value, so the expansion is not replaced with anything. Multiple statements can either be separated on different lines, or with semicolons; indentation is significant, just as in normal Python code. Statements, however, can have side effects, including printing; output to sys.stdout (explicitly or via a print statement) is collected by the interpreter and sent to the output (unless this behavior is suppressed with the -n option). The usual Python indentation rules must be followed, although if the statement consists of only one statement, leading and trailing whitespace is ignored (e.g., @{ print time.time() } is equivalent to @{print time.time()}). Example:
          @{x = 123}
          @{a = 1; b = 2}
          @{print time.time()}
          @# Note that extra newlines will appear above because of the
          @# newlines trailing the close braces.  To suppress them
          @# use a @ before the newline:
          @{
          for i in range(10):
              print "i is %d" % i
          }@
          @{print "Welcome to EmPy."}@
@% KEY (WHITESPACE VALUE)_opt NEWLINE
Declare a significator. Significators consume the whole line (including the trailing newline), and consist of a key string containing no whitespace, and than optional value prefixed by whitespace. The key may not start with or contain internal whitespace, but the value may; preceding or following whitespace in the value is stripped. Significators are totally optional, and are intended to be used for easy external (that is, outside of EmPy) identification when used in large scale environments with many EmPy files to be processed. The purpose of significators is to provide identification information about each file in a special, easy-to-parse form so that external programs can process the significators and build databases, independently of EmPy. Inside of EmPy, when a significator is encountered, its key, value pair is translated into a simple assignment of the form __KEY__ = VALUE , where "__KEY__" is the key string with two underscores on either side and "VALUE" is a Python expression. Example:
          @%title     "Gravitation"
          @%author    "Misner", "Thorne", "Wheeler"
          @%publisher "W.H. Freeman and Company"
          @%pages     1279
          @%keywords  'physics', 'gravity', 'Einstein', 'relativity'
          @%copyright 1970, 1971
**'@< CONTENTS >'**
Invoke a custom markup. The custom markup is a special markup reserved for use by the user; it has no prescribed meaning on its own. If contents is a string representing what appears in between the angle brackets, then expanding this markup is equivalent to empy.invokeCallback(contents). See the "Custom markup" section for more information.

Control

EmPy version 3 and above includes the ability to direct conditional and repeated expansion of blocks of EmPy code with control markups (the obsolescent "substitution" markups are unavailable as of version 3.0). Control markups have analogs to control flow structures in Python such as if/elif/else, for, and while. Control markups are set off with the @[...] notation.

Control markups are designed to be used in precisely the same way that their internal Python analogues are used, except that the control markups are intended to be used where there is much more markup than control structure.

Some control markups are considered "primary," (e.g., if, for, while) as they begin a control markup. Others are considered "secondary," since they can only appear inside control flow markups delineated by primary markups (e.g., elif, else, continue, break).

Since EmPy, unlike Python, cannot use indentation to determine where control structures begin and end, all primary control markups must be followed by a corresponding terminating control markup:

        @[PRIMARY ...]...@[end PRIMARY]

(where PRIMARY represents one of the primary keywords). The end markup is mandatory, as is the space between the end and the starting keyword. For instance:

        @# If `person' is alive, show their age.
        @person.name is @
        @[if person.isAlive]@person.age@[else]dead@[end if].

All primary markups must be terminated in this way, and the keyword appearing in the appropriate end markup must match the primary markup it corresponds to; if either of these conditions are not satisfied, the result is a parse error. Everything between the starting control flow marker (@[PRIMARY ...]) and the ending marker (@[end PRIMARY]) -- including other markups, even control markups -- is considered part of the markup. Control markups can be nested:

        @# Print all non-false elements on separate lines.
        @[for elem in elements]@[if elem]@elem@\n@[end if]@[end for]

Three major types of primary control markups are available: conditional (e.g., if, try), looping (e.g., for, while), and definitional (e.g., def, discussed below). Conditional control markups conditionally expand their contents, whereas looping control markups repeatedly expand their contents. The third type, definitional markups, will define new objects in the globals relating to their contents. Conditional and looping markups also differ in one substantial respect: Looping constructs support '@[continue]' and '@[break]' markups which, like their Python equivalents, continue with the next iteration or break out of the innermost looping construct, respectively ('@[continue]' and '@[break]' markups have no meaning inside conditional markups and are an error). Also like their Python equivalents, '@[continue]' and '@[break]' may appear inside nested markups, so long as they ultimately are contained by at least one looping control markup:

        @# Walk a long a linked list, printing each element.
        @[while 1]@
        @node
        @{node = node.next}@
        @[if not node]@[break]@[end if]@
        @[end while]

The provided markups are designed to mimic the internal Python control structures as closely as possible. The supported control markups are (the phrases in all uppercase are intended to signify user-selectable patterns):

        @[if CONDITION1]...@[elif CONDITION2]...@[else]...@[end if]
        @[try]...@[except ...]...@[except ...]...@[end try]
        @[try]...@[finally]...@[end try]
        @[for VARIABLE in SEQUENCE]...@[else]...@[end for]
        @[while CONDITION]...@[else]...@[end while]
        @[def SIGNATURE]...@[end def]

All recognizable forms behave like their Python equivalents; if can contain multiple elif secondary markups within it; the else markups are optional (but must appear at the end), the try form with the except clause can contain multiple ones which are handled in sequence, the try form can either contain one or more except clauses or one finally clause (but not both), and the for and while structures can contain continue or break clauses internally (even if contained within other markups).

The third type of primary control markup is "definitional," in that they create objects in the globals for later use (e.g., def). This allows the definition of a callable object which, when called, will expand the contained markup (which can in turn, of course, contain further markups). The argument to the markup can be any legal Python function signature:

        @[def f(x, y, z=2, *args, **keywords)]...@[end def]

would define a function in the globals named f that takes the given arguments. A macro markup of the form @[def SIGNATURE]CODE@[end def] is equivalent to the Python code:

        def SIGNATURE:
            r"""CODE""" # so it is a doc string
            empy.expand(r"""CODE""", locals())

That is, it creates a Python function with the same name and function arguments, whose docstring is the contents of the EmPy markup that will be expanded when called. And, when called, it will expand those contents, with the locals passed in.

Unicode support

EmPy version 3.1 and above includes intrinsic Unicode support. EmPy's Unicode support defers to Python's internal Unicode support, available in Python 2.0 and up, in order to allow seamless and transparent translation of different encodings to the native Python Unicode format.

Knowledge of Python's Unicode support is expected, although not completely required, to gain full benefit of EmPy's Unicode features. To enable Unicode support, start EmPy with the -u/--unicode option. EmPy will then transparently encode from the input stream, process markups internally with native Unicode, and then decode transparently to the output stream.

By default, Python sets sys.stdin and sys.stdout with a default encoding which is accessible via 'sys.getdefaultencoding()'; encodings are represented by string names. These streams have encodings set by the system and cannot be changed.

However, encodings for newly created files (files to be read when specified on the command line, and/or files to be written when used with the -o and -a arguments) can be specified for EmPy via command line options. The --unicode-encoding option simultaneously indicates the encoding to be used for both input and output, whereas the --unicode-input-encoding and --unicode-output-encoding options can each be used to specify different encodings for both input and output. (If an encoding is not explicitly indicated, it resorts to the system default in sys.getdefaultencoding(), which is locale dependent.)

Python's Unicode implementation has the concept of error handlers, registered with the codecs module, which can be specified to determine what action should take place if input cannot be decoded into Unicode, or Unicode cannot be encoded into output. EmPy uses these same "errors," as they are called, and can be specified via command line options. The three most common error handlers are: ignore, where invalid sequences are simply ignored; replace, where invalid sequences are replaced with an encoding-specific indicator, usually a question mark; and strict, where invalid sequences raise an error. The --unicode-errors command line option specifies the same error handler to be used for both input and output, and the --unicode-input-errors and --unicode-output-errors options can specify different error handlers for input and output. If an error handler is not explicitly specified, the strict handler (which will raise errors) is used.

Remember, to specify input encodings or errors that will take effect, one cannot take input from sys.stdin and must explicitly specify an EmPy file to process on the command line. Similarly, for output encodings or errors, sys.stdout cannot be used and an explicit output file must be specified with the -o or -a options. It is perfectly valid to enable the Unicode subsystem (-u option) while using sys.stdin and sys.stdout, but the encodings and errors of these preexisting streams cannot be changed.

Combined with the --no-prefix option, which disables all markup processing, EmPy can act merely as an encoding translator, relying on Python's Unicode facilities:

        em.py --no-prefix \
            --unicode-input-encoding=utf-8 \
            --unicode-output-encoding=latin-1 \
            -o filename.Latin-1 filename.UTF-8

Significators

Significators, introduced in EmPy version 1.2, are intended to represent special assignment in a form that is easy to externally parse. For instance, if one has a system that contains many EmPy files, each of which has its own title, one could use a title significator in each file and use a simple regular expression to find this significator in each file and organize a database of the EmPy files to be built. This is an easier proposition than, for instance, attempting to grep for a normal Python assignment (inside a @{...} expansion) of the desired variable.

Significators look like the following:

        @%KEY VALUE

including the trailing newline, where "key" is a name and "value" is a Python expression, and are separated by any whitespace. This is equivalent to the following Python code:

        __KEY__ = VALUE

That is to say, a significator key translates to a Python variable consisting of that key surrounded by double underscores on either side. The value may contain spaces, but the key may not. So:

        @%title "All Roads Lead to Rome"

translates to the Python code:

        __title__ = "All Roads Lead to Rome"

but obviously in a way that easier to detect externally than if this Python code were to appear somewhere in an expansion. Since significator keys are surrounded by double underscores, significator keys can be any sequence of alphanumeric and underscore characters; choosing 123 is perfectly valid for a significator (although straight), since it maps to the name __123__ which is a legal Python identifier.

Note the value can be any Python expression. The value can be omitted; if missing, it is treated as None.

Significators are completely optional; it is completely legal for a EmPy file or files to be processed without containing any significators. Significators can appear anywhere within a file outside of other markups, but typically they are placed near the top of the file to make them easy to spot and edit by humans.

A regular expression string designed to match significators (with the default prefix) is available as empy.SIGNIFICATOR_RE_STRING, and also is a toplevel definition in the em module itself.

Diversions

EmPy supports an extended form of diversions, which are a mechanism for deferring and recalling output on demand, similar to the functionality included in m4. Multiple "streams" of output can be diverted (deferred) and undiverted (recalled) in this manner. A diversion is identified with a name, which is any immutable object such an integer or string. When recalled, diverted code is not resent through the EmPy interpreter (although a filter could be set up to do this).

By default, no diversions take place. When no diversion is in effect, processing output goes directly to the specified output file. This state can be explicitly requested at any time by calling the empy.stopDiverting function. It is always legal to call this function.

When diverted, however, output goes to a deferred location which can then be recalled later. Output is diverted with the empy.startDiversion function, which takes an argument that is the name of the diversion. If there is no diversion by that name, a new diversion is created and output will be sent to that diversion; if the diversion already exists, output will be appended to that preexisting diversion.

Output send to diversions can be recalled in two ways. The first is through the empy.playDiversion function, which takes the name of the diversion as an argument. This recalls the named diversion, sends it to the output, and then erases that diversion. A variant of this behavior is the empy.replayDiversion, which recalls the named diversion but does not eliminate it afterwards; empy.replayDiversion can be repeatedly called with the same diversion name, and will replay that diversion repeatedly. empy.createDiversion create a diversion without actually diverting to it, for cases where you want to make sure a diversion exists but do not yet want to send anything to it.

The diversion object itself can be retrieved with empy.retrieveDiversion. Diversions act as writable file-objects, supporting the usual write, writelines, flush, and close methods. The data that has been diverted to them can be retrieved in one of two ways; either through the asString method, which returns the entire contents of the diversion as a single strong, or through the asFile method, which returns the contents of the diversion as a readable (not writable) file-like object.

Diversions can also be explicitly deleted without recalling them with the empy.purgeDiversion function, which takes the desired diversion name as an argument.

Additionally there are three functions which will apply the above operations to all existing diversions: empy.playAllDiversions, empy.replayAllDiversions, and empy.purgeAllDiversions. All three will do the equivalent of a empy.stopDiverting call before they do their thing.

The name of the current diversion can be requested with the empy.getCurrentDiversion function; also, the names of all existing diversions (in sorted order) can be retrieved with empy.getAllDiversions.

When all processing is finished, the equivalent of a call to empy.playAllDiversions is done.

Filters

EmPy also supports dynamic filters, introduced in version 1.3. Filters are put in place right "before" the final output file, and so are only invoked after all other processing has taken place (including interpreting and diverting). Filters take input, remap it, and then send it to the output.

The current filter can be retrieved with the empy.getFilter function. The filter can be cleared (reset to no filter) with empy.resetFilter and a special "null filter" which does not send any output at all can be installed with empy.nullFilter. A custom filter can be set with the empy.setFilter function; for convenience, specialized shortcuts for filters preexist and can be used in lieu of actual empy.Filter instances for the empy.setFilter or empy.attachFilter argument:

  • None is a special filter meaning "no filter"; when installed, no filtering whatsoever will take place. empy.setFilter(None) is equivalent to empy.resetFilter().

  • 0 (or any other numeric constant equal to zero) is another special filter that represents the null filter; when installed, no output will ever be sent to the filter's sink.

  • A filter specified as a function (or lambda) is expected to take one string argument and return one string argument; this filter will execute the function on any input and use the return value as output.

  • A filter that is a string is a 256-character table is substituted with the result of a call to string.translate using that table.

  • A filter can be an instance of a subclass of empy.Filter. This is the most general form of filter. (In actuality, it can be any object that exhibits a Filter interface, which would include the normal file-like write, flush, and close methods, as well as next, attach, and detach methods for filter-specific behavior.)

  • Finally, the argument to empy.setFilter can be a Python list consisting of one or more of the above objects. In that case, those filters are chained together in the order they appear in the list. An empty list is the equivalent of 'None'; all filters will be uninstalled.

Filters are, at their core, simply file-like objects (minimally supporting write, flush, and close methods that behave in the usual way) which, after performing whatever processing they need to do, send their work to the next file-like object or filter in line, called that filter's "sink." That is to say, filters can be "chained" together; the action of each filter takes place in sequence, with the output of one filter being the input of the next. Additionally, filters support a _flush method (note the leading underscore) which will always flush the filter's underlying sink; this method should be not overridden.

Filters also support three additional methods, not part of the traditional file interface: attach, which takes as an argument a file-like object (perhaps another filter) and sets that as the filter's "sink" -- that is, the next filter/file-like object in line. detach (which takes no arguments) is another method which flushes the filter and removes its sink, leaving it isolated. Finally, next is an accessor method which returns the filter's sink -- or None, if the filter does not yet have a sink attached.

To create your own filter, you can create an object which supports the above described interface, or simply derive from the empy.Filter class and override its write and possibly flush methods. You can chain filters together by passing them as elements in a list to the empy.setFilter function, or you can chain them together manually with the attach method:

        firstFilter.attach(secondFilter)
        empy.setFilter(firstFilter)

or just let EmPy do the chaining for you:

        empy.setFilter([firstFilter, secondFilter])

In either case, EmPy will walk the filter chain and find the end and then hook that into the appropriate interpreter stream; you need not do this manually. The function empy.attachFilter can be used to attach a single filter (or shortcut, as above) to the end of a currently existing chain. Note that unlike its cousin empy.setFilter, one cannot pass a sequence of filters (or filter shortcuts) to empy.attachFilter. (If there is no existing filter chain installed, empy.attachFilter will behave the same as empy.setFilter.)

Subclasses of empy.Filter are already provided with the above null, function, and string functionality described above; they are NullFilter, FunctionFilter, and StringFilter, respectively. In addition, a filter which supports buffering, BufferedFilter, is provided. Several variants are included: SizeBufferedFilter, a filter which buffers into fixed-sized chunks, LineBufferedFilter, a filter which buffers by lines, and MaximallyBufferedFilter, a filter which completely buffers its input.

Hooks

The EmPy system allows for the registry of hooks with a running EmPy interpreter. Originally introduced in version 2.0 and much improved in 3.2, hooks are objects, registered with an interpreter, whose methods represent specific callbacks. Any number of hook objects can be registered with an interpreter, and when a callback is invoked, the associated method on each one of those hook objects will be called by the interpreter in sequence.

Hooks are simply instances, nominally derived from the empy.Hook class. The empy.Hook class itself defines a series of methods, with the expected arguments, which would be called by a running EmPy interpreter. This scenario, much improved from the prior implementation in 2.0, allows hooks to keep state and have more direct access to the interpreter they are running in (the empy.Hook instance contains an interpreter attribute).

To use a hook, derive a class from empy.Hook and override the desired methods (with the same signatures as they appear in the base class). Create an instance of that subclass, and then register it with a running interpreter with the empy.addHook function. (This same hook instance can be removed with the empy.removeHook function.)

More than one hook instance can be registered with an interpreter; in such a case, the appropriate methods are invoked on each instance in the order in which they were registered. To adjust this behavior, an optional prepend argument to the empy.addHook function can be used dictate that the new hook should placed at the beginning of the sequence of hooks, rather than at the end (which is the default).

All hooks can be enabled and disabled entirely for a given interpreter; this is done with the empy.enableHooks and empy.disableHooks functions. By default hooks are enabled, but obviously if no hooks have been registered no hook callbacks will be made. Whether hooks are enabled or disabled can be determined by calling empy.areHooksEnabled. To get a (copy of) the list of registered hooks, call empy.getHooks. Finally, to invoke a hook manually, use empy.invokeHook.

For a list of supported hook callbacks, see the empy.Hook class definition.

As a practical example, this sample Python code would print a pound sign followed by the name of every file that is included with 'empy.include':

        class IncludeHook(empy.Hook):
            def beforeInclude(self, name, file, locals):
                print "# %s" % name

        empy.addHook(IncludeHook())

Custom markup

Since version 3.2.1, the markup '@<...>' is reserved for user-defined use. Unlike the other markups, this markup has no specified meaning on its own, and can be provided a meaning by the user. This meaning is provided with the use of a "custom callback," or just "callback," which can be set, queried, or reset using the pseudomodule function.

The custom callback is a callable object which, when invoked, is passed a single argument: a string representing the contents of what was found inside the custom markup '@<...>'.

To register a callback, call empy.registerCallback. To remove one, call empy.deregisterCallback. To retrieve the callback (if any) registered with the interpreter, use empy.getCallback. Finally, to invoke the callback just as if the custom markup were encountered, call empy.invokeCallback. For instance, '@' would be equivalent to the call @empy.invokeCallback("This text").

By default, to invoke a callback (either explicitly with empy.invokeCallback or by processing a '@<...>' custom markup) when no callback has been registered is an error. This behavior can be changed with the CALLBACK_OPT option, or the --no-callback-error command line option.

Pseudomodule

The empy pseudomodule is available only in an operating EmPy system. (The name of the module, by default empy, can be changed with the -m option or the EMPY_PSEUDO environment variable). It is called a pseudomodule because it is not actually a module, but rather exports a module-like interface. In fact, the pseudmodule is actually the same internal object as the interpreter itself.

The pseudomodule contains the following functions and objects (and their signatures, with a suffixed opt indicating an optional argument):

First, basic identification:

VERSION
A constant variable which contains a string representation of the EmPy version.
SIGNIFICATOR_RE_STRING
A constant variable representing a regular expression string (using the default prefix) that can be used to find significators in EmPy code.
SIGNIFICATOR_RE_SUFFIX
The portion of the significator regular expression string excluding the prefix, so that those using non-standard prefix can build their own custom regular expression string with myPrefix + empy.SIGNIFICATOR_RE_SUFFIX.
interpreter
The instance of the interpreter that is currently being used to perform execution. Note: This is now obsolete; the pseudomodule is itself the interpreter. Instead of using empy.interpreter, simply use empy.
argv
A list consisting of the name of the primary EmPy script and its command line arguments, in analogue to the sys.argv list.
args
A list of the command line arguments following the primary EmPy script; this is equivalent to empy.argv[1:].
identify() -> string, integer
Retrieve identification information about the current parsing context. Returns a 2-tuple consisting of a filename and a line number; if the file is something other than from a physical file (e.g., an explicit expansion with empy.expand, a file-like object within Python, or via the -E or -F command line options), a string representation is presented surrounded by angle brackets. Note that the context only applies to the EmPy context, not the Python context.
atExit(callable)
Register a callable object (such as a function) taking no arguments which will be called at the end of a normal shutdown. Callable objects registered in this way are called in the reverse order in which they are added, so the first callable registered with empy.atExit is the last one to be called. Note that although the functionality is related to hooks, empy.atExit does no work via the hook mechanism, and you are guaranteed that the interpreter and stdout will be in a consistent state when the callable is invoked.

Context manipulation:

pushContext(name_opt, line_opt)
Create a new context with the given name and line and push it on the stack.
popContext()
Pop the top context and dispose of it.
setContextName(name)
Manually set the name of the current context.
setContextLine(line)
Manually set the line number of the current context; line must be a numeric value. Note that afterward the line number will increment by one for each newline that is encountered, as before.

Globals manipulation:

getGlobals()
Retrieve the globals dictionary for this interpreter. Unlike when calling globals() in Python, this dictionary can be manipulated and you can expect changes you make to it to be reflected in the interpreter that holds it.
setGlobals(globals)
Reseat the globals dictionary associated with this interpreter to the provided mapping type.
updateGlobals(globals)
Merge the given dictionary into this interpreter's globals.
clearGlobals(globals_opt)
Clear out the globals (restoring, of course, the empy pseudomodule). Optionally, instead of starting with a refresh dictionary, use the dictionary provided.
saveGlobals(deep=True)
Save a copy of the globals onto an internal history stack from which it can be restored later. The optional deep argument indicates whether or not the copying should be a deep copy (default) or a shallow one. Copying is done with copy.deepcopy or copy.copy, respectively.
restoreGlobals(destructive=True)
Restore the most recently saved globals from the history stack to as the current globals for this instance. The optional destructive argument indicates whether or not the restore should remove the restored globals from the history stack (default), or whether it should be left there for subsequent restores.

Types:

Interpreter
The actual interpreter class.

The following functions allow direct execution; optional locals arguments, if specified, are treated as the locals dictionary in evaluation and execution:

defined(name, locals_opt)
Return true if the given name is defined either in the (optional) locals or the interpreter globals; return false otherwise.
evaluate(expression, locals_opt)
Evaluate the given expression.
serialize(expression, locals_opt)
Serialize the expression, just as the interpreter would: If it is not None, convert it to a string with the str builtin function, and then write out the result. If it evaluates to None, do nothing.
execute(statements, locals_opt)
Execute the given statement(s).
single(source, locals_opt)
Interpret the "single" source code, just as the Python interactive interpreter would.
import_(name, locals_opt)
Import a module.
atomic(name, value, locals_opt)
Perform a single, atomic assignment. In this case name is the string denoating the name of the (single) variable to be assigned to, and value is a Python object which the name is to be bound to.
assign(name, value, locals_opt)
Perform general assignment. This decays to atomic assignment (above) in the normal case, but supports "tuple unpacking" in the sense that if name string contains commas, it is treated as a sequence of names and memberwise assignment with each member of the value (still a Python object, but which must be a sequence). This function will raise a TypeError or ValueError just like Python would if tuple unpacking is not possible (that is, if the value is not a sequence or is of an incompatible length, respectively). This only supports the assignment of Python identifiers, not arbitrary Python lvalues.
significate(key, value_opt, locals_opt)
Do a manual signification. If value is not specified, it is treated as None.

The following functions relate to source manipulation:

include(file_or_filename, locals_opt)
Include another EmPy file, by processing it in place. The argument can either be a filename (which is then opened with open in text mode) or a file object, which is used as is. Once the included file is processed, processing of the current file continues. Includes can be nested. The call also takes an optional locals dictionary which will be passed into the evaluation function.
expand(string, locals_opt) -> string
Explicitly invoke the EmPy parsing system to process the given string and return its expansion. This allows multiple levels of expansion, e.g., @(empy.expand("@(2 + 2)")). The call also takes an optional locals dictionary which will be passed into the evaluation function. This is necessary when text is being expanded inside a function definition and it is desired that the function arguments (or just plain local variables) are available to be referenced within the expansion.
quote(string) -> string
The inverse process of empy.expand, this will take a string and return a new string that, when expanded, would expand to the original string. In practice, this means that appearances of the prefix character are doubled, except when they appear inside a string literal.
escape(string, more_opt) -> string
Given a string, quote the nonprintable characters contained within it with EmPy escapes. The optional more argument specifies additional characters that should be escaped.
flush()
Do an explicit flush on the underlying stream.
string(string, name_opt, locals_opt)
Explicitly process a string-like object. This differs from empy.expand in that the string is directly processed into the EmPy system, rather than being evaluated in an isolated context and then returned as a string.

Changing the behavior of the pseudomodule itself:

flatten(keys_opt)
Perform the equivalent of from empy import ... in code (which is not directly possible because empy is a pseudomodule). If keys is omitted, it is taken as being everything in the empy pseudomodule. Each of the elements of this pseudomodule is flattened into the globals namespace; after a call to empy.flatten, they can be referred to simple as globals, e.g., @divert(3) instead of @empy.divert(3). If any preexisting variables are bound to these names, they are silently overridden. Doing this is tantamount to declaring an from ... import ... which is often considered bad form in Python.

Prefix-related functions:

getPrefix() -> char
Return the current prefix.
setPrefix(char)
Set a new prefix. Immediately after this call finishes, the prefix will be changed. Changing the prefix affects only the current interpreter; any other created interpreters are unaffected. Setting the prefix to None or the null string means that no further markups will be processed, equivalent to specifying the --no-prefix command line argument.

Diversions:

stopDiverting()
Any diversions that are currently taking place are stopped; thereafter, output will go directly to the output file as normal. It is never illegal to call this function.
createDiversion(name)
Create a diversion, but do not begin diverting to it. This is the equivalent of starting a diversion and then immediately stopping diversion; it is used in cases where you want to make sure that a diversion will exist for future replaying but may be empty.
startDiversion(name)
Start diverting to the specified diversion name. If such a diversion does not already exist, it is created; if it does, then additional material will be appended to the preexisting diversions.
playDiversion(name)
Recall the specified diversion and then purge it. The provided diversion name must exist.
replayDiversion(name)
Recall the specified diversion without purging it. The provided diversion name must exist.
purgeDiversion(name)
Purge the specified diversion without recalling it. The provided diversion name must exist.
playAllDiversions()
Play (and purge) all existing diversions in the sorted order of their names. This call does an implicit empy.stopDiverting before executing.
replayAllDiversions()
Replay (without purging) all existing diversions in the sorted order of their names. This call does an implicit empy.stopDiverting before executing.
purgeAllDiversions()
Purge all existing diversions without recalling them. This call does an implicit empy.stopDiverting before executing.
getCurrentDiversion() -> diversion
Return the name of the current diversion.
getAllDiversions() -> sequence
Return a sorted list of all existing diversions.

Filters:

getFilter() -> filter
Retrieve the current filter. None indicates no filter is installed.
resetFilter()
Reset the filter so that no filtering is done.
nullFilter()
Install a special null filter, one which consumes all text and never sends any text to the output.
setFilter(shortcut)
Install a new filter. A filter is None or an empty sequence representing no filter, or 0 for a null filter, a function for a function filter, a string for a string filter, or an instance of empy.Filter (or a workalike object). If filter is a list of the above things, they will be chained together manually; if it is only one, it will be presumed to be solitary or to have already been manually chained together. See the "Filters" section for more information.
attachFilter(shortcut)
Attach a single filter (sequences are not allowed here) to the end of a currently existing filter chain, or if there is no current chain, install it as empy.setFilter would. As with empy.setFilter, the shortcut versions of filters are also allowed here.

Hooks:

areHooksEnabled()
Return whether or not hooks are presently enabled.
enableHooks()
Enable invocation of hooks. By default hooks are enabled.
disableHooks()
Disable invocation of hooks. Hooks can still be added, removed, and queried, but invocation of hooks will not occur (even explicit invocation with empy.invokeHook).
getHooks()
Get a (copy of the) list of the hooks currently registered.
clearHooks()
Clear all the hooks registered with this interpreter.
addHook(hook, prepend_opt)
Add this hook to the hooks associated with this interpreter. By default, the hook is appended to the end of the existing hooks, if any; if the optional insert argument is present and true, it will be prepended to the list instead.
removeHook(hook)
Remove this hook from the hooks associated with this interpreter.
invokeHook(_name, ...)
Manually invoke a hook method. The remaining arguments are treated as keyword arguments and the resulting dictionary is passed in as the second argument to the hooks.

Custom markup callback:

getCallback() -> callback
Retrieve the current callback associated with this interpreter, or None if it does not yet have one.
registerCallback(callback)
Register a callback to be called whenever a custom markup ('@<...>') is encountered. When encountered, invokeCallback is called.
deregisterCallback()
Clear any callback previously registered with the interpreter for being called when a custom markup is encountered.
invokeCallback(contents)
Invoke a custom callback. This function is called whenever a custom markup ('@<...>') is encountered. It in turn calls the registered callback, with a single argument, contents, which is a string representing of the contents of the custom markup.

Invocation

Basic invocation involves running the interpreter on an EmPy file and some optional arguments. If no file are specified, or the file is named -, EmPy takes its input from stdin. One can suppress option evaluation (to, say, specify a file that begins with a dash) by using the canonical -- option.

-h/--help
Print usage and exit.
-H/--extended-help
Print extended usage and exit. Extended usage includes a rundown of all the legal expansions, escape sequences, pseudomodule contents, used hooks, and supported environment variables.
-v/--verbose
The EmPy system will print all manner of details about what it is doing and what it is processing to stderr.
-V/--version
Print version and exit.
-a/--append (filename)
Open the specified file for append instead of using stdout.
-b/--buffered-output
Fully buffer processing output, including the file open itself. This is helpful when, should an error occur, you wish that no output file be generated at all (for instance, when using EmPy in conjunction with make). When specified, either the -o or -a options must be specified, and the -b option must precede them. This can also be specified through the existence of the EMPY_BUFFERED_OUTPUT environment variable.
-f/--flatten
Before processing, move the contents of the empy pseudomodule into the globals, just as if empy.flatten() were executed immediately after starting the interpreter. That is, e.g., empy.include can be referred to simply as include when this flag is specified on the command line. This can also be specified through the existence of the EMPY_FLATTEN environment variable.
-i/--interactive
After the main EmPy file has been processed, the state of the interpreter is left intact and further processing is done from stdin. This is analogous to the Python interpreter's -i option, which allows interactive inspection of the state of the system after a main module is executed. This behaves as expected when the main file is stdin itself. This can also be specified through the existence of the EMPY_INTERACTIVE environment variable.
-k/--suppress-errors
Normally when an error is encountered, information about its location is printed and the EmPy interpreter exits. With this option, when an error is encountered (except for keyboard interrupts), processing stops and the interpreter enters interactive mode, so the state of affairs can be assessed. This is also helpful, for instance, when experimenting with EmPy in an interactive manner. -k implies -i.
-n/--no-override-stdout
Do not override sys.stdout with a proxy object which the EmPy system interacts with. If suppressed, this means that side effect printing will not be captured and routed through the EmPy system. However, if this option is specified, EmPy can support multithreading.
-o/--output (filename)
Open the specified file for output instead of using stdout. If a file with that name already exists it is overwritten.
-p/--prefix (prefix)
Change the prefix used to detect expansions. The argument is the one-character string that will be used as the prefix. Note that whatever it is changed to, the way to represent the prefix literally is to double it, so if $ is the prefix, a literal dollar sign is represented with $$. Note that if the prefix is changed to one of the secondary characters (those that immediately follow the prefix to indicate the type of action EmPy should take), it will not be possible to represent literal prefix characters by doubling them (e.g., if the prefix were inadvisedly changed to # then ## would already have to represent a comment, so ## could not represent a literal #). This can also be specified through the EMPY_PREFIX environment variable.
-r/--raw-errors
Normally, EmPy catches Python exceptions and prints them alongside an error notation indicating the EmPy context in which it occurred. This option causes EmPy to display the full Python traceback; this is sometimes helpful for debugging. This can also be specified through the existence of the EMPY_RAW_ERRORS environment variable.
-u/--unicode
Enable the Unicode subsystem. This option only need be present if you wish to enable the Unicode subsystem with the defaults; any other Unicode-related option (starting with --unicode...) will also enable the Unicode subsystem.
-D/--define (assignment)
Execute a Python assignment of the form variable = expression. If only a variable name is provided (i.e., the statement does not contain an = sign), then it is taken as being assigned to None. The -D option is simply a specialized -E option that special cases the lack of an assignment operator. Multiple -D options can be specified.
-E/--execute (statement)
Execute the Python (not EmPy) statement before processing any files. Multiple -E options can be specified.
-F/--execute-file (filename)
Execute the Python (not EmPy) file before processing any files. This is equivalent to -E execfile("filename") but provides a more readable context. Multiple -F options can be specified.
-I/--import (module)
Imports the specified module name before processing any files. Multiple modules can be specified by separating them by commas, or by specifying multiple -I options.
-P/--preprocess (filename)
Process the EmPy file before processing the primary EmPy file on the command line.
--binary
Treat the file as a binary file, and read in chunks rather than line by line. In this mode, the "line" indicator represents the number of bytes read, not the number of lines processed.
--no-prefix
Disable the prefixing system entirely; when specified, EmPy will not expand any markups. This allows EmPy to merely act as a Unicode encoding translator..
--pause-at-end
If present, then raw_input will be called at the end of processing. Useful in systems where the output window would otherwise be closed by the operating system/window manager immediately after EmPy exited.
--relative-path
When present, the path the EmPy script being invoked is contained in will be prepended to sys.path. This is analogous to Python's internal handling of sys.path and scripts. If input is from stdin (- for a filename or no filename is specified), then nothing is added to the path.
--no-callback-error
Do not consider it an error if the custom markup is invoked '@<...>' and there is no callback function registered for it.
--chunk-size (chunk)
Use the specific binary chunk size rather than the default; implies --binary.
--unicode-encoding (encoding)
Specify the Unicode encoding to be used for both input and output.
--unicode-input-encoding (encoding)
Specify the Unicode encoding to be used for input.
--unicode-output-encoding (encoding)
Specify the Unicode encoding to be used for output.
'--unicode-input-errors (errors)
Specify the Unicode error handling to be used for input.
'--unicode-errors (errors)
Specify the Unicode error handling to be used for both input and output.
'--unicode-output-errors (errors)
Specify the Unicode error handling to be used for output.

Environment variables

EmPy also supports a few environment variables to predefine certain behaviors. The settings chosen by environment variables can be overridden via command line arguments. The following environment variables have meaning to EmPy:

EMPY_OPTIONS
If present, the contents of this environment variable will be treated as options, just as if they were entered on the command line, before the actual command line arguments are processed. Note that these arguments are not processed by the shell, so quoting, filename globbing, and the like, will not work.
EMPY_PREFIX
If present, the value of this environment variable represents the prefix that will be used; this is equivalent to the -p command line option.
EMPY_PSEUDO
If present, the value of this environment variable represents the name of the pseudomodule that will be incorporated into every running EmPy system; this is equivalent to the -m command line option.
EMPY_FLATTEN
If defined, this is equivalent to including -f on the command line.
EMPY_RAW_ERRORS
If defined, this is equivalent to including -r on the command line.
EMPY_INTERACTIVE
If defined, this is equivalent to including -i on the command line.
EMPY_BUFFERED_OUTPUT
If defined, this is equivalent to including -b on the command line.
EMPY_UNICODE
If defined, this is equivalent to including -u on the command line.
EMPY_UNICODE_INPUT_ENCODING
If present, the value of this environment variable indicates the name of the Unicode input encoding to be used. This is equivalent to the --unicode-input-encoding command line option.
EMPY_UNICODE_OUTPUT_ENCODING
If present, the value of this environment variable indicates the name of the Unicode output encoding to be used. This is equivalent to the --unicode-output-encoding command line option.
EMPY_UNICODE_INPUT_ERRORS
If present, the value of this environment variable indicates the name of the error handler to be used for input. This is equivalent to the --unicode-input-errors command line option.
EMPY_UNICODE_OUTPUT_ERRORS
If present, the value of this environment variable indicates the name of the error handler to be used for output. This is equivalent to the --unicode-output-errors command line option.

Examples and testing EmPy

See the sample EmPy file sample.em which is included with the distribution. Run EmPy on it by typing something like:

         ./em.py sample.em

and compare the results and the sample source file side by side. The sample content is intended to be self-documenting, and even an introduction to the basic features of EmPy while simultaneously exercising them.

The file sample.bench is the benchmark output of the sample. Running the EmPy interpreter on the provided sample.em file should produce precisely the same results. You can run the provided test script to see if your EmPy environment is behaving as expected (presuming a Unix-like operating system):

        ./test.sh

By default this will test with the first Python interpreter available in the path; if you want to test with another interpreter, you can provide it as the first argument on the command line, e.g.:

        ./test.sh python2.1
        ./test.sh /usr/bin/python1.5
        ./test.sh jython

A more comprehensive test suite and set of real-world examples is planned for a future version.

Embedding EmPy

For atomic applications, the expand function is provided (the extra keyword arguments passed in are treated as locals):

        import em
        print em.expand("@x + @y is @(x + y).", x=2, y=3)

One can specify a globals dictionary and all the other interpreter options (below) as well. One can specify a globals dictionary that will be used if one wants persistence:

        import em
        g = {}
        em.expand("@{x = 10}", g)
        print em.expand("x is @x.", g)

The standalone expand function, however, creates and destroys an Interpreter instance each time it is called. For repeated expansions, this can be expensive. Instead, you will probably want to use the full-fledged features of embedding. An EmPy interpreter can be created with as code as simple as:

        import em
        interpreter = em.Interpreter()
        # The following prints the results to stdout:
        interpreter.string("@{x = 123}@x\n")
        # This expands to the same thing, but puts the results as a
        # string in the variable result:
        result = interpreter.expand("@{x = 123}@x\n")
        # This just prints the value of x directly:
        print interpreter.globals['x']
        # Process an actual file (and output to stdout):
        interpreter.file(open('/path/to/some/file'))
        interpreter.shutdown() # this is important; see below

One can capture the output of a run in something other than stdout by specifying the output parameter:

        import em, StringIO
        output = StringIO.StringIO()
        interpreter = em.Interpreter(output=output)
        # Do something.
        interpreter.file(open('/path/to/some/file'))
        interpreter.shutdown() # again, this is important; see below
        print output.getvalue() # this is the result from the session

When you are finished with your interpreter, it is important to call its shutdown method:

        interpreter.shutdown()

This will ensure that the interpreter cleans up all its overhead, entries in the sys.stdout proxy, and so forth. It is usually advisable that this be used in a try...finally clause:

        interpreter = em.Interpreter(...)
        try:
            ...
        finally:
            interpreter.shutdown()

The em.Interpreter constructor takes the following arguments; all are optional. Since options may be added in the future, it is highly recommended that the constructor be invoked via keyword arguments, rather than assuming their order. The arguments are:

output
The output file which the interpreter will be sending all its processed data to. This need only be a file-like object; it need not be an actual file. If omitted, sys.__stdout__ is used.
argv
An argument list analogous to sys.argv, consisting of the script name and zero or more arguments. These are available to executing interpreters via empy.argv and empy.args. If omitted, a non-descript script name is used with no arguments.
prefix
The prefix (a single-character string). Defaults to @. It is an error for this to be anything other than one character.
pseudo
The name (string) of the pseudmodule. Defaults to empy.
options
A dictionary of options that can override the default behavior of the interpreter. The names of the options are constant names ending in _OPT and their defaults are given in Interpreter.DEFAULT_OPTIONS.
globals
By default, interpreters begin with a pristine dictionary of globals (except, of course, for the empy pseudomodule). Specifying this argument will allow the globals to start with more.
hooks
A sequence of hooks (or None for none) to register with the interpreter at startup. Hooks can, of course, be added after the fact, but this allows the hooks to intercept the atStartup event (otherwise, the startup event would already have occurred by the time new hooks could be registered)..

Many things can be done with EmPy interpreters; for the full developer documentation, see the generated documentation for the em module.

Interpreter options

The following options (passed in as part of the options dictionary to the Interpreter constructor) have the following meanings. The defaults are shown below and are also indicated in an Interpreter.DEFAULT_OPTIONS dictionary.

BANGPATH_OPT
Should a bangpath (#!) as the first line of an EmPy file be treated as if it were an EmPy comment? Note that #! sequences starting lines or appearing anywhere else in the file are untouched regardless of the value of this option. Default: true.
BUFFERED_OPT
Should an abort method be called upon failure? This relates to the fully-buffered option, where all output can be buffered including the file open; this option only relates to the interpreter's behavior after that proxy file object has been created. Default: false.
RAW_OPT
Should errors be displayed as raw Python errors (that is, the exception is allowed to propagate through to the toplevel so that the user gets a standard Python traceback)? Default: false.
EXIT_OPT
Upon an error, should execution continue (although the interpreter stacks will be purged)? Note that even in the event this is set, the interpreter will halt upon receiving a KeyboardInterrupt. Default: true.
FLATTEN_OPT
Upon initial startup, should the empy pseudomodule namespace be flattened, i.e., should empy.flatten be called? Note this option only has an effect when the interpreter is first created; thereafter it is ignored. Default: false.
OVERRIDE_OPT
Should the sys.stdout object be overridden with a proxy object? If not, side effect output cannot be captured by the EmPy system, but EmPy will support multithreading. Default: true.
CALLBACK_OPT
If a callback is invoked when none has yet been registered, should an error be raised or should the situation be ignored? Default: true.

Data flow

input -> interpreter -> diversions -> filters -> output

Here, in summary, is how data flows through a working EmPy system:

  1. Input comes from a source, such an .em file on the command line, or via an empy.include statement.

  2. The interpreter processes this material as it comes in, expanding EmPy expansions as it goes.

  3. After interpretation, data is then sent through the diversion layer, which may allow it directly through (if no diversion is in progress) or defer it temporarily. Diversions that are recalled initiate from this point.

  4. Any filters in place are then used to filter the data and produce filtered data as output.

  5. Finally, any material surviving this far is sent to the output stream. That stream is stdout by default, but can be changed with the -o or -a options, or may be fully buffered with the -b option (that is, the output file would not even be opened until the entire system is finished).

Author's notes

I originally conceived EmPy as a replacement for my Web templating system which uses m4 (a general macroprocessing system for Unix).

Most of my Web sites include a variety of m4 files, some of which are dynamically generated from databases, which are then scanned by a cataloging tool to organize them hierarchically (so that, say, a particular m4 file can understand where it is in the hierarchy, or what the titles of files related to it are without duplicating information); the results of the catalog are then written in database form as an m4 file (which every other m4 file implicitly includes), and then GNU make converts each m4 to an HTML file by processing it.

As the Web sites got more complicated, the use of m4 (which I had originally enjoyed for the challenge and abstractness) really started to become an impediment to serious work; while I am very knowledgeable about m4 -- having used it for for so many years -- getting even simple things done with it is awkward and difficult. Worse yet, as I started to use Python more and more over the years, the cataloging programs which scanned the m4 and built m4 databases were migrated to Python and made almost trivial, but writing out huge awkward tables of m4 definitions simply to make them accessible in other m4 scripts started to become almost farcical -- especially when coupled with the difficulty in getting simple things done in m4.

It occurred to me what I really wanted was an all-Python solution. But replacing what used to be the m4 files with standalone Python programs would result in somewhat awkward programs normally consisting mostly of unprocessed text punctuated by small portions where variables and small amounts of code need to be substituted. Thus the idea was a sort of inverse of a Python interpreter: a program that normally would just pass text through unmolested, but when it found a special signifier would execute Python code in a normal environment. I looked at existing Python templating systems, and didn't find anything that appealed to me -- I wanted something where the desired markups were simple and unobtrusive. After considering between choices of signifiers, I settled on @ and EmPy was born.

As I developed the tool, I realized it could have general appeal, even to those with widely varying problems to solve, provided the core tool they needed was an interpreter that could embed Python code inside templated text. As I continue to use the tool, I have been adding features as unintrusively as possible as I see areas that can be improved.

A design goal of EmPy is that its feature set should work on several levels; at each level, if the user does not wish or need to use features from another level, they are under no obligation to do so. If you have no need of diversions, for instance, you are under no obligation to use them. If significators will not help you organize a set of EmPy scripts globally, then you need not use them. New features that are being added are whenever possible transparently backward compatible; if you do not need them, their introduction should not affect you in any way. The use of unknown prefix sequences results in errors, guaranteeing that they are reserved for future use.

Glossary

control
A control markup, used to direct high-level control flow within an EmPy session. Control markups are expressed with the @[...] notation.
diversion
A process by which output is deferred, and can be recalled later on demand, multiple times if necessary.
document
The abstraction of an EmPy document as used by a processor.
escape
A markup designed to expand to a single (usually non-printable) character, similar to escape sequences in C or other languages.
expansion
The process of processing EmPy markups and producing output.
expression
An expression markup represents a Python expression to be evaluated, and replaced with the str of its value. Expression markups are expressed with the @(...) notation.
filter
A file-like object which can be chained to other objects (primarily the final stream) and can buffer, alter, or manipulate in any way the data sent. Filters can also be chained together in arbitrary order.
globals
The dictionary (or dictionary-like object) which resides inside the interpreter and holds the currently-defined variables.
hook
A callable object that can be registered in a dictionary, and which will be invoked before, during, or after certain internal operations, identified by name with a string.
interpreter
The application (or class instance) which processes EmPy markup.
markup
EmPy substitutions set off with a prefix and appropriate delimeters.
output
The final destination of the result of processing an EmPy file.
prefix
The ASCII character used to set off an expansions. By default, @.
processor
An extensible system which processes a group of EmPy files, usually arranged in a filesystem, and scans them for significators.
pseudomodule
The module-like object named empy which is exposed internally inside every EmPy system.
shortcut
A special object which takes the place of an instance of the Filter class, to represent a special form of filter. These include 0 for a null filter, a callable (function or lambda) to represent a callable filter, or a 256-character string which represents a translation filter.
significator
A special form of an assignment markup in EmPy which can be easily parsed externally, primarily designed for representing uniform assignment across a collection of files. Significators are indicated with the @% markup.
statement
A line of code that needs to be executed; statements do not have return values. In EmPy, statements are set off with @{...}.

Acknowledgements

Questions, suggestions, bug reports, evangelism, and even complaints from many people have helped make EmPy what it is today. Some, but by no means all, of these people are (in alphabetical order by surname):

  • Biswapesh Chattopadhyay

  • Beni Cherniavsky

  • Dr. S. Candelaria de Ram

  • Eric Eide

  • Dinu Gherman

  • Grzegorz Adam Hankiewicz

  • Bohdan Kushnir

  • Robert Kroeger

  • Kouichi Takahashi

  • Ville Vainio

Known issues and caveats

  • EmPy was primarily intended for static processing of documents, rather than dynamic use, and hence speed of processing was not the primary consideration in its design.

  • EmPy is not threadsafe by default. This is because of the need for EmPy to override the sys.stdout file with a proxy object which can capture effects of print and other spooling to stdout. This proxy can be suppressed with the -n option, which will result in EmPy being unable to do anything meaningful with this output, but will allow EmPy to be threadsafe.

  • To function properly, EmPy must override sys.stdout with a proxy file object, so that it can capture output of side effects and support diversions for each interpreter instance. It is important that code executed in an environment not rebind sys.stdout, although it is perfectly legal to invoke it explicitly (e.g., @sys.stdout.write("Hello world\n")). If one really needs to access the "true" stdout, then use sys.__stdout__ instead (which should also not be rebound). EmPy uses the standard Python error handlers when exceptions are raised in EmPy code, which print to sys.stderr.

  • Due to Python's curious handling of the print statement -- particularly the form with a trailing comma to suppress the final newline -- mixing statement expansions using prints inline with unexpanded text will often result in surprising behavior, such as extraneous (sometimes even deferred!) spaces. This is a Python "feature," and occurs in non-EmPy applications as well; for finer control over output formatting, use sys.stdout.write or empy.interpreter.write directly.

  • The empy "module" exposed through the EmPy interface (e.g., @empy) is an artificial module. It cannot be imported with the import statement (and shouldn't -- it is an artifact of the EmPy processing system and does not correspond to any accessible .py file).

  • For an EmPy statement expansion all alone on a line, e.g., @{a = 1}, note that this will expand to a blank line due to the newline following the closing curly brace. To suppress this blank line, use the symmetric convention @{a = 1}@.

  • When using EmPy with make, note that partial output may be created before an error occurs; this is a standard caveat when using make. To avoid this, write to a temporary file and move when complete, delete the file in case of an error, use the -b option to fully buffer output (including the open), or (with GNU make) define a .DELETE_ON_ERROR target.

  • empy.identify tracks the context of executed EmPy code, not Python code. This means that blocks of code delimited with @{ and } will identify themselves as appearing on the line at which the } appears, and that pure Python code executed via the -D, -E and -F command line arguments will show up as all taking place on line 1. If you're tracking errors and want more information about the location of the errors from the Python code, use the -r command line option, which will provide you with the full Python traceback.

  • The conditional form of expression expansion @(...?...!...) allows the use of a colon instead of an exclamation point, e.g., @(...?...:...). This behavior is supported for backward compatibility, but is deprecated. Due to an oversight, the colon was a poor choice since colons can appear legally in expressions (e.g., dictionary literals or lambda expressions).

  • The '@[try]' construct only works with Python exceptions derived from Exception. It is not able to catch string exceptions.

  • The '@[for]' variable specification supports tuples for tuple unpacking, even recursive tuples. However, it is limited in that the names included may only be valid Python identifiers, not arbitrary Python lvalues. Since the internal Python mechanism is very rarely used for this purpose (e.g., 'for (x, l[0], q.a) in sequence'), it is not thought to be a significant limitation.

Wish list

Here are some random ideas for future revisions of EmPy. If any of these are of particular interest to you, your input would be appreciated.

  • Some real-world examples should really be included for demonstrating the power and expressiveness of EmPy first-hand.

  • More extensive help (rather than a ridiculously long README), probably inherently using the EmPy system itself for building to HTML and other formats, thereby acting as a help facility and a demonstration of the working system.

  • A "trivial" mode, where all the EmPy system does is scan for simple symbols to replace them with evaluations/executions, rather than having to do the contextual scanning it does now. This has the down side of being much less configurable and powerful but the upside of being extremely efficient.

  • A "debug" mode, where EmPy prints the contents of everything it's about to evaluate (probably to stderr) before it does?

  • The ability to funnel all code through a configurable RExec for user-controlled security control. This would probably involve abstracting the execution functionality outside of the interpreter. [This suggestion is on hold until the rexec/Bastion exploits are worked out.]

  • Optimized handling of processing would be nice for the possibility of an Apache module devoted to EmPy processing.

  • An EmPy emacs mode.

  • An optimization of offloading diversions to files when they become truly huge. (This is made possible by the abstraction of the Diversion class.)

  • Support for mapping filters (specified by dictionaries).

  • Support for some sort of batch processing, where several EmPy files can be listed at once and all of them evaluated with the same initial (presumably expensive) environment. empy.saveGlobals and empy.restoreGlobals have been introduced as a partial solution, but they need to be made more robust.

  • A more elaborate interactive mode, perhaps with a prompt and readline support.

  • A StructuredText and/or reStructuredText filter would be quite useful, as would SGML/HTML/XML/XHTML, s-expression, Python, etc. auto-indenter filters.

  • An indexing filter, which can process text and pick out predefined keywords and thereby setup links to them.

  • The ability to rerun diverted material back through the interpreter. (This can be done, awkwardly, by manually creating a filter which itself contains an interpreter, but it might be helpful if this was an all-in-one operation.)

  • A caching system that stores off the compilations of repeated evaluations and executions so that in a persistent environment the same code does not have to be repeatedly evaluated/executed. This would probably be a necessity in an Apache module-based solution. Perhaps caching even to the point of generating pure PyWM bytecode?

  • An option to change the format of the standard EmPy errors in a traceback.

  • Support for some manner of implicitly processed /etc/empyrc and/or ~/.empyrc file, and of course an option to inhibit its processing. This can already be accomplished (and with greater control) via use of EMPY_OPTIONS, though.

  • More uniform handling of the preprocessing directives (-I, -D, -E, -F, and -P), probably mapping directly to methods in the Interpreter class.

  • Support for integration with mod_python.

  • In simple expressions, a {...} suffix has no meaning in Python (e.g., in Python, @x(...) is a call, @x[...] is subscription, but @x{...} is illegal). This could be exploited by having a {...} suffix in a simple expression representing an encapsulation of an expanded string; e.g., @bullet{There are @count people here} would be equivalent to @bullet(empy.expand("There are @count people here", locals()))}.

  • A tool to collect significator information from a hierarchy of .em files and put them in a database form available for individual scripts would be extremely useful -- this tool should be extensible so that users can use it to, say, build ordered hierarchies of their EmPy files by detecting contextual information like application-specific links to other EmPy documents.

  • Extensions of the basic EmPy concepts to projects for other interpreted languages, such as Java, Lua, Ruby, and/or Perl.

  • Ignore SystemExit when doing error handling, letting the exception progagate up? So far no one seems to worry about this; deliberately exiting early in a template seems to be an unlikely occurrence. (Furthermore, there are the os.abort and os._exit facilities for terminating without exception propagation.)

  • A new markup which is the equivalent of $...:...$ in source control systems, where the left-hand portion represents a keyword and the right-hand portion represents its value which is substituted in by the EmPy system.

  • The ability to obtain the filename (if relevant) and mode of the primary output file.

  • The ability to redirect multiple streams of output; not diversions, but rather the ability to write to one file and then another. Since output would be under the EmPy script's control, this would imply a useful --no-output option, where by default no output is written. This would also suggest the usefulness of all the output file delegates (diversions, filters, abstract files, etc.) passing unrecognized method calls all the way down to underlying file object.

  • In addition to the em.py script, an additional support library (non-executable) should be included which includes ancillary functionality for more advanced features, but which is not necessary to use EmPy in its basic form as a standalone executable. Such features would include things like significator processing, metadata scanning, and advanced prompting systems.

Release history

  • 3.3; 2003 Oct 27. Custom markup '@<...>'; remove separate pseudomodule instance for greater transparency; deprecate interpreter attribute of pseudomodule; deprecate auxiliary class name attributes associated with pseudomodule in preparation for separate support library in 4.0; add --no-callback-error and --no-bangpath-processing command line options; add atToken hook.

  • 3.2; 2003 Oct 7. Reengineer hooks support to use hook instances; add -v option; add --relative-path option; reversed PEP 317 style; modify Unicode support to give less confusing errors in the case of unknown encodings and error handlers; relicensed under LGPL.

  • 3.1.1; 2003 Sep 20. Add literal @"..." markup; add --pause-at-end command line option; fix improper globals collision error via the sys.stdout proxy.

  • 3.1; 2003 Aug 8. Unicode support (Python 2.0 and above); add Document and Processor helper classes for processing significators; add --no-prefix option for suppressing all markups.

  • 3.0.4; 2003 Aug 7. Implement somewhat more robust lvalue parsing for '@[for]' construct (thanks to Beni Cherniavsky for inspiration).

  • 3.0.3; 2003 Jul 9. Fix bug regarding recursive tuple unpacking using '@[for]'; add empy.saveGlobals, empy.restoreGlobals, and empy.defined functions.

  • 3.0.2; 2003 Jun 19. @? and @! markups for changing the current context name and line, respectively; add update method to interpreter; new and renamed context operations, empy.setContextName, empy.setContextLine, empy.pushContext, empy.popContext.

  • 3.0.1; 2003 Jun 9. Fix simple bug preventing command line preprocessing directives (-I, -D, -E, -F, -P) from executing properly; defensive PEP 317 compliance [defunct].

  • 3.0; 2003 Jun 1. Control markups with '@[...]'; remove substitutions (use control markups instead); support @(...?...!...) for conditional expressions in addition to the now-deprecated @(...?...:...) variety; add acknowledgements and glossary sections to documentation; rename buffering option back to -b; add -m option and EMPY_PSEUDO environment variable for changing the pseudomodule name; add -n option and EMPY_NO_OVERRIDE environment variable for suppressing sys.stdout proxy; rename main error class to 'Error'; add standalone expand function; add --binary and --chunk-size options; reengineer parsing system to use Tokens for easy extensibility; safeguard curly braces in simple expressions (meaningless in Python and thus likely a typographical error) by making them a parse error; fix bug involving custom Interpreter instances ignoring globals argument; distutils support.

  • 2.3; 2003 Feb 20. Proper and full support for concurrent and recursive interpreters; protection from closing the true stdout file object; detect edge cases of interpreter globals or sys.stdout proxy collisions; add globals manipulation functions empy.getGlobals, empy.setGlobals, and empy.updateGlobals which properly preserve the empy pseudomodule; separate usage info out into easily accessible lists for easier presentation; have -h option show simple usage and -H show extened usage; add NullFile utility class.

  • 2.2.6; 2003 Jan 30. Fix a bug in the Filter.detach method (which would not normally be called anyway).

  • 2.2.5; 2003 Jan 9. Strip carriage returns out of executed code blocks for DOS/Windows compatibility.

  • 2.2.4; 2002 Dec 23. Abstract Filter interface to use methods only; add @[noop: ...] substitution for completeness and block commenting [defunct].

  • 2.2.3; 2002 Dec 16. Support compatibility with Jython by working around a minor difference between CPython and Jython in string splitting.

  • 2.2.2; 2002 Dec 14. Include better docstrings for pseudomodule functions; segue to a dictionary-based options system for interpreters; add empy.clearAllHooks and 'empy.clearGlobals'; include a short documentation section on embedding interpreters; fix a bug in significator regular expression.

  • 2.2.1; 2002 Nov 30. Tweak test script to avoid writing unnecessary temporary file; add Interpreter.single method; expose evaluate, execute, substitute [defunct], and single methods to the pseudomodule; add (rather obvious) EMPY_OPTIONS environment variable support; add empy.enableHooks and 'empy.disableHooks'; include optimization to transparently disable hooks until they are actually used.

  • 2.2; 2002 Nov 21. Switched to -V option for version information; empy.createDiversion for creating initially empty diversion; direct access to diversion objects with 'empy.retrieveDiversion'; environment variable support; removed --raw long argument (use --raw-errors instead); added quaternary escape code (well, why not).

  • 2.1; 2002 Oct 18. empy.atExit registry separate from hooks to allow for normal interpreter support; include a benchmark sample and test.sh verification script; expose empy.string directly; -D option for explicit defines on command line; remove ill-conceived support for @else: separator in @[if ...] substitution [defunct] ; handle nested substitutions properly [defunct] ; @[macro ...] substitution for creating recallable expansions [defunct].

  • 2.0.1; 2002 Oct 8. Fix missing usage information; fix after_evaluate hook not getting called; add empy.atExit call to register values.

  • 2.0; 2002 Sep 30. Parsing system completely revamped and simplified, eliminating a whole class of context-related bugs; builtin support for buffered filters; support for registering hooks; support for command line arguments; interactive mode with -i; significator value extended to be any valid Python expression.

  • 1.5.1; 2002 Sep 24. Allow @] to represent unbalanced close brackets in @[...] markups [defunct].

  • 1.5; 2002 Sep 18. Escape codes (@\...); conditional and repeated expansion substitutions [defunct] ; replaced with control markups]; fix a few bugs involving files which do not end in newlines.

  • 1.4; 2002 Sep 7. Fix bug with triple quotes; collapse conditional and protected expression syntaxes into the single generalized @(...) notation; empy.setName and empy.setLine functions [deprecated] ; true support for multiple concurrent interpreters with improved sys.stdout proxy; proper support for empy.expand to return a string evaluated in a subinterpreter as intended; merged Context and Parser classes together, and separated out Scanner functionality.

  • 1.3; 2002 Aug 24. Pseudomodule as true instance; move toward more verbose (and clear) pseudomodule functions; fleshed out diversion model; filters; conditional expressions; protected expressions; preprocessing with -P (in preparation for possible support for command line arguments).

  • 1.2; 2002 Aug 16. Treat bangpaths as comments; empy.quote for the opposite process of 'empy.expand'; significators (@%... sequences); -I option; -f option; much improved documentation.

  • 1.1.5; 2002 Aug 15. Add a separate invoke function that can be called multiple times with arguments to simulate multiple runs.

  • 1.1.4; 2002 Aug 12. Handle strings thrown as exceptions properly; use getopt to process command line arguments; cleanup file buffering with AbstractFile; very slight documentation and code cleanup.

  • 1.1.3; 2002 Aug 9. Support for changing the prefix from within the empy pseudomodule.

  • 1.1.2; 2002 Aug 5. Renamed buffering option [defunct], added -F option for interpreting Python files from the command line, fixed improper handling of exceptions from command line options (-E, -F).

  • 1.1.1; 2002 Aug 4. Typo bugfixes; documentation clarification.

  • 1.1; 2002 Aug 4. Added option for fully buffering output (including file opens), executing commands through the command line; some documentation errors fixed.

  • 1.0; 2002 Jul 23. Renamed project to EmPy. Documentation and sample tweaks; added empy.flatten. Added -a option.

  • 0.3; 2002 Apr 14. Extended "simple expression" syntax, interpreter abstraction, proper context handling, better error handling, explicit file inclusion, extended samples.

  • 0.2; 2002 Apr 13. Bugfixes, support non-expansion of Nones, allow choice of alternate prefix.

  • 0.1.1; 2002 Apr 12. Bugfixes, support for Python 1.5.x, add -r option.

  • 0.1; 2002 Apr 12. Initial early access release.

Author

This module was written by Erik Max Francis. If you use this software, have suggestions for future releases, or bug reports, I'd love to hear about it.

Even if you try out EmPy for a project and find it unsuitable, I'd like to know what stumbling blocks you ran into so they can potentially be addressed in a future version.

Version

Version 3.3 $Date: 2003/10/27 $ $Author: max $

Modules and Packages   

em

A system for processing Python as markup embedded in text.


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/0000755000175000017500000000000007747157077013072 5ustar anaanaempy-3.3.orig/doc/home/max/0000755000175000017500000000000007747157077013657 5ustar anaanaempy-3.3.orig/doc/home/max/projects/0000755000175000017500000000000007747157077015510 5ustar anaanaempy-3.3.orig/doc/home/max/projects/empy/0000755000175000017500000000000007747157077016462 5ustar anaanaempy-3.3.orig/doc/home/max/projects/empy/doc/0000755000175000017500000000000007747157077017227 5ustar anaanaempy-3.3.orig/doc/home/max/projects/empy/doc/em.html0000644000175000017500000004472107747162555020523 0ustar anaana Module: em

Table of Contents

Module: em em.py

A system for processing Python as markup embedded in text.

Imported modules   
import copy
import getopt
import os
import re
import string
import sys
import types
Functions   
environment
expand
info
invoke
main
usage
  environment 
environment ( name,  default=None )

Get data from the current environment. If the default is True or False, then presume that we're only interested in the existence or non-existence of the environment variable.

  expand 
expand (
        _data,
        _globals=None,
        _argv=None,
        _prefix=DEFAULT_PREFIX,
        _pseudo=None,
        _options=None,
        **_locals,
        )

Do an atomic expansion of the given source data, creating and shutting down an interpreter dedicated to the task. The sys.stdout object is saved off and then replaced before this function returns.

  info 
info ( table )

  invoke 
invoke ( args )

Run a standalone instance of an EmPy interpeter.

Exceptions   
Error, "prefix must be single-character string"
ValueError, "-b only makes sense with -o or -a arguments"
  main 
main ()

  usage 
usage ( verbose=True )

Print usage information.

Classes   

AbstractFile

An abstracted file that, when buffered, will totally buffer the

BreakFlow

A break control flow.

BufferedFilter

A buffered filter is one that doesn't modify the source data

CommentToken

A comment markup.

Context

An interpreter context, which encapsulates a name, an input

ContextLineToken

A context line change markup.

ContextNameToken

A context name change markup.

ContinueFlow

A continue control flow.

ControlToken

A control token.

CustomToken

A custom markup.

Diversion

The representation of an active diversion. Diversions act as

DiversionError

An error related to diversions.

Document

A representation of an individual EmPy document, as used by a

Error

The base class for all EmPy errors.

EscapeToken

An escape markup.

ExpansionToken

A token that involves an expansion.

ExpressionToken

An expression markup.

Filter

An abstract filter.

FilterError

An error related to filters.

FlowError

An exception related to control flow.

FunctionFilter

A filter that works simply by pumping its input through a

Hook

The base class for implementing hooks.

InPlaceToken

An in-place markup.

Interpreter

An interpreter can process chunks of EmPy code.

LineBufferedFilter

A line-buffered filter only lets data through when it sees

LiteralToken

A literal markup.

MaximallyBufferedFilter

A maximally-buffered filter only lets its data through on the final

MetaError

A wrapper around a real Python exception for including a copy of

NullFile

A simple class that supports all the file-like object methods

NullFilter

A filter that never sends any output to its sink.

NullToken

A chunk of data not containing markups.

ParseError

A parse error occurred.

PrefixToken

A prefix markup.

Processor

An entity which is capable of processing a hierarchy of EmPy

ProxyFile

The proxy file object that is intended to take the place of

ReprToken

A repr markup.

Scanner

A scanner holds a buffer for lookahead parsing and has the

SignificatorToken

A significator markup.

SimpleExpressionToken

A simple expression markup.

SizeBufferedFilter

A size-buffered filter only in fixed size chunks (excepting the

Stack

A simple stack that behaves as a sequence (with 0 being the top

StackUnderflowError

A stack underflow.

StatementToken

A statement markup.

Stream

A wrapper around an (output) file object which supports

StringFilter

A filter that takes a translation string (256 characters) and

StringLiteralToken

A string token markup.

Subsystem

The subsystem class defers file creation so that it can create

SubsystemError

An error associated with the Unicode subsystem.

Token

An element of expansion.

TransientParseError

A parse error occurred which may be resolved by feeding more data.

UncloseableFile

A simple class which wraps around a delegate file-like object

VerboseHook

A verbose hook that reports all information received by the

WhitespaceToken

A whitespace markup.


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/0000755000175000017500000000000007747157100017613 5ustar anaanaempy-3.3.orig/doc/home/max/projects/empy/doc/em/AbstractFile.html0000644000175000017500000001632607747162554023065 0ustar anaana Class: AbstractFile

Table of Contents

Class: AbstractFile em.py

An abstracted file that, when buffered, will totally buffer the file, including even the file open.

Methods   
__del__
__init__
abort
close
commit
flush
write
writelines
  __del__ 
__del__ ( self )

  __init__ 
__init__ (
        self,
        filename,
        mode='w',
        buffered=False,
        )

  abort 
abort ( self )

  close 
close ( self )

  commit 
commit ( self )

  flush 
flush ( self )

  write 
write ( self,  data )

  writelines 
writelines ( self,  data )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/BreakFlow.html0000644000175000017500000000405507747162554022372 0ustar anaana Class: BreakFlow

Table of Contents

Class: BreakFlow em.py

A break control flow.

Base Classes   
FlowError

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/BufferedFilter.html0000644000175000017500000001125707747162554023410 0ustar anaana Class: BufferedFilter

Table of Contents

Class: BufferedFilter em.py

A buffered filter is one that doesn't modify the source data sent to the sink, but instead holds it for a time. The standard variety only sends the data along when it receives a flush command.

Base Classes   
Filter
Methods   
__init__
flush
write
  __init__ 
__init__ ( self )

  flush 
flush ( self )

  write 
write ( self,  data )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/CommentToken.html0000644000175000017500000001113407747162554023115 0ustar anaana Class: CommentToken

Table of Contents

Class: CommentToken em.py

A comment markup.

Base Classes   
ExpansionToken
Methods   
scan
string
  scan 
scan ( self,  scanner )

Exceptions   
TransientParseError, "comment expects newline"
  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Context.html0000644000175000017500000001115307747162554022137 0ustar anaana Class: Context

Table of Contents

Class: Context em.py

An interpreter context, which encapsulates a name, an input file object, and a parser object.

Methods   
__init__
__str__
bump
identify
  __init__ 
__init__ (
        self,
        name,
        line=0,
        units=DEFAULT_UNIT,
        )

  __str__ 
__str__ ( self )

  bump 
bump ( self,  quantity=1 )

  identify 
identify ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ContextLineToken.html0000644000175000017500000001131407747162554023747 0ustar anaana Class: ContextLineToken

Table of Contents

Class: ContextLineToken em.py

A context line change markup.

Base Classes   
ExpansionToken
Methods   
run
scan
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

Exceptions   
ParseError, "context line requires integer"
TransientParseError, "context line expects newline"

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ContextNameToken.html0000644000175000017500000001123407747162554023741 0ustar anaana Class: ContextNameToken

Table of Contents

Class: ContextNameToken em.py

A context name change markup.

Base Classes   
ExpansionToken
Methods   
run
scan
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

Exceptions   
TransientParseError, "context name expects newline"

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ContinueFlow.html0000644000175000017500000000406607747162554023134 0ustar anaana Class: ContinueFlow

Table of Contents

Class: ContinueFlow em.py

A continue control flow.

Base Classes   
FlowError

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ControlToken.html0000644000175000017500000002670507747162554023145 0ustar anaana Class: ControlToken

Table of Contents

Class: ControlToken em.py

A control token.

Base Classes   
ExpansionToken
Methods   
build
run
scan
string
subrun
subscan
substring
  build 
build ( self,  allowed=None )

Process the list of subtokens and divide it into a list of 2-tuples, consisting of the dividing tokens and the list of subtokens that follow them. If allowed is specified, it will represent the list of the only secondary markup types which are allowed.

Exceptions   
ParseError, "control unexpected secondary: '%s'" % subtoken.type
  run 
run (
        self,
        interpreter,
        locals,
        )

Exceptions   
BreakFlow, "control 'break' without 'for', 'while'"
ContinueFlow, "control 'continue' without 'for', 'while'"
ParseError, "control '%s' cannot be at this level" % self.type
ParseError, "control 'end' requires primary markup"
ParseError, "control 'for' expects at most one 'else'"
ParseError, "control 'if' unexpected secondary: '%s'" % secondary.type
ParseError, "control 'try' can only have one 'finally'"
ParseError, "control 'try' cannot have 'except' and 'finally'"
ParseError, "control 'try' needs 'except' or 'finally'"
ParseError, "control 'while' expects at most one 'else'"
ParseError, "control expected 'for x in seq'"
  scan 
scan ( self,  scanner )

Exceptions   
ParseError, "control '%s' needs arguments" % self.type
ParseError, "unknown control markup: '%s'" % self.type
  string 
string ( self )

  subrun 
subrun (
        self,
        tokens,
        interpreter,
        locals,
        )

Execute a sequence of tokens.

  subscan 
subscan (
        self,
        scanner,
        primary,
        )

Do a subscan for contained tokens.

Exceptions   
ParseError, "control must end with 'end %s'" % primary
TransientParseError, "control '%s' needs more tokens" % primary
  substring 
substring ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/CustomToken.html0000644000175000017500000001111407747162554022763 0ustar anaana Class: CustomToken

Table of Contents

Class: CustomToken em.py

A custom markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Diversion.html0000644000175000017500000001521307747162554022456 0ustar anaana Class: Diversion

Table of Contents

Class: Diversion em.py

The representation of an active diversion. Diversions act as (writable) file objects, and then can be recalled either as pure strings or (readable) file objects.

Methods   
__init__
asFile
asString
close
flush
write
writelines
  __init__ 
__init__ ( self )

  asFile 
asFile ( self )

Return the diversion as a file.

  asString 
asString ( self )

Return the diversion as a string.

  close 
close ( self )

  flush 
flush ( self )

  write 
write ( self,  data )

  writelines 
writelines ( self,  lines )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/DiversionError.html0000644000175000017500000000401107747162554023462 0ustar anaana Class: DiversionError

Table of Contents

Class: DiversionError em.py

An error related to diversions.

Base Classes   
Error
    Exception

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Document.html0000644000175000017500000000515007747162554022271 0ustar anaana Class: Document

Table of Contents

Class: Document em.py

A representation of an individual EmPy document, as used by a processor.

Methods   
__init__
  __init__ 
__init__ (
        self,
        ID,
        filename,
        )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Error.html0000644000175000017500000000371307747162554021607 0ustar anaana Class: Error

Table of Contents

Class: Error em.py

The base class for all EmPy errors.

Base Classes   
Exception

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/EscapeToken.html0000644000175000017500000001274407747162554022723 0ustar anaana Class: EscapeToken

Table of Contents

Class: EscapeToken em.py

An escape markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

Exceptions   
ParseError, "invalid escape control code"
ParseError, "invalid numeric escape code"
ParseError, "unrecognized escape code"
ParseError, r"Unicode name escape should be \N{...}"
SubsystemError, "unknown Unicode character name: %s" % name
  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ExpansionToken.html0000644000175000017500000001113507747162554023460 0ustar anaana Class: ExpansionToken

Table of Contents

Class: ExpansionToken em.py

A token that involves an expansion.

Base Classes   
Token
Methods   
__init__
run
scan
  __init__ 
__init__ (
        self,
        prefix,
        first,
        )

  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ExpressionToken.html0000644000175000017500000001113107747162554023647 0ustar anaana Class: ExpressionToken

Table of Contents

Class: ExpressionToken em.py

An expression markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Filter.html0000644000175000017500000002461307747162555021746 0ustar anaana Class: Filter

Table of Contents

Class: Filter em.py

An abstract filter.

Methods   
__init__
_flush
attach
close
detach
flush
last
next
write
writelines
  __init__ 
__init__ ( self )

Exceptions   
NotImplementedError
  _flush 
_flush ( self )

The _flush method should always flush the sink and should not be overridden.

  attach 
attach ( self,  filter )

Attach a filter to this one.

  close 
close ( self )

Close the filter. Do an explicit flush first, then close the sink.

  detach 
detach ( self )

Detach a filter from its sink.

  flush 
flush ( self )

The flush method can be overridden.

  last 
last ( self )

Find the last filter in this chain.

  next 
next ( self )

Return the next filter/file-like object in the sequence, or None.

  write 
write ( self,  data )

The standard write method; this must be overridden in subclasses.

Exceptions   
NotImplementedError
  writelines 
writelines ( self,  lines )

Standard writelines wrapper.


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/FilterError.html0000644000175000017500000000400007747162555022744 0ustar anaana Class: FilterError

Table of Contents

Class: FilterError em.py

An error related to filters.

Base Classes   
Error
    Exception

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/FlowError.html0000644000175000017500000000400507747162555022433 0ustar anaana Class: FlowError

Table of Contents

Class: FlowError em.py

An exception related to control flow.

Base Classes   
Error
    Exception

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/FunctionFilter.html0000644000175000017500000000770307747162555023455 0ustar anaana Class: FunctionFilter

Table of Contents

Class: FunctionFilter em.py

A filter that works simply by pumping its input through a function which maps strings into strings.

Base Classes   
Filter
Methods   
__init__
write
  __init__ 
__init__ ( self,  function )

  write 
write ( self,  data )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Hook.html0000644000175000017500000011545507747162555021426 0ustar anaana Class: Hook

Table of Contents

Class: Hook em.py

The base class for implementing hooks.

Methods   
__init__
afterAtomic
afterBinary
afterClause
afterControl
afterDefined
afterEscape
afterEvaluate
afterExecute
afterExpand
afterFile
afterImport
afterInclude
afterLiteral
afterMulti
afterQuote
afterSerialize
afterSignificate
afterSingle
afterString
atFinalize
atHandle
atInteract
atParse
atReady
atShutdown
atStartup
atToken
beforeAtomic
beforeBinary
beforeClause
beforeControl
beforeDefined
beforeEscape
beforeEvaluate
beforeExecute
beforeExpand
beforeFile
beforeImport
beforeInclude
beforeLiteral
beforeMulti
beforeQuote
beforeSerialize
beforeSignificate
beforeSingle
beforeString
deregister
null
pop
push
register
  __init__ 
__init__ ( self )

  afterAtomic 
afterAtomic ( self )

  afterBinary 
afterBinary ( self )

  afterClause 
afterClause (
        self,
        exception,
        variable,
        )

  afterControl 
afterControl ( self )

  afterDefined 
afterDefined ( self,  result )

  afterEscape 
afterEscape ( self,  result )

  afterEvaluate 
afterEvaluate ( self,  result )

  afterExecute 
afterExecute ( self )

  afterExpand 
afterExpand ( self,  result )

  afterFile 
afterFile ( self )

  afterImport 
afterImport ( self )

  afterInclude 
afterInclude ( self )

  afterLiteral 
afterLiteral ( self )

  afterMulti 
afterMulti ( self )

  afterQuote 
afterQuote ( self,  result )

  afterSerialize 
afterSerialize ( self )

  afterSignificate 
afterSignificate ( self )

  afterSingle 
afterSingle ( self )

  afterString 
afterString ( self )

  atFinalize 
atFinalize ( self )

  atHandle 
atHandle ( self,  meta )

  atInteract 
atInteract ( self )

  atParse 
atParse (
        self,
        scanner,
        locals,
        )

  atReady 
atReady ( self )

  atShutdown 
atShutdown ( self )

  atStartup 
atStartup ( self )

  atToken 
atToken ( self,  token )

  beforeAtomic 
beforeAtomic (
        self,
        name,
        value,
        locals,
        )

  beforeBinary 
beforeBinary (
        self,
        name,
        file,
        chunkSize,
        locals,
        )

  beforeClause 
beforeClause (
        self,
        catch,
        locals,
        )

  beforeControl 
beforeControl (
        self,
        type,
        rest,
        locals,
        )

  beforeDefined 
beforeDefined (
        self,
        name,
        locals,
        )

  beforeEscape 
beforeEscape (
        self,
        string,
        more,
        )

  beforeEvaluate 
beforeEvaluate (
        self,
        expression,
        locals,
        )

  beforeExecute 
beforeExecute (
        self,
        statements,
        locals,
        )

  beforeExpand 
beforeExpand (
        self,
        string,
        locals,
        )

  beforeFile 
beforeFile (
        self,
        name,
        file,
        locals,
        )

  beforeImport 
beforeImport (
        self,
        name,
        locals,
        )

  beforeInclude 
beforeInclude (
        self,
        name,
        file,
        locals,
        )

  beforeLiteral 
beforeLiteral ( self,  text )

  beforeMulti 
beforeMulti (
        self,
        name,
        values,
        locals,
        )

  beforeQuote 
beforeQuote ( self,  string )

  beforeSerialize 
beforeSerialize (
        self,
        expression,
        locals,
        )

  beforeSignificate 
beforeSignificate (
        self,
        key,
        value,
        locals,
        )

  beforeSingle 
beforeSingle (
        self,
        source,
        locals,
        )

  beforeString 
beforeString (
        self,
        name,
        string,
        locals,
        )

  deregister 
deregister ( self,  interpreter )

Exceptions   
Error, "hook not associated with this interpreter"
  null 
null ( self )

  pop 
pop ( self )

  push 
push ( self )

  register 
register ( self,  interpreter )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/HookError.html0000644000175000017500000000400307747157077022425 0ustar anaana Class: HookError

Table of Contents

Class: HookError em.py

An exception associated with hooks.

Base Classes   
Error
    Exception

Table of Contents

This document was automatically generated on Mon Oct 27 00:58:36 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/InPlaceToken.html0000644000175000017500000001112107747162555023023 0ustar anaana Class: InPlaceToken

Table of Contents

Class: InPlaceToken em.py

An in-place markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Interpreter.html0000644000175000017500000023106007747162555023020 0ustar anaana Class: Interpreter

Table of Contents

Class: Interpreter em.py

An interpreter can process chunks of EmPy code.

Methods   
__del__
__init__
__repr__
addHook
areHooksEnabled
assign
atExit
atomic
attachFilter
binary
clause
clear
clearGlobals
clearHooks
close
context
createDiversion
defined
deregister
deregisterCallback
disableHooks
enableHooks
escape
evaluate
execute
expand
fail
file
finalize
fix
flatten
flush
getAllDiversions
getCallback
getCurrentDiversion
getFilter
getGlobals
getHooks
getPrefix
handle
identify
import_
include
installProxy
interact
invoke
invokeCallback
invokeHook
literal
meta
multi
nullFilter
ok
parse
playAllDiversions
playDiversion
pop
popContext
purgeAllDiversions
purgeDiversion
push
pushContext
quote
ready
register
registerCallback
removeHook
replayAllDiversions
replayDiversion
reset
resetFilter
restore
restoreGlobals
retrieveDiversion
safe
save
saveGlobals
serialize
setContextLine
setContextName
setFilter
setGlobals
setPrefix
shutdown
significate
single
startDiversion
stopDiverting
stream
string
tokenize
unfix
update
updateGlobals
wrap
write
writelines
  __del__ 
__del__ ( self )

  __init__ 
__init__ (
        self,
        output=None,
        argv=None,
        prefix=DEFAULT_PREFIX,
        pseudo=None,
        options=None,
        globals=None,
        hooks=None,
        )

  __repr__ 
__repr__ ( self )

  addHook 
addHook (
        self,
        hook,
        prepend=False,
        )

Add a new hook; optionally insert it rather than appending it.

  areHooksEnabled 
areHooksEnabled ( self )

Return whether or not hooks are presently enabled.

  assign 
assign (
        self,
        name,
        value,
        locals=None,
        )

Do a potentially complex (including tuple unpacking) assignment.

  atExit 
atExit ( self,  callable )

Register a function to be called at exit.

  atomic 
atomic (
        self,
        name,
        value,
        locals=None,
        )

Do an atomic assignment.

  attachFilter 
attachFilter ( self,  shortcut )

Attach a single filter to the end of the current filter chain.

  binary 
binary (
        self,
        file,
        name='<binary>',
        chunkSize=0,
        locals=None,
        )

Parse the entire contents of a file-like object, in chunks.

  clause 
clause (
        self,
        catch,
        locals=None,
        )

Given the string representation of an except clause, turn it into a 2-tuple consisting of the class name, and either a variable name or None.

  clear 
clear ( self )

Clear out the globals dictionary with a brand new one.

  clearGlobals 
clearGlobals ( self )

Clear out the globals with a brand new dictionary.

  clearHooks 
clearHooks ( self )

Clear all hooks.

  close 
close ( self )

  context 
context ( self )

  createDiversion 
createDiversion ( self,  name )

Create a diversion (but do not divert to it) if it does not already exist.

  defined 
defined (
        self,
        name,
        locals=None,
        )

Return a Boolean indicating whether or not the name is defined either in the locals or the globals.

  deregister 
deregister ( self,  hook )

Remove an already registered hook.

  deregisterCallback 
deregisterCallback ( self )

Remove any previously registered callback with this interpreter.

  disableHooks 
disableHooks ( self )

Disable hooks.

  enableHooks 
enableHooks ( self )

Enable hooks.

  escape 
escape (
        self,
        data,
        more='',
        )

Escape a string so that nonprintable characters are replaced with compatible EmPy expansions.

  evaluate 
evaluate (
        self,
        expression,
        locals=None,
        )

Evaluate an expression.

  execute 
execute (
        self,
        statements,
        locals=None,
        )

Execute a statement.

  expand 
expand (
        self,
        data,
        locals=None,
        )

Do an explicit expansion on a subordinate stream.

  fail 
fail (
        self,
        error,
        fatal=False,
        )

Handle an actual error that occurred.

  file 
file (
        self,
        file,
        name='<file>',
        locals=None,
        )

Parse the entire contents of a file-like object, line by line.

  finalize 
finalize ( self )

Execute any remaining final routines.

  fix 
fix ( self )

Reset the globals, stamping in the pseudomodule.

Exceptions   
Error, "interpreter globals collision"
  flatten 
flatten ( self,  keys=None )

Flatten the contents of the pseudo-module into the globals namespace.

  flush 
flush ( self )

  getAllDiversions 
getAllDiversions ( self )

Get the names of all existing diversions.

  getCallback 
getCallback ( self )

Get the callback registered with this interpreter, or None.

  getCurrentDiversion 
getCurrentDiversion ( self )

Get the name of the current diversion.

  getFilter 
getFilter ( self )

Get the current filter.

  getGlobals 
getGlobals ( self )

Retrieve the globals.

  getHooks 
getHooks ( self )

Get the current hooks.

  getPrefix 
getPrefix ( self )

Get the current prefix.

  handle 
handle ( self,  meta )

Handle a MetaError.

  identify 
identify ( self )

Identify the topmost context with a 2-tuple of the name and line number.

  import_ 
import_ (
        self,
        name,
        locals=None,
        )

Do an import.

  include 
include (
        self,
        fileOrFilename,
        locals=None,
        )

Do an include pass on a file or filename.

  installProxy 
installProxy ( self )

Install a proxy if necessary.

Exceptions   
Error, "interpreter stdout proxy lost"
  interact 
interact ( self )

Perform interaction.

  invoke 
invoke (
        self,
        _name,
        **keywords,
        )

Invoke the hook(s) associated with the hook name, should they exist.

  invokeCallback 
invokeCallback ( self,  contents )

Invoke the callback.

Exceptions   
Error, "custom markup invoked with no defined callback"
  invokeHook 
invokeHook (
        self,
        _name,
        **keywords,
        )

Manually invoke a hook.

  literal 
literal ( self,  text )

Process a string literal.

  meta 
meta ( self,  exc=None )

Construct a MetaError for the interpreter's current state.

  multi 
multi (
        self,
        names,
        values,
        locals=None,
        )

Do a (potentially recursive) assignment.

Exceptions   
TypeError, "unpack non-sequence"
ValueError, "unpack tuple of wrong size"
  nullFilter 
nullFilter ( self )

Install a filter that will consume all text.

  ok 
ok ( self )

Is the interpreter still active?

  parse 
parse (
        self,
        scanner,
        locals=None,
        )

Parse and run as much from this scanner as possible.

  playAllDiversions 
playAllDiversions ( self )

Play all existing diversions and then purge them.

  playDiversion 
playDiversion ( self,  name )

Play the given diversion and then purge it.

  pop 
pop ( self )

  popContext 
popContext ( self )

Pop the top context.

  purgeAllDiversions 
purgeAllDiversions ( self )

Purge all existing diversions.

  purgeDiversion 
purgeDiversion ( self,  name )

Eliminate the given diversion.

  push 
push ( self )

  pushContext 
pushContext (
        self,
        name='<unnamed>',
        line=0,
        )

Create a new context and push it.

  quote 
quote ( self,  data )

Quote the given string so that if it were expanded it would evaluate to the original.

  ready 
ready ( self )

Declare the interpreter ready for normal operations.

  register 
register (
        self,
        hook,
        prepend=False,
        )

Register the provided hook.

  registerCallback 
registerCallback ( self,  callback )

Register a custom markup callback with this interpreter.

  removeHook 
removeHook ( self,  hook )

Remove a preexisting hook.

  replayAllDiversions 
replayAllDiversions ( self )

Replay all existing diversions without purging them.

  replayDiversion 
replayDiversion ( self,  name )

Replay the diversion without purging it.

  reset 
reset ( self )

  resetFilter 
resetFilter ( self )

Reset the filter so that it does no filtering.

  restore 
restore ( self,  destructive=True )

Restore the topmost historic globals.

  restoreGlobals 
restoreGlobals ( self,  destructive=True )

Restore the most recently saved copy of the globals.

  retrieveDiversion 
retrieveDiversion ( self,  name )

Retrieve the diversion object associated with the name.

  safe 
safe (
        self,
        scanner,
        final=False,
        locals=None,
        )

Do a protected parse. Catch transient parse errors; if final is true, then make a final pass with a terminator, otherwise ignore the transient parse error (more data is pending).

  save 
save ( self,  deep=True )

  saveGlobals 
saveGlobals ( self,  deep=True )

Save a copy of the globals off onto the history stack.

  serialize 
serialize (
        self,
        expression,
        locals=None,
        )

Do an expansion, involving evaluating an expression, then converting it to a string and writing that string to the output if the evaluation is not None.

  setContextLine 
setContextLine ( self,  line )

Set the name of the topmost context.

  setContextName 
setContextName ( self,  name )

Set the name of the topmost context.

  setFilter 
setFilter ( self,  shortcut )

Set the filter.

  setGlobals 
setGlobals ( self,  globals )

Set the globals to the specified dictionary.

  setPrefix 
setPrefix ( self,  prefix )

Set the prefix.

  shutdown 
shutdown ( self )

Declare this interpreting session over; close the stream file object. This method is idempotent.

  significate 
significate (
        self,
        key,
        value=None,
        locals=None,
        )

Declare a significator.

  single 
single (
        self,
        source,
        locals=None,
        )

Execute an expression or statement, just as if it were entered into the Python interactive interpreter.

  startDiversion 
startDiversion ( self,  name )

Start diverting to the given diversion name.

  stopDiverting 
stopDiverting ( self )

Stop any diverting.

  stream 
stream ( self )

  string 
string (
        self,
        data,
        name='<string>',
        locals=None,
        )

Parse a string.

  tokenize 
tokenize ( self,  name )

Take an lvalue string and return a name or a (possibly recursive) list of names.

Exceptions   
ParseError, "unexpected assignment token: '%s'" % garbage
  unfix 
unfix ( self )

Remove the pseudomodule (if present) from the globals.

  update 
update ( self,  other )

Update the current globals dictionary with another dictionary.

  updateGlobals 
updateGlobals ( self,  otherGlobals )

Merge another mapping object into this interpreter's globals.

  wrap 
wrap (
        self,
        callable,
        args,
        )

Wrap around an application of a callable and handle errors. Return whether no error occurred.

  write 
write ( self,  data )

  writelines 
writelines ( self,  stuff )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/LineBufferedFilter.html0000644000175000017500000000774607747162555024231 0ustar anaana Class: LineBufferedFilter

Table of Contents

Class: LineBufferedFilter em.py

A line-buffered filter only lets data through when it sees whole lines.

Base Classes   
BufferedFilter
Methods   
__init__
write
  __init__ 
__init__ ( self )

  write 
write ( self,  data )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/LiteralToken.html0000644000175000017500000000767107747162555023123 0ustar anaana Class: LiteralToken

Table of Contents

Class: LiteralToken em.py

A literal markup.

Base Classes   
ExpansionToken
Methods   
run
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/MaximallyBufferedFilter.html0000644000175000017500000001122207747162555025257 0ustar anaana Class: MaximallyBufferedFilter

Table of Contents

Class: MaximallyBufferedFilter em.py

A maximally-buffered filter only lets its data through on the final close. It ignores flushes.

Base Classes   
BufferedFilter
Methods   
__init__
close
flush
  __init__ 
__init__ ( self )

  close 
close ( self )

  flush 
flush ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/MetaError.html0000644000175000017500000000766407747162555022430 0ustar anaana Class: MetaError

Table of Contents

Class: MetaError em.py

A wrapper around a real Python exception for including a copy of the context.

Base Classes   
Exception
Methods   
__init__
__str__
  __init__ 
__init__ (
        self,
        contexts,
        exc,
        )

  __str__ 
__str__ ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/NullFile.html0000644000175000017500000001227207747162555022231 0ustar anaana Class: NullFile

Table of Contents

Class: NullFile em.py

A simple class that supports all the file-like object methods but simply does nothing at all.

Methods   
__init__
close
flush
write
writelines
  __init__ 
__init__ ( self )

  close 
close ( self )

  flush 
flush ( self )

  write 
write ( self,  data )

  writelines 
writelines ( self,  lines )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/NullFilter.html0000644000175000017500000000633207747162555022577 0ustar anaana Class: NullFilter

Table of Contents

Class: NullFilter em.py

A filter that never sends any output to its sink.

Base Classes   
Filter
Methods   
write
  write 
write ( self,  data )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/NullToken.html0000644000175000017500000001105607747162555022431 0ustar anaana Class: NullToken

Table of Contents

Class: NullToken em.py

A chunk of data not containing markups.

Base Classes   
Token
Methods   
__init__
run
string
  __init__ 
__init__ ( self,  data )

  run 
run (
        self,
        interpreter,
        locals,
        )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ParseError.html0000644000175000017500000000377107747162555022607 0ustar anaana Class: ParseError

Table of Contents

Class: ParseError em.py

A parse error occurred.

Base Classes   
Error
    Exception

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/PrefixToken.html0000644000175000017500000000766607747162555022770 0ustar anaana Class: PrefixToken

Table of Contents

Class: PrefixToken em.py

A prefix markup.

Base Classes   
ExpansionToken
Methods   
run
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Processor.html0000644000175000017500000001706207747162555022500 0ustar anaana Class: Processor

Table of Contents

Class: Processor em.py

An entity which is capable of processing a hierarchy of EmPy files and building a dictionary of document objects associated with them describing their significator contents.

Methods   
__init__
clear
directory
file
identifier
line
postprocess
scan
  __init__ 
__init__ ( self,  factory=Document )

  clear 
clear ( self )

  directory 
directory (
        self,
        basename,
        dirCriteria,
        fileCriteria,
        depth=None,
        )

  file 
file (
        self,
        document,
        file,
        )

  identifier 
identifier (
        self,
        pathname,
        filename,
        )

  line 
line (
        self,
        document,
        line,
        )

  postprocess 
postprocess ( self )

  scan 
scan (
        self,
        basename,
        extensions=DEFAULT_EMPY_EXTENSIONS,
        )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ProxyFile.html0000644000175000017500000002163507747162555022443 0ustar anaana Class: ProxyFile

Table of Contents

Class: ProxyFile em.py

The proxy file object that is intended to take the place of sys.stdout. The proxy can manage a stack of file objects it is writing to, and an underlying raw file object.

Methods   
__init__
_testProxy
clear
close
current
flush
pop
push
write
writelines
  __init__ 
__init__ ( self,  bottom )

  _testProxy 
_testProxy ( self )

  clear 
clear ( self,  interpreter )

  close 
close ( self )

Close the current file. If the current file is the bottom, then close it and dispose of it.

  current 
current ( self )

Get the current stream to write to.

  flush 
flush ( self )

  pop 
pop ( self,  interpreter )

  push 
push ( self,  interpreter )

  write 
write ( self,  data )

  writelines 
writelines ( self,  lines )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/ReprToken.html0000644000175000017500000001110607747162555022423 0ustar anaana Class: ReprToken

Table of Contents

Class: ReprToken em.py

A repr markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Scanner.html0000644000175000017500000007515207747162555022116 0ustar anaana Class: Scanner

Table of Contents

Class: Scanner em.py

A scanner holds a buffer for lookahead parsing and has the ability to scan for special symbols and indicators in that buffer.

Methods   
__getitem__
__getslice__
__init__
__len__
__nonzero__
acquire
advance
check
chop
complex
feed
find
last
nested
next
one
phrase
quote
read
release
rest
retreat
set
simple
sync
unsync
word
  __getitem__ 
__getitem__ ( self,  index )

  __getslice__ 
__getslice__ (
        self,
        start,
        stop,
        )

  __init__ 
__init__ (
        self,
        prefix,
        data='',
        )

  __len__ 
__len__ ( self )

  __nonzero__ 
__nonzero__ ( self )

  acquire 
acquire ( self )

Lock the scanner so it doesn't destroy data on sync.

  advance 
advance ( self,  count=1 )

Advance the pointer count characters.

  check 
check (
        self,
        i,
        archetype=None,
        )

Scan for the next single or triple quote, with the specified archetype. Return the found quote or None.

Exceptions   
TransientParseError, "need to scan for rest of quote"
  chop 
chop (
        self,
        count=None,
        slop=0,
        )

Chop the first count + slop characters off the front, and return the first count. If count is not specified, then return everything.

Exceptions   
TransientParseError, "not enough data to read"
  complex 
complex (
        self,
        enter,
        exit,
        start=0,
        end=None,
        skip=None,
        )

Scan from i for an ending sequence, respecting quotes, entries and exits.

Exceptions   
TransientParseError, "expecting end of complex expression"
  feed 
feed ( self,  data )

Feed some more data to the scanner.

  find 
find (
        self,
        sub,
        start=0,
        end=None,
        )

Find the next occurrence of the character, or return -1.

  last 
last (
        self,
        char,
        start=0,
        end=None,
        )

Find the first character that is not the specified character.

Exceptions   
TransientParseError, "expecting other than %s" % char
  nested 
nested (
        self,
        enter,
        exit,
        start=0,
        end=None,
        )

Scan from i for an ending sequence, respecting entries and exits only.

Exceptions   
TransientParseError, "expecting end of complex expression"
  next 
next (
        self,
        target,
        start=0,
        end=None,
        mandatory=False,
        )

Scan for the next occurrence of one of the characters in the target string; optionally, make the scan mandatory.

Exceptions   
ParseError, "expecting %s, not found" % target
TransientParseError, "expecting ending character"
  one 
one ( self )

Parse and return one token, or None if the scanner is empty.

Exceptions   
ParseError, "unknown markup: %s%s" %( self.prefix, first )
  phrase 
phrase ( self,  start=0 )

Scan from i for a phrase (e.g., word, f(a, b, c), 'a[i]', or combinations like 'x[i](a)'.

Exceptions   
ParseError, "curly braces can't open simple expressions"
  quote 
quote (
        self,
        start=0,
        end=None,
        mandatory=False,
        )

Scan for the end of the next quote.

Exceptions   
ParseError, "expecting end of string literal"
TransientParseError, "expecting end of string literal"
  read 
read (
        self,
        i=0,
        count=1,
        )

Read count chars starting from i; raise a transient error if there aren't enough characters remaining.

Exceptions   
TransientParseError, "need more data to read"
  release 
release ( self )

Unlock the scanner.

  rest 
rest ( self )

Get the remainder of the buffer.

  retreat 
retreat ( self,  count=1 )

Exceptions   
ParseError, "can't retreat back over synced out chars"
  set 
set ( self,  data )

Start the scanner digesting a new batch of data; start the pointer over from scratch.

  simple 
simple ( self,  start=0 )

Scan from i for a simple expression, which consists of one more phrases separated by dots.

  sync 
sync ( self )

Sync up the buffer with the read head.

  unsync 
unsync ( self )

Undo changes; reset the read head.

  word 
word ( self,  start=0 )

Scan from i for a simple word.

Exceptions   
TransientParseError, "expecting end of word"

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/SignificatorToken.html0000644000175000017500000001262507747162555024143 0ustar anaana Class: SignificatorToken

Table of Contents

Class: SignificatorToken em.py

A significator markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

Exceptions   
ParseError, "no whitespace between % and key"
ParseError, "significator must have nonblank key"
TransientParseError, "significator expects newline"
  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/SimpleExpressionToken.html0000644000175000017500000001115307747162555025026 0ustar anaana Class: SimpleExpressionToken

Table of Contents

Class: SimpleExpressionToken em.py

A simple expression markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/SizeBufferedFilter.html0000644000175000017500000000777107747162555024252 0ustar anaana Class: SizeBufferedFilter

Table of Contents

Class: SizeBufferedFilter em.py

A size-buffered filter only in fixed size chunks (excepting the final chunk).

Base Classes   
BufferedFilter
Methods   
__init__
write
  __init__ 
__init__ ( self,  bufferSize )

  write 
write ( self,  data )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Stack.html0000644000175000017500000002573707747162555021576 0ustar anaana Class: Stack

Table of Contents

Class: Stack em.py

A simple stack that behaves as a sequence (with 0 being the top of the stack, not the bottom).

Methods   
__getitem__
__init__
__len__
__nonzero__
__repr__
clone
filter
pop
purge
push
top
  __getitem__ 
__getitem__ ( self,  index )

  __init__ 
__init__ ( self,  seq=None )

  __len__ 
__len__ ( self )

  __nonzero__ 
__nonzero__ ( self )

  __repr__ 
__repr__ ( self )

  clone 
clone ( self )

Create a duplicate of this stack.

  filter 
filter ( self,  function )

Filter the elements of the stack through the function.

  pop 
pop ( self )

Pop the top element off the stack and return it.

Exceptions   
StackUnderflowError, "stack is empty for pop"
  purge 
purge ( self )

Purge the stack.

  push 
push ( self,  object )

Push an element onto the top of the stack.

  top 
top ( self )

Access the top element on the stack.

Exceptions   
StackUnderflowError, "stack is empty for top"

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/StackUnderflowError.html0000644000175000017500000000400607747162555024460 0ustar anaana Class: StackUnderflowError

Table of Contents

Class: StackUnderflowError em.py

A stack underflow.

Base Classes   
Error
    Exception

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/StatementToken.html0000644000175000017500000001112507747162555023460 0ustar anaana Class: StatementToken

Table of Contents

Class: StatementToken em.py

A statement markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Stream.html0000644000175000017500000004476107747162555021762 0ustar anaana Class: Stream

Table of Contents

Class: Stream em.py

A wrapper around an (output) file object which supports diversions and filtering.

Methods   
__init__
attach
close
create
divert
flush
install
last
purge
purgeAll
retrieve
revert
shortcut
undivert
undivertAll
write
writelines
  __init__ 
__init__ ( self,  file )

  attach 
attach ( self,  shortcut )

Attached a solitary filter (no sequences allowed here) at the end of the current filter chain.

  close 
close ( self )

  create 
create ( self,  name )

Create a diversion if one does not already exist, but do not divert to it yet.

Exceptions   
DiversionError, "diversion name must be non-None"
  divert 
divert ( self,  name )

Start diverting.

Exceptions   
DiversionError, "diversion name must be non-None"
  flush 
flush ( self )

  install 
install ( self,  shortcut=None )

Install a new filter; None means no filter. Handle all the special shortcuts for filters here.

  last 
last ( self )

Find the last filter in the current filter chain, or None if there are no filters installed.

  purge 
purge ( self,  name )

Purge the specified diversion.

Exceptions   
DiversionError, "diversion name must be non-None"
  purgeAll 
purgeAll ( self )

Eliminate all existing diversions.

  retrieve 
retrieve ( self,  name )

Retrieve the given diversion.

Exceptions   
DiversionError, "diversion name must be non-None"
DiversionError, "nonexistent diversion: %s" % name
  revert 
revert ( self )

Reset any current diversions.

  shortcut 
shortcut ( self,  shortcut )

Take a filter shortcut and translate it into a filter, returning it. Sequences don't count here; these should be detected independently.

Exceptions   
NotImplementedError, "mapping filters not yet supported"
  undivert 
undivert (
        self,
        name,
        purgeAfterwards=False,
        )

Undivert a particular diversion.

Exceptions   
DiversionError, "diversion name must be non-None"
DiversionError, "nonexistent diversion: %s" % name
  undivertAll 
undivertAll ( self,  purgeAfterwards=True )

Undivert all pending diversions.

  write 
write ( self,  data )

  writelines 
writelines ( self,  lines )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/StringFilter.html0000644000175000017500000001121107747162555023123 0ustar anaana Class: StringFilter

Table of Contents

Class: StringFilter em.py

A filter that takes a translation string (256 characters) and filters any incoming data through it.

Base Classes   
Filter
Methods   
__init__
write
  __init__ 
__init__ ( self,  table )

Exceptions   
FilterError, "table must be 256-character string"
  write 
write ( self,  data )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/StringLiteralToken.html0000644000175000017500000001114007747162555024274 0ustar anaana Class: StringLiteralToken

Table of Contents

Class: StringLiteralToken em.py

A string token markup.

Base Classes   
ExpansionToken
Methods   
run
scan
string
  run 
run (
        self,
        interpreter,
        locals,
        )

  scan 
scan ( self,  scanner )

  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Subsystem.html0000644000175000017500000001713207747162555022515 0ustar anaana Class: Subsystem

Table of Contents

Class: Subsystem em.py

The subsystem class defers file creation so that it can create Unicode-wrapped files if desired (and possible).

Methods   
__init__
assertUnicode
defaultOpen
initialize
open
unicodeOpen
  __init__ 
__init__ ( self )

  assertUnicode 
assertUnicode ( self )

Exceptions   
SubsystemError, "Unicode subsystem unavailable"
  defaultOpen 
defaultOpen (
        self,
        name,
        mode=None,
        )

  initialize 
initialize (
        self,
        inputEncoding=None,
        outputEncoding=None,
        inputErrors=None,
        outputErrors=None,
        )

Exceptions   
SubsystemError, "Unicode subsystem unavailable"
  open 
open (
        self,
        name,
        mode=None,
        )

  unicodeOpen 
unicodeOpen (
        self,
        name,
        mode=None,
        )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/SubsystemError.html0000644000175000017500000000403107747162555023521 0ustar anaana Class: SubsystemError

Table of Contents

Class: SubsystemError em.py

An error associated with the Unicode subsystem.

Base Classes   
Error
    Exception

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/Token.html0000644000175000017500000001225307747162555021576 0ustar anaana Class: Token

Table of Contents

Class: Token em.py

An element of expansion.

Methods   
__str__
run
string
  __str__ 
__str__ ( self )

  run 
run (
        self,
        interpreter,
        locals,
        )

Exceptions   
NotImplementedError
  string 
string ( self )

Exceptions   
NotImplementedError

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/TransientParseError.html0000644000175000017500000000426407747162555024475 0ustar anaana Class: TransientParseError

Table of Contents

Class: TransientParseError em.py

A parse error occurred which may be resolved by feeding more data. Such an error reaching the toplevel is an unexpected EOF error.

Base Classes   
ParseError

Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/UncloseableFile.html0000644000175000017500000001237007747162555023552 0ustar anaana Class: UncloseableFile

Table of Contents

Class: UncloseableFile em.py

A simple class which wraps around a delegate file-like object and lets everything through except close calls.

Methods   
__init__
close
flush
write
writelines
  __init__ 
__init__ ( self,  delegate )

  close 
close ( self )

Eat this one.

  flush 
flush ( self )

  write 
write ( self,  data )

  writelines 
writelines ( self,  lines )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/VerboseHook.html0000644000175000017500000000660207747162555022745 0ustar anaana Class: VerboseHook

Table of Contents

Class: VerboseHook em.py

A verbose hook that reports all information received by the hook interface. This class dynamically scans the Hook base class to ensure that all hook methods are properly represented.

Base Classes   
Hook
Methods   
__init__
  __init__ 
__init__ ( self,  output=sys.stderr )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/doc/home/max/projects/empy/doc/em/WhitespaceToken.html0000644000175000017500000000640507747162555023615 0ustar anaana Class: WhitespaceToken

Table of Contents

Class: WhitespaceToken em.py

A whitespace markup.

Base Classes   
ExpansionToken
Methods   
string
  string 
string ( self )


Table of Contents

This document was automatically generated on Mon Oct 27 01:29:13 2003 by HappyDoc version 2.1 empy-3.3.orig/sample.em0000444000175000017500000002610707747161671013200 0ustar anaana#! $Id: //projects/empy/sample.em#50 $ $Date: 2003/10/27 $ @# Bangpaths are handled properly (the above line will not appear). #! This line however will appear (not the first line in the script). @# This is a comment. This should not appear in the processed output. This is text. It should appear in the processed output. This is a literal at sign: @@. This is a line continuation; @ this will appear on the same line. Note that it will actually eat any white@ space (one word). @{ # The em.py script has to be somewhere in sys.path! import em }@ @# Escape codes. This will appear on one line.@\nThis will appear on a separate line. This is separated by a tab:@\tSee? These are uppercase As (presuming ASCII): A, @\q1001, @\o101, @\d065, @\x41. @{ import sys # This is just a normal Python comment. print "This is more text." }@ @# Note the @{ ... }@ convention to suppress the newline following the }. @# Also note that comments are completely tossed: This is not expanded: @(x). @# The basics. @{ import sys, math, string x = 4 s = 'alpha' word = "book" l = [3, 2, 1] def square(n): return n**2 friends = ['Albert', 'Betty', 'Charles', 'Donald'] }@ The basics: The square of @(x) is @(x**2), or @(square(x)). Internal whitespace is fine: @( x ) squared is @( square(x) ). Statements: @{sys.stdout.write("%d**2 = %d" % (x, square(x)))}. Whitespace too: @{ sys.stdout.write("%d**2 = %d (still)" % (x, square(x))) }. @{ print "But only on single-line statement expansions." if 1: print "Internal whitespace on multi-line statements is significant." for i in range(2): print "Normal Python indentation rules must be followed here." }@ Simple expressions: x is @x, l is @l, s is "@s," and @x squared is @square(x). Literals too: x is @x, but would be written @@x. Trailing dots are ignored: The value of x is @x. Quotes outside of expansions are also ignored: This is quoted: "x is @x." @# Whitespace is important in simple expressions. Array subscription: The first element of l is @l[0]. But this is not: The first element of l is not @l [0]. That was equivalent to: @(l) and then [0], not @l[0]. But whitespace can go inside the brackets: @l[0] is @l[ 0 ]. Same with functions: @square(x) is @square( x ). The same applies to the other forms. Involved: The legal digits are @string.digits. More involved: The names of my friends are @string.join(friends, ', '). Following expressions: Pluralize "@word" as "@(word)s," or maybe "@word@ s." By default str is used (@s), but you can use repr if you want (@`s`). Conditional expressions: @(x ? "x is true" ! "x is false"). Pluralization: How many words? @x word@(x != 1 ? 's'). Protected expressions: @(foo $ "foo is not defined"). Also here, whitespace isn't important: @(bar$"bar isn't defined either"). The math module has @(math ? "been imported" $ "not been imported"). The re module has @(re ? "been imported" $ "not been imported"). Division by zero is @(x/0 $ "illegal"). To swallow errors, use None: @(buh $ None) [two spaces]. This is self-expanding: @:2 + 2:(this will get replaced with 4): You can expand multiple times: @ @empy.expand("@empy.expand('@:2 + 2:hugalugahglughalug:')") @# More complex examples, including classes. @{ class C: def __init__(self, name): self.name = name def greetString(self): return "Hello, %s" % self.name def printGreeting(self): sys.stdout.write("Hello, %s" % self.name) # implicit None return c = C("empy") }@ c's class is @c.__class__.__name__. c's name is @c.name. Method call: @c.greetString(). Note that None is not expanded: @(None) [two spaces]. But the string 'None' is, of course, printed fine: @('None'). So a function can return None for side effects only: @c.printGreeting(). @# Control. @{ a = 5 # something positive b = -3 # something negative z = 0 # zero }@ If: a is @[if a > 0]positive@[end if]. If/else: b is @[if b > 0]positive@[else]negative@[end if]. If/elif/else: cmp(a, b) is @ @[if a < b]negative@[elif a > b]positive@[else]zero@[end if]. Numbers:@ @[for i in range(10)] @i@[end for]. Evens:@ @[for i in range(10)]@[if i % 2 == 1]@[continue]@[end if] @i@[end for]. Integers less than 5:@ @[for i in range(10)]@[if i >= 5]@[break]@[end if] @i@[end for]. Countdown:@ @{j = 10}@[while j >= 0] @j@{j = j - 1}@[end while]. While/else: @[while z]shouldn't get here@[else]works@[end while]. For/else:@ @[for i in range(3)] @i@[else] also works@[end for]. Tuple unpacking:@[for x in [1, 2, 3, 4]] <@x>@[end for]. Tuple unpacking:@[for x, in [[1], [2], [3], [4]]] <@x>@[end for]. Tuple unpacking:@[for (x,) in [[1], [2], [3], [4]]] <@x>@[end for]. Tuple unpacking:@[for x, y in [[1, 2], [3, 4]]] <@x, @y>@[end for]. Tuple unpacking:@[for (x), (y) in [[1, 2], [3, 4]]] <@x, @y>@[end for]. Tuple unpacking:@[for (x, y) in [[1, 2], [3, 4]]] <@x, @y>@[end for]. Tuple unpacking:@[for x, (y) in [[1, 2], [3, 4]]] <@x, @y>@[end for]. Tuple unpacking:@[for x, (y,) in [[1, [2]], [3, [4]]]] <@x, @y>@[end for]. Tuple unpacking:@[for (x), y in [[1, 2], [3, 4]]] <@x, @y>@[end for]. Tuple unpacking:@[for (x,), y in [[[1], 2], [[3], 4]]] <@x, @y>@[end for]. Tuple unpacking:@[for (x,), (y,) in [[[1], [2]], [[3], [4]]]] <@x, @y>@[end for]. More tuple unpacking:@[for (((x))), ((y),) in [[1, [2]], [3, [4]]]] <@x, @y>@[end for]. Garbage is @[try]@hglhagulahguha@[except NameError]not defined@[end try]. Division by zero is @[try]@(a/z)@[except ZeroDivisionError]illegal@[end try]. Catch all: @[try]@ghlaghlhagl@[except]something happened@[end try]. Finally works: @ @[try]@[try]@(1/0)@[finally]finally, @[end try]@[except]and caught@[end try]. @[def sign(x)]@x is @[if x > 0]positive@[elif x < 0]negative@[else]zero@[end if]@[end def]@ Define: @sign(a), @sign(b), @sign(z). @# Diversions. Again, a trailing @ is used to suppress the following newline. A. This text is undiverted. @empy.startDiversion(1)@ C. This text is diverted. @empy.stopDiverting()@ B. This text is also undiverted. @empy.playDiversion(1)@ D. Again, this text is undiverted. @empy.startDiversion('a')@ E. This text is diverted and then undiverted@ @empy.stopDiverting()@ @empy.replayDiversion('a'). @empy.playDiversion('a') (this should appear twice). @empy.startDiversion('q')@ F. This text is diverted and then cancelled. @empy.playDiversion('q')@ G. This text is again undiverted. @empy.startDiversion('x')@ X. This text will be purged and should not appear! @empy.stopDiverting()@ H. There should be one remaining diversion: @empy.getAllDiversions(). @empy.purgeDiversion('x')@ I. But not after purging it: @empy.getAllDiversions(). @{ # Finally, make a manual diversion and manipulate it. empy.createDiversion('z') zDiversion = empy.retrieveDiversion('z') zDiversion.write("J. This should be the final diversion, created manually.\n") empy.playDiversion('z') }@ @# Parsing checks. Blanks: @(''), @(""), @(''''''), @(""""""). Single quotes: @('\''), @("'"), @("""'"""). Double quotes: @("\""), @('"'), @('''"'''). Triple quotes: @("\"\"\""), @('"""'), @('\'\'\''), @("'''"). Quotes surrounded by spaces: @(""" " """), @(''' ' '''). At signs: @('@'), @("@"), @('''@'''), @("""@"""). Close parentheses: @(')'), @(")"), @((")")), @((')')). Close parentheses in quotes: @("')'"), @('\')\''). Close braces with an intervening space: @ @{sys.stdout.write("}")} @{sys.stdout.write('}')}. Repr of a backquote: @`'`'`. Exes: @("?"?'x'), @(0?"!"!'x'), @(0?":":'x'), @("]"?'x'), @(1?"x"!"]"). Dollar signs: @("$"$None), @(asdf?"$"$"$"), @(1?asdf$"$"). These are strings: @'single quoted string' @"double quoted string" @'single quoted string with escaped \'single quotes\'' @"double quoted string with escaped \"double quotes\"" @'''triple single quoted string''' @"""triple double quoted string""" @'single quoted string with "double quotes"' @"double quoted string with 'single quotes'" @'''triple single quoted continued \ string''' @"""triple double quoted continued \ string""" @'''triple single quoted ...multi-line string''' @"""triple double quoted ... multi-line string""" @# Significators. @%a @%b @%c "x" @%d "x" @%e "x y" @%f "x y" Encountered significators: a and b should be None: @`__a__`, @`__b__` c and d should be 'x': @`__c__`, @`__d__` e and f should be 'x y': @`__e__`, @`__f__` @# Filters. @{import string}@ This line should be in mixed case. @empy.setFilter(string.lower)@ This line should be all lowercase. @empy.setFilter(string.upper)@ This line should be all uppercase (how gauche). @empy.setFilter([em.LineBufferedFilter(), lambda x: '[%s]\n' % x[:-1]])@ This line should be bracketed. So should this line. @empy.setFilter([em.SizeBufferedFilter(5), lambda x: '*' + x])@ There should be stars every five characters on this line. @empy.nullFilter()@ This line should not appear at all! @empy.resetFilter()@ This line should be back to mixed case. @empy.attachFilter(string.upper)@ @empy.attachFilter(em.LineBufferedFilter())@ @empy.attachFilter(lambda x: '[%s]\n' % x[:-1])@ This line should be all uppercase with brackets. @empy.resetFilter()@ This line should be back to mixed case (again). @# Contexts, metaoperations. @{ import StringIO def context(): return "%s:%d" % empy.identify() stringFile = StringIO.StringIO("2 + 2 = @(2 + 2) [@context()].\n") }@ The new context is @context(). File inclusion [@context()]: @empy.include(stringFile)@ Expansion [@context()]: @ @empy.expand("This should be appear [@context()]") @ on the same line as this [@context()]. More expansion [@context()]: @ @{sys.stdout.write(empy.expand("Another expansion [@context()]"))}. This is the next line [@context()]. Quoting: @empy.quote("x when quoted would be '@x' or @x"). More quoting: @empy.quote("This will be @doubled but '''@this is not'''"). Here's the last view of the old context: @context(). Creating a new context ... @empy.pushContext()@ The current context is: @context(). @?NewName The context name should now be 'NewName': @context(). @!1000 The line number should now be 1000: @context(). @empy.popContext()@ Back to the old context: @context(). @# Embedded interpreters and standalone expansion. @{ q = 1 }@ Interpreter's q is @q. @{ try: i = em.Interpreter() i.string("@{q = 10}") i.string("Embedded interpreter's q is @q.\n") finally: i.shutdown() }@ Interpreter's q is still @q; the embedded interpreter had no effect. Standalone expansion: @em.expand("1 + 1 is @(1 + 1).") With locals: @em.expand("@x + @y is @(x + y).", x=2, y=3) @{ g = {} }@ With globals: @em.expand("@{x = 10}g's x is @x.", g) Still with globals: @em.expand("g's x + 1 is @(x + 1).", g) g's x is still @g['x']. @# Hooks. @{ class SampleHook(em.Hook): def null(self): self.interpreter.write('[SampleHook.null invoked]') sampleHook = SampleHook() empy.addHook(sampleHook) }@ Invoking the sample hook: @empy.invokeHook('null'). @{ empy.removeHook(sampleHook) }@ @# Custom. @{ def customCallback(contents, empy=empy): empy.write('[%s]' % contents) empy.registerCallback(customCallback) }@ Using a custom markup: @. Again: @<>. Once more: @">. @{ empy.deregisterCallback() }@ @# Finals; note these are evaluated in reverse order. @empy.atExit(lambda: empy.string("Version: @empy.VERSION.\n"))@ @empy.atExit(lambda: empy.write("This is the penultimate line.\n"))@ empy-3.3.orig/sample.bench0000444000175000017500000001364307747161651013655 0ustar anaana#! This line however will appear (not the first line in the script). This is text. It should appear in the processed output. This is a literal at sign: @. This is a line continuation; this will appear on the same line. Note that it will actually eat any whitespace (one word). This will appear on one line. This will appear on a separate line. This is separated by a tab: See? These are uppercase As (presuming ASCII): A, A, A, A, A. This is more text. The basics: The square of 4 is 16, or 16. Internal whitespace is fine: 4 squared is 16. Statements: 4**2 = 16. Whitespace too: 4**2 = 16 (still). But only on single-line statement expansions. Internal whitespace on multi-line statements is significant. Normal Python indentation rules must be followed here. Normal Python indentation rules must be followed here. Simple expressions: x is 4, l is [3, 2, 1], s is "alpha," and 4 squared is 16. Literals too: x is 4, but would be written @x. Trailing dots are ignored: The value of x is 4. Quotes outside of expansions are also ignored: This is quoted: "x is 4." Array subscription: The first element of l is 3. But this is not: The first element of l is not [3, 2, 1] [0]. That was equivalent to: [3, 2, 1] and then [0], not 3. But whitespace can go inside the brackets: 3 is 3. Same with functions: 16 is 16. The same applies to the other forms. Involved: The legal digits are 0123456789. More involved: The names of my friends are Albert, Betty, Charles, Donald. Following expressions: Pluralize "book" as "books," or maybe "books." By default str is used (alpha), but you can use repr if you want ('alpha'). Conditional expressions: x is true. Pluralization: How many words? 4 words. Protected expressions: foo is not defined. Also here, whitespace isn't important: bar isn't defined either. The math module has been imported. The re module has not been imported. Division by zero is illegal. To swallow errors, use None: [two spaces]. This is self-expanding: @:2 + 2:4: You can expand multiple times: @:2 + 2:4: c's class is C. c's name is empy. Method call: Hello, empy. Note that None is not expanded: [two spaces]. But the string 'None' is, of course, printed fine: None. So a function can return None for side effects only: Hello, empy. If: a is positive. If/else: b is negative. If/elif/else: cmp(a, b) is positive. Numbers: 0 1 2 3 4 5 6 7 8 9. Evens: 0 2 4 6 8. Integers less than 5: 0 1 2 3 4. Countdown: 10 9 8 7 6 5 4 3 2 1 0. While/else: works. For/else: 0 1 2 also works. Tuple unpacking: <1> <2> <3> <4>. Tuple unpacking: <1> <2> <3> <4>. Tuple unpacking: <1> <2> <3> <4>. Tuple unpacking: <1, 2> <3, 4>. Tuple unpacking: <1, 2> <3, 4>. Tuple unpacking: <1, 2> <3, 4>. Tuple unpacking: <1, 2> <3, 4>. Tuple unpacking: <1, 2> <3, 4>. Tuple unpacking: <1, 2> <3, 4>. Tuple unpacking: <1, 2> <3, 4>. Tuple unpacking: <1, 2> <3, 4>. More tuple unpacking: <1, 2> <3, 4>. Garbage is not defined. Division by zero is illegal. Catch all: something happened. Finally works: finally, and caught. Define: 5 is positive, -3 is negative, 0 is zero. A. This text is undiverted. B. This text is also undiverted. C. This text is diverted. D. Again, this text is undiverted. E. This text is diverted and then undiverted. E. This text is diverted and then undiverted (this should appear twice). F. This text is diverted and then cancelled. G. This text is again undiverted. H. There should be one remaining diversion: ['x']. I. But not after purging it: []. J. This should be the final diversion, created manually. Blanks: , , , . Single quotes: ', ', '. Double quotes: ", ", ". Triple quotes: """, """, ''', '''. Quotes surrounded by spaces: " , ' . At signs: @, @, @, @. Close parentheses: ), ), ), ). Close parentheses in quotes: ')', ')'. Close braces with an intervening space: } }. Repr of a backquote: '`'. Exes: x, x, x, x, x. Dollar signs: $, $, $. These are strings: single quoted string double quoted string single quoted string with escaped 'single quotes' double quoted string with escaped "double quotes" triple single quoted string triple double quoted string single quoted string with "double quotes" double quoted string with 'single quotes' triple single quoted continued string triple double quoted continued string triple single quoted ...multi-line string triple double quoted ... multi-line string Encountered significators: a and b should be None: None, None c and d should be 'x': 'x', 'x' e and f should be 'x y': 'x y', 'x y' This line should be in mixed case. this line should be all lowercase. THIS LINE SHOULD BE ALL UPPERCASE (HOW GAUCHE). [This line should be bracketed.] [So should this line.] *There* shou*ld be* star*s eve*ry fi*ve ch*aract*ers o*n thi*s lin*e. This line should be back to mixed case. [THIS LINE SHOULD BE ALL UPPERCASE WITH BRACKETS.] This line should be back to mixed case (again). The new context is sample.em:243. File inclusion [sample.em:244]: 2 + 2 = 4 [:1]. Expansion [sample.em:245]: This should be appear [:1] on the same line as this [sample.em:247]. More expansion [sample.em:248]: Another expansion [:1]. This is the next line [sample.em:250]. Quoting: x when quoted would be '@x' or @@x. More quoting: This will be @@doubled but '''@this is not'''. Here's the last view of the old context: sample.em:253. Creating a new context ... The current context is: :1. The context name should now be 'NewName': NewName:3. The line number should now be 1000: NewName:1000. Back to the old context: sample.em:256. Interpreter's q is 1. Embedded interpreter's q is 10. Interpreter's q is still 1; the embedded interpreter had no effect. Standalone expansion: 1 + 1 is 2. With locals: 2 + 3 is 5. With globals: g's x is 10. Still with globals: g's x + 1 is 11. g's x is still 10. Invoking the sample hook: [SampleHook.null invoked]. Using a custom markup: [This appears in brackets]. Again: []. Once more: [This is a right angle bracket in quotes: ">"]. This is the penultimate line. Version: 3.3. empy-3.3.orig/test.sh0000555000175000017500000000074607637205512012702 0ustar anaana#!/bin/sh # # $Id: //projects/empy/test.sh#6 $ $Date: 2003/03/22 $ if [ $# = 1 ] then PYTHON="$1" else PYTHON=python fi EMPY=em.py SAMPLE=sample.em BENCH=sample.bench if $PYTHON -c 'import sys; print sys.version' > /dev/null then : else echo "EmPy was not checked; $PYTHON looks broken." 1>&2 exit 1 fi if $PYTHON $EMPY $SAMPLE | diff $BENCH - then echo "EmPy checks out." 1>&2 else echo "EmPy does not check out! Please mail output to author." 1>&2 fi empy-3.3.orig/version.txt0000644000175000017500000000000407747162550013605 0ustar anaana3.3