Codebase list sugar-artwork / 58d58f3
Remove embedded em.py in favor of upstream empy * update make files * update shebang lines Debian impact; Use system python3-empy package Fedora impact; Use system python3-empy package Signed-off-by: James Cameron <quozl@laptop.org> Pro-Panda authored 5 years ago James Cameron committed 4 years ago
8 changed file(s) with 17 addition(s) and 6618 deletion(s). Raw diff Collapse all Expand all
1717 AC_PATH_PROG([XCURSORGEN], [xcursorgen])
1818 if test -z "$XCURSORGEN"; then
1919 AC_MSG_ERROR([xcursorgen is required])
20 fi
21
22 AC_PATH_PROG([EMPY3], [empy3])
23 if test -z "$EMPY3"; then
24 AC_MSG_ERROR([python3-empy is required])
2025 fi
2126
2227 PKG_CHECK_MODULES(GTK2, gtk+-2.0 >= 2.16.0,,
0 INCLUDES = \
0 AM_CPPFLAGS = \
11 $(ENGINE_CFLAGS) $(WARN_CFLAGS)
22
33 enginedir = $(libdir)/gtk-2.0/$(GTK_VERSION)/engines
00 sugar-72.gtkrc: gtkrc.em
1 $(srcdir)/em.py -p $$ -D scaling=\'72\' $(srcdir)/gtkrc.em > \
1 empy3 -p $$ -D scaling=\'72\' $(srcdir)/gtkrc.em > \
22 $(top_builddir)/gtk/theme/sugar-72.gtkrc
33
44 sugar-100.gtkrc: gtkrc.em
5 $(srcdir)/em.py -p $$ -D scaling=\'100\' $(srcdir)/gtkrc.em > \
5 empy3 -p $$ -D scaling=\'100\' $(srcdir)/gtkrc.em > \
66 $(top_builddir)/gtk/theme/sugar-100.gtkrc
77
88 clean:
2525 rm -rf $(DESTDIR)$(datadir)/themes/sugar-72/gtk-2.0
2626 rm -rf $(DESTDIR)$(datadir)/themes/sugar-100/gtk-2.0
2727
28 EXTRA_DIST = em.py gtkrc.em
28 EXTRA_DIST = gtkrc.em
2929 CLEANFILES = $(GTKRC_FILES)
+0
-3303
gtk/theme/em.py less more
0 #!/usr/bin/env python
1 #
2 # $Id: //projects/empy/em.py#146 $ $Date: 2017-02-12 $
3
4 """
5 A system for processing Python as markup embedded in text.
6 """
7
8
9 __program__ = 'empy'
10 __version__ = '3.3.3'
11 __url__ = 'http://www.alcyone.com/software/empy/'
12 __author__ = 'Erik Max Francis <max@alcyone.com>'
13 __copyright__ = 'Copyright (C) 2002-2017 Erik Max Francis'
14 __license__ = 'LGPL'
15
16
17 import copy
18 import getopt
19 import inspect
20 import os
21 import re
22 import sys
23 import types
24
25 # 2.x/3.0 compatbility
26 try:
27 from StringIO import StringIO
28 except ImportError:
29 from io import StringIO
30
31 try:
32 _unicode = unicode # bytes will be undefined in 3.x releases
33 _str = str
34 _unichr = unichr
35 _input = raw_input
36 def _exec(code, globals, locals=None):
37 if globals is None:
38 exec("""exec code""")
39 else:
40 if locals is None:
41 exec("""exec code in globals""")
42 else:
43 exec("""exec code in globals, locals""")
44 except NameError:
45 _unicode = str
46 _str = bytes
47 _unichr = chr
48 _input = input
49 try:
50 _exec = __builtins__.__dict__['exec']
51 except AttributeError:
52 _exec = __builtins__['exec']
53
54 # Some basic defaults.
55 FAILURE_CODE = 1
56 DEFAULT_PREFIX = '@'
57 DEFAULT_PSEUDOMODULE_NAME = 'empy'
58 DEFAULT_SCRIPT_NAME = '?'
59 SIGNIFICATOR_RE_SUFFIX = r"%(\S+)\s*(.*)\s*$"
60 SIGNIFICATOR_RE_STRING = DEFAULT_PREFIX + SIGNIFICATOR_RE_SUFFIX
61 BANGPATH = '#!'
62 DEFAULT_CHUNK_SIZE = 8192
63 DEFAULT_ERRORS = 'strict'
64
65 # Character information.
66 IDENTIFIER_FIRST_CHARS = '_abcdefghijklmnopqrstuvwxyz' \
67 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
68 IDENTIFIER_CHARS = IDENTIFIER_FIRST_CHARS + '0123456789.'
69 ENDING_CHARS = {'(': ')', '[': ']', '{': '}'}
70
71 # Environment variable names.
72 OPTIONS_ENV = 'EMPY_OPTIONS'
73 PREFIX_ENV = 'EMPY_PREFIX'
74 PSEUDO_ENV = 'EMPY_PSEUDO'
75 FLATTEN_ENV = 'EMPY_FLATTEN'
76 RAW_ENV = 'EMPY_RAW_ERRORS'
77 INTERACTIVE_ENV = 'EMPY_INTERACTIVE'
78 BUFFERED_ENV = 'EMPY_BUFFERED_OUTPUT'
79 NO_OVERRIDE_ENV = 'EMPY_NO_OVERRIDE'
80 UNICODE_ENV = 'EMPY_UNICODE'
81 INPUT_ENCODING_ENV = 'EMPY_UNICODE_INPUT_ENCODING'
82 OUTPUT_ENCODING_ENV = 'EMPY_UNICODE_OUTPUT_ENCODING'
83 INPUT_ERRORS_ENV = 'EMPY_UNICODE_INPUT_ERRORS'
84 OUTPUT_ERRORS_ENV = 'EMPY_UNICODE_OUTPUT_ERRORS'
85
86 # Interpreter options.
87 BANGPATH_OPT = 'processBangpaths' # process bangpaths as comments?
88 BUFFERED_OPT = 'bufferedOutput' # fully buffered output?
89 RAW_OPT = 'rawErrors' # raw errors?
90 EXIT_OPT = 'exitOnError' # exit on error?
91 FLATTEN_OPT = 'flatten' # flatten pseudomodule namespace?
92 OVERRIDE_OPT = 'override' # override sys.stdout with proxy?
93 CALLBACK_OPT = 'noCallbackError' # is no custom callback an error?
94
95 # Usage info.
96 OPTION_INFO = [
97 ("-V --version", "Print version and exit"),
98 ("-h --help", "Print usage and exit"),
99 ("-H --extended-help", "Print extended usage and exit"),
100 ("-k --suppress-errors", "Do not exit on errors; go interactive"),
101 ("-p --prefix=<char>", "Change prefix to something other than @"),
102 (" --no-prefix", "Do not do any markup processing at all"),
103 ("-m --module=<name>", "Change the internal pseudomodule name"),
104 ("-f --flatten", "Flatten the members of pseudmodule to start"),
105 ("-r --raw-errors", "Show raw Python errors"),
106 ("-i --interactive", "Go into interactive mode after processing"),
107 ("-n --no-override-stdout", "Do not override sys.stdout with proxy"),
108 ("-o --output=<filename>", "Specify file for output as write"),
109 ("-a --append=<filename>", "Specify file for output as append"),
110 ("-b --buffered-output", "Fully buffer output including open"),
111 (" --binary", "Treat the file as a binary"),
112 (" --chunk-size=<chunk>", "Use this chunk size for reading binaries"),
113 ("-P --preprocess=<filename>", "Interpret EmPy file before main processing"),
114 ("-I --import=<modules>", "Import Python modules before processing"),
115 ("-D --define=<definition>", "Execute Python assignment statement"),
116 ("-E --execute=<statement>", "Execute Python statement before processing"),
117 ("-F --execute-file=<filename>", "Execute Python file before processing"),
118 (" --pause-at-end", "Prompt at the ending of processing"),
119 (" --relative-path", "Add path of EmPy script to sys.path"),
120 (" --no-callback-error", "Custom markup without callback is error"),
121 (" --no-bangpath-processing", "Suppress bangpaths as comments"),
122 ("-u --unicode", "Enable Unicode subsystem (Python 2+ only)"),
123 (" --unicode-encoding=<e>", "Set both input and output encodings"),
124 (" --unicode-input-encoding=<e>", "Set input encoding"),
125 (" --unicode-output-encoding=<e>", "Set output encoding"),
126 (" --unicode-errors=<E>", "Set both input and output error handler"),
127 (" --unicode-input-errors=<E>", "Set input error handler"),
128 (" --unicode-output-errors=<E>", "Set output error handler"),
129 ]
130
131 USAGE_NOTES = """\
132 Notes: Whitespace immediately inside parentheses of @(...) are
133 ignored. Whitespace immediately inside braces of @{...} are ignored,
134 unless ... spans multiple lines. Use @{ ... }@ to suppress newline
135 following expansion. Simple expressions ignore trailing dots; `@x.'
136 means `@(x).'. A #! at the start of a file is treated as a @#
137 comment."""
138
139 MARKUP_INFO = [
140 ("@# ... NL", "Comment; remove everything up to newline"),
141 ("@? NAME NL", "Set the current context name"),
142 ("@! INTEGER NL", "Set the current context line number"),
143 ("@ WHITESPACE", "Remove following whitespace; line continuation"),
144 ("@\\ ESCAPE_CODE", "A C-style escape sequence"),
145 ("@@", "Literal @; @ is escaped (duplicated prefix)"),
146 ("@), @], @}", "Literal close parenthesis, bracket, brace"),
147 ("@ STRING_LITERAL", "Replace with string literal contents"),
148 ("@( EXPRESSION )", "Evaluate expression and substitute with str"),
149 ("@( TEST [? THEN [! ELSE]] )", "If test is true, evaluate then, otherwise else"),
150 ("@( TRY $ CATCH )", "Expand try expression, or catch if it raises"),
151 ("@ SIMPLE_EXPRESSION", "Evaluate simple expression and substitute;\n"
152 "e.g., @x, @x.y, @f(a, b), @l[i], etc."),
153 ("@` EXPRESSION `", "Evaluate expression and substitute with repr"),
154 ("@: EXPRESSION : [DUMMY] :", "Evaluates to @:...:expansion:"),
155 ("@{ STATEMENTS }", "Statements are executed for side effects"),
156 ("@[ CONTROL ]", "Control markups: if E; elif E; for N in E;\n"
157 "while E; try; except E, N; finally; continue;\n"
158 "break; end X"),
159 ("@%% KEY WHITESPACE VALUE NL", "Significator form of __KEY__ = VALUE"),
160 ("@< CONTENTS >", "Custom markup; meaning provided by user"),
161 ]
162
163 ESCAPE_INFO = [
164 ("@\\0", "NUL, null"),
165 ("@\\a", "BEL, bell"),
166 ("@\\b", "BS, backspace"),
167 ("@\\dDDD", "three-digit decimal code DDD"),
168 ("@\\e", "ESC, escape"),
169 ("@\\f", "FF, form feed"),
170 ("@\\h", "DEL, delete"),
171 ("@\\n", "LF, linefeed, newline"),
172 ("@\\N{NAME}", "Unicode character named NAME"),
173 ("@\\oOOO", "three-digit octal code OOO"),
174 ("@\\qQQQQ", "four-digit quaternary code QQQQ"),
175 ("@\\r", "CR, carriage return"),
176 ("@\\s", "SP, space"),
177 ("@\\t", "HT, horizontal tab"),
178 ("@\\uHHHH", "16-bit hexadecimal Unicode HHHH"),
179 ("@\\UHHHHHHHH", "32-bit hexadecimal Unicode HHHHHHHH"),
180 ("@\\v", "VT, vertical tab"),
181 ("@\\xHH", "two-digit hexadecimal code HH"),
182 ("@\\z", "EOT, end of transmission"),
183 ]
184
185 PSEUDOMODULE_INFO = [
186 ("VERSION", "String representing EmPy version"),
187 ("SIGNIFICATOR_RE_STRING", "Regular expression matching significators"),
188 ("SIGNIFICATOR_RE_SUFFIX", "The above stub, lacking the prefix"),
189 ("interpreter", "Currently-executing interpreter instance"),
190 ("argv", "The EmPy script name and command line arguments"),
191 ("args", "The command line arguments only"),
192 ("identify()", "Identify top context as name, line"),
193 ("setContextName(name)", "Set the name of the current context"),
194 ("setContextLine(line)", "Set the line number of the current context"),
195 ("atExit(callable)", "Invoke no-argument function at shutdown"),
196 ("getGlobals()", "Retrieve this interpreter's globals"),
197 ("setGlobals(dict)", "Set this interpreter's globals"),
198 ("updateGlobals(dict)", "Merge dictionary into interpreter's globals"),
199 ("clearGlobals()", "Start globals over anew"),
200 ("saveGlobals([deep])", "Save a copy of the globals"),
201 ("restoreGlobals([pop])", "Restore the most recently saved globals"),
202 ("defined(name, [loc])", "Find if the name is defined"),
203 ("evaluate(expression, [loc])", "Evaluate the expression"),
204 ("serialize(expression, [loc])", "Evaluate and serialize the expression"),
205 ("execute(statements, [loc])", "Execute the statements"),
206 ("single(source, [loc])", "Execute the 'single' object"),
207 ("atomic(name, value, [loc])", "Perform an atomic assignment"),
208 ("assign(name, value, [loc])", "Perform an arbitrary assignment"),
209 ("significate(key, [value])", "Significate the given key, value pair"),
210 ("include(file, [loc])", "Include filename or file-like object"),
211 ("expand(string, [loc])", "Explicitly expand string and return"),
212 ("string(data, [name], [loc])", "Process string-like object"),
213 ("quote(string)", "Quote prefixes in provided string and return"),
214 ("flatten([keys])", "Flatten module contents into globals namespace"),
215 ("getPrefix()", "Get current prefix"),
216 ("setPrefix(char)", "Set new prefix"),
217 ("stopDiverting()", "Stop diverting; data sent directly to output"),
218 ("createDiversion(name)", "Create a diversion but do not divert to it"),
219 ("retrieveDiversion(name)", "Retrieve the actual named diversion object"),
220 ("startDiversion(name)", "Start diverting to given diversion"),
221 ("playDiversion(name)", "Recall diversion and then eliminate it"),
222 ("replayDiversion(name)", "Recall diversion but retain it"),
223 ("purgeDiversion(name)", "Erase diversion"),
224 ("playAllDiversions()", "Stop diverting and play all diversions in order"),
225 ("replayAllDiversions()", "Stop diverting and replay all diversions"),
226 ("purgeAllDiversions()", "Stop diverting and purge all diversions"),
227 ("getFilter()", "Get current filter"),
228 ("resetFilter()", "Reset filter; no filtering"),
229 ("nullFilter()", "Install null filter"),
230 ("setFilter(shortcut)", "Install new filter or filter chain"),
231 ("attachFilter(shortcut)", "Attach single filter to end of current chain"),
232 ("areHooksEnabled()", "Return whether or not hooks are enabled"),
233 ("enableHooks()", "Enable hooks (default)"),
234 ("disableHooks()", "Disable hook invocation"),
235 ("getHooks()", "Get all the hooks"),
236 ("clearHooks()", "Clear all hooks"),
237 ("addHook(hook, [i])", "Register the hook (optionally insert)"),
238 ("removeHook(hook)", "Remove an already-registered hook from name"),
239 ("invokeHook(name_, ...)", "Manually invoke hook"),
240 ("getCallback()", "Get interpreter callback"),
241 ("registerCallback(callback)", "Register callback with interpreter"),
242 ("deregisterCallback()", "Deregister callback from interpreter"),
243 ("invokeCallback(contents)", "Invoke the callback directly"),
244 ("Interpreter", "The interpreter class"),
245 ]
246
247 ENVIRONMENT_INFO = [
248 (OPTIONS_ENV, "Specified options will be included"),
249 (PREFIX_ENV, "Specify the default prefix: -p <value>"),
250 (PSEUDO_ENV, "Specify name of pseudomodule: -m <value>"),
251 (FLATTEN_ENV, "Flatten empy pseudomodule if defined: -f"),
252 (RAW_ENV, "Show raw errors if defined: -r"),
253 (INTERACTIVE_ENV, "Enter interactive mode if defined: -i"),
254 (BUFFERED_ENV, "Fully buffered output if defined: -b"),
255 (NO_OVERRIDE_ENV, "Do not override sys.stdout if defined: -n"),
256 (UNICODE_ENV, "Enable Unicode subsystem: -n"),
257 (INPUT_ENCODING_ENV, "Unicode input encoding"),
258 (OUTPUT_ENCODING_ENV, "Unicode output encoding"),
259 (INPUT_ERRORS_ENV, "Unicode input error handler"),
260 (OUTPUT_ERRORS_ENV, "Unicode output error handler"),
261 ]
262
263 class Error(Exception):
264 """The base class for all EmPy errors."""
265 pass
266
267 EmpyError = EmPyError = Error # DEPRECATED
268
269 class DiversionError(Error):
270 """An error related to diversions."""
271 pass
272
273 class FilterError(Error):
274 """An error related to filters."""
275 pass
276
277 class StackUnderflowError(Error):
278 """A stack underflow."""
279 pass
280
281 class SubsystemError(Error):
282 """An error associated with the Unicode subsystem."""
283 pass
284
285 class FlowError(Error):
286 """An exception related to control flow."""
287 pass
288
289 class ContinueFlow(FlowError):
290 """A continue control flow."""
291 pass
292
293 class BreakFlow(FlowError):
294 """A break control flow."""
295 pass
296
297 class ParseError(Error):
298 """A parse error occurred."""
299 pass
300
301 class TransientParseError(ParseError):
302 """A parse error occurred which may be resolved by feeding more data.
303 Such an error reaching the toplevel is an unexpected EOF error."""
304 pass
305
306
307 class MetaError(Exception):
308
309 """A wrapper around a real Python exception for including a copy of
310 the context."""
311
312 def __init__(self, contexts, exc):
313 Exception.__init__(self, exc)
314 self.contexts = contexts
315 self.exc = exc
316
317 def __str__(self):
318 backtrace = [str(x) for x in self.contexts]
319 return "%s: %s (%s)" % (self.exc.__class__, self.exc,
320 (', '.join(backtrace)))
321
322
323 class Subsystem:
324
325 """The subsystem class defers file creation so that it can create
326 Unicode-wrapped files if desired (and possible)."""
327
328 def __init__(self):
329 self.useUnicode = False
330 self.inputEncoding = None
331 self.outputEncoding = None
332 self.errors = None
333
334 def initialize(self, inputEncoding=None, outputEncoding=None,
335 inputErrors=None, outputErrors=None):
336 self.useUnicode = True
337 defaultEncoding = sys.getdefaultencoding()
338 if inputEncoding is None:
339 inputEncoding = defaultEncoding
340 self.inputEncoding = inputEncoding
341 if outputEncoding is None:
342 outputEncoding = defaultEncoding
343 self.outputEncoding = outputEncoding
344 if inputErrors is None:
345 inputErrors = DEFAULT_ERRORS
346 self.inputErrors = inputErrors
347 if outputErrors is None:
348 outputErrors = DEFAULT_ERRORS
349 self.outputErrors = outputErrors
350
351 def assertUnicode(self):
352 if not self.useUnicode:
353 raise SubsystemError("Unicode subsystem unavailable")
354
355 def open(self, name, mode=None):
356 if self.useUnicode:
357 return self.unicodeOpen(name, mode)
358 else:
359 return self.defaultOpen(name, mode)
360
361 def defaultOpen(self, name, mode=None):
362 if mode is None:
363 mode = 'r'
364 return open(name, mode)
365
366 def unicodeOpen(self, name, mode=None):
367 import codecs
368 if mode is None:
369 mode = 'rb'
370 if mode.find('w') >= 0 or mode.find('a') >= 0:
371 encoding = self.outputEncoding
372 errors = self.outputErrors
373 else:
374 encoding = self.inputEncoding
375 errors = self.inputErrors
376 return codecs.open(name, mode, encoding, errors)
377
378 theSubsystem = Subsystem()
379
380
381 class Stack:
382
383 """A simple stack that behaves as a sequence (with 0 being the top
384 of the stack, not the bottom)."""
385
386 def __init__(self, seq=None):
387 if seq is None:
388 seq = []
389 self.data = seq
390
391 def top(self):
392 """Access the top element on the stack."""
393 try:
394 return self.data[-1]
395 except IndexError:
396 raise StackUnderflowError("stack is empty for top")
397
398 def pop(self):
399 """Pop the top element off the stack and return it."""
400 try:
401 return self.data.pop()
402 except IndexError:
403 raise StackUnderflowError("stack is empty for pop")
404
405 def push(self, object):
406 """Push an element onto the top of the stack."""
407 self.data.append(object)
408
409 def filter(self, function):
410 """Filter the elements of the stack through the function."""
411 self.data = list(filter(function, self.data))
412
413 def purge(self):
414 """Purge the stack."""
415 self.data = []
416
417 def clone(self):
418 """Create a duplicate of this stack."""
419 return self.__class__(self.data[:])
420
421 def __nonzero__(self): return len(self.data) != 0 # 2.x
422 def __bool__(self): return len(self.data) != 0 # 3.x
423 def __len__(self): return len(self.data)
424 def __getitem__(self, index): return self.data[-(index + 1)]
425
426 def __repr__(self):
427 return ('<%s instance at 0x%x [%s]>' %
428 (self.__class__, id(self),
429 ', '.join(repr(x) for x in self.data)))
430
431
432 class AbstractFile:
433
434 """An abstracted file that, when buffered, will totally buffer the
435 file, including even the file open."""
436
437 def __init__(self, filename, mode='w', buffered=False):
438 # The calls below might throw, so start off by marking this
439 # file as "done." This way destruction of a not-completely-
440 # initialized AbstractFile will generate no further errors.
441 self.done = True
442 self.filename = filename
443 self.mode = mode
444 self.buffered = buffered
445 if buffered:
446 self.bufferFile = StringIO()
447 else:
448 self.bufferFile = theSubsystem.open(filename, mode)
449 # Okay, we got this far, so the AbstractFile is initialized.
450 # Flag it as "not done."
451 self.done = False
452
453 def __del__(self):
454 self.close()
455
456 def write(self, data):
457 self.bufferFile.write(data)
458
459 def writelines(self, data):
460 self.bufferFile.writelines(data)
461
462 def flush(self):
463 self.bufferFile.flush()
464
465 def close(self):
466 if not self.done:
467 self.commit()
468 self.done = True
469
470 def commit(self):
471 if self.buffered:
472 file = theSubsystem.open(self.filename, self.mode)
473 file.write(self.bufferFile.getvalue())
474 file.close()
475 else:
476 self.bufferFile.close()
477
478 def abort(self):
479 if self.buffered:
480 self.bufferFile = None
481 else:
482 self.bufferFile.close()
483 self.bufferFile = None
484 self.done = True
485
486
487 class Diversion:
488
489 """The representation of an active diversion. Diversions act as
490 (writable) file objects, and then can be recalled either as pure
491 strings or (readable) file objects."""
492
493 def __init__(self):
494 self.file = StringIO()
495
496 # These methods define the writable file-like interface for the
497 # diversion.
498
499 def write(self, data):
500 self.file.write(data)
501
502 def writelines(self, lines):
503 for line in lines:
504 self.write(line)
505
506 def flush(self):
507 self.file.flush()
508
509 def close(self):
510 self.file.close()
511
512 # These methods are specific to diversions.
513
514 def asString(self):
515 """Return the diversion as a string."""
516 return self.file.getvalue()
517
518 def asFile(self):
519 """Return the diversion as a file."""
520 return StringIO(self.file.getvalue())
521
522
523 class Stream:
524
525 """A wrapper around an (output) file object which supports
526 diversions and filtering."""
527
528 def __init__(self, file):
529 self.file = file
530 self.currentDiversion = None
531 self.diversions = {}
532 self.filter = file
533 self.done = False
534
535 def write(self, data):
536 if self.currentDiversion is None:
537 self.filter.write(data)
538 else:
539 self.diversions[self.currentDiversion].write(data)
540
541 def writelines(self, lines):
542 for line in lines:
543 self.write(line)
544
545 def flush(self):
546 self.filter.flush()
547
548 def close(self):
549 if not self.done:
550 self.undivertAll(True)
551 self.filter.close()
552 self.done = True
553
554 def shortcut(self, shortcut):
555 """Take a filter shortcut and translate it into a filter, returning
556 it. Sequences don't count here; these should be detected
557 independently."""
558 if shortcut == 0:
559 return NullFilter()
560 elif (isinstance(shortcut, types.FunctionType) or
561 inspect.ismethoddescriptor(shortcut) or
562 isinstance(shortcut, types.BuiltinFunctionType) or
563 isinstance(shortcut, types.BuiltinMethodType) or
564 isinstance(shortcut, types.LambdaType)):
565 return FunctionFilter(shortcut)
566 elif isinstance(shortcut, _str) or isinstance(shortcut, _unicode):
567 return StringFilter(filter)
568 elif isinstance(shortcut, dict):
569 raise NotImplementedError("mapping filters not yet supported")
570 else:
571 # Presume it's a plain old filter.
572 return shortcut
573
574 def last(self):
575 """Find the last filter in the current filter chain, or None if
576 there are no filters installed."""
577 if self.filter is None:
578 return None
579 thisFilter, lastFilter = self.filter, None
580 while thisFilter is not None and thisFilter is not self.file:
581 lastFilter = thisFilter
582 thisFilter = thisFilter.next()
583 return lastFilter
584
585 def install(self, shortcut=None):
586 """Install a new filter; None means no filter. Handle all the
587 special shortcuts for filters here."""
588 # Before starting, execute a flush.
589 self.filter.flush()
590 if shortcut is None or shortcut == [] or shortcut == ():
591 # Shortcuts for "no filter."
592 self.filter = self.file
593 else:
594 if isinstance(shortcut, list) or isinstance(shortcut, tuple):
595 shortcuts = list(shortcut)
596 else:
597 shortcuts = [shortcut]
598 # Run through the shortcut filter names, replacing them with
599 # full-fledged instances of Filter.
600 filters = []
601 for shortcut in shortcuts:
602 filters.append(self.shortcut(shortcut))
603 if len(filters) > 1:
604 # If there's more than one filter provided, chain them
605 # together.
606 lastFilter = None
607 for filter in filters:
608 if lastFilter is not None:
609 lastFilter.attach(filter)
610 lastFilter = filter
611 lastFilter.attach(self.file)
612 self.filter = filters[0]
613 else:
614 # If there's only one filter, assume that it's alone or it's
615 # part of a chain that has already been manually chained;
616 # just find the end.
617 filter = filters[0]
618 lastFilter = filter.last()
619 lastFilter.attach(self.file)
620 self.filter = filter
621
622 def attach(self, shortcut):
623 """Attached a solitary filter (no sequences allowed here) at the
624 end of the current filter chain."""
625 lastFilter = self.last()
626 if lastFilter is None:
627 # Just install it from scratch if there is no active filter.
628 self.install(shortcut)
629 else:
630 # Attach the last filter to this one, and this one to the file.
631 filter = self.shortcut(shortcut)
632 lastFilter.attach(filter)
633 filter.attach(self.file)
634
635 def revert(self):
636 """Reset any current diversions."""
637 self.currentDiversion = None
638
639 def create(self, name):
640 """Create a diversion if one does not already exist, but do not
641 divert to it yet."""
642 if name is None:
643 raise DiversionError("diversion name must be non-None")
644 if name not in self.diversions:
645 self.diversions[name] = Diversion()
646
647 def retrieve(self, name):
648 """Retrieve the given diversion."""
649 if name is None:
650 raise DiversionError("diversion name must be non-None")
651 if name in self.diversions:
652 return self.diversions[name]
653 else:
654 raise DiversionError("nonexistent diversion: %s" % name)
655
656 def divert(self, name):
657 """Start diverting."""
658 if name is None:
659 raise DiversionError("diversion name must be non-None")
660 self.create(name)
661 self.currentDiversion = name
662
663 def undivert(self, name, purgeAfterwards=False):
664 """Undivert a particular diversion."""
665 if name is None:
666 raise DiversionError("diversion name must be non-None")
667 if name in self.diversions:
668 diversion = self.diversions[name]
669 self.filter.write(diversion.asString())
670 if purgeAfterwards:
671 self.purge(name)
672 else:
673 raise DiversionError("nonexistent diversion: %s" % name)
674
675 def purge(self, name):
676 """Purge the specified diversion."""
677 if name is None:
678 raise DiversionError("diversion name must be non-None")
679 if name in self.diversions:
680 del self.diversions[name]
681 if self.currentDiversion == name:
682 self.currentDiversion = None
683
684 def undivertAll(self, purgeAfterwards=True):
685 """Undivert all pending diversions."""
686 if self.diversions:
687 self.revert() # revert before undiverting!
688 names = sorted(self.diversions.keys())
689 for name in names:
690 self.undivert(name)
691 if purgeAfterwards:
692 self.purge(name)
693
694 def purgeAll(self):
695 """Eliminate all existing diversions."""
696 if self.diversions:
697 self.diversions = {}
698 self.currentDiversion = None
699
700
701 class NullFile:
702
703 """A simple class that supports all the file-like object methods
704 but simply does nothing at all."""
705
706 def __init__(self): pass
707 def write(self, data): pass
708 def writelines(self, lines): pass
709 def flush(self): pass
710 def close(self): pass
711
712
713 class UncloseableFile:
714
715 """A simple class which wraps around a delegate file-like object
716 and lets everything through except close calls."""
717
718 def __init__(self, delegate):
719 self.delegate = delegate
720
721 def write(self, data):
722 self.delegate.write(data)
723
724 def writelines(self, lines):
725 self.delegate.writelines(data)
726
727 def flush(self):
728 self.delegate.flush()
729
730 def close(self):
731 """Eat this one."""
732 pass
733
734
735 class ProxyFile:
736
737 """The proxy file object that is intended to take the place of
738 sys.stdout. The proxy can manage a stack of file objects it is
739 writing to, and an underlying raw file object."""
740
741 def __init__(self, bottom):
742 self.stack = Stack()
743 self.bottom = bottom
744
745 def current(self):
746 """Get the current stream to write to."""
747 if self.stack:
748 return self.stack[-1][1]
749 else:
750 return self.bottom
751
752 def push(self, interpreter):
753 self.stack.push((interpreter, interpreter.stream()))
754
755 def pop(self, interpreter):
756 result = self.stack.pop()
757 assert interpreter is result[0]
758
759 def clear(self, interpreter):
760 self.stack.filter(lambda x, i=interpreter: x[0] is not i)
761
762 def write(self, data):
763 self.current().write(data)
764
765 def writelines(self, lines):
766 self.current().writelines(lines)
767
768 def flush(self):
769 self.current().flush()
770
771 def close(self):
772 """Close the current file. If the current file is the bottom, then
773 close it and dispose of it."""
774 current = self.current()
775 if current is self.bottom:
776 self.bottom = None
777 current.close()
778
779 def _testProxy(self): pass
780
781
782 class Filter:
783
784 """An abstract filter."""
785
786 def __init__(self):
787 if self.__class__ is Filter:
788 raise NotImplementedError
789 self.sink = None
790
791 def next(self):
792 """Return the next filter/file-like object in the sequence, or None."""
793 return self.sink
794
795 def __next__(self): return self.next()
796
797 def write(self, data):
798 """The standard write method; this must be overridden in subclasses."""
799 raise NotImplementedError
800
801 def writelines(self, lines):
802 """Standard writelines wrapper."""
803 for line in lines:
804 self.write(line)
805
806 def _flush(self):
807 """The _flush method should always flush the sink and should not
808 be overridden."""
809 self.sink.flush()
810
811 def flush(self):
812 """The flush method can be overridden."""
813 self._flush()
814
815 def close(self):
816 """Close the filter. Do an explicit flush first, then close the
817 sink."""
818 self.flush()
819 self.sink.close()
820
821 def attach(self, filter):
822 """Attach a filter to this one."""
823 if self.sink is not None:
824 # If it's already attached, detach it first.
825 self.detach()
826 self.sink = filter
827
828 def detach(self):
829 """Detach a filter from its sink."""
830 self.flush()
831 self._flush() # do a guaranteed flush to just to be safe
832 self.sink = None
833
834 def last(self):
835 """Find the last filter in this chain."""
836 this, last = self, self
837 while this is not None:
838 last = this
839 this = this.next()
840 return last
841
842 class NullFilter(Filter):
843
844 """A filter that never sends any output to its sink."""
845
846 def write(self, data): pass
847
848 class FunctionFilter(Filter):
849
850 """A filter that works simply by pumping its input through a
851 function which maps strings into strings."""
852
853 def __init__(self, function):
854 Filter.__init__(self)
855 self.function = function
856
857 def write(self, data):
858 self.sink.write(self.function(data))
859
860 class StringFilter(Filter):
861
862 """A filter that takes a translation string (256 characters) and
863 filters any incoming data through it."""
864
865 def __init__(self, table):
866 if not ((isinstance(table, _str) or isinstance(table, _unicode))
867 and len(table) == 256):
868 raise FilterError("table must be 256-character string")
869 Filter.__init__(self)
870 self.table = table
871
872 def write(self, data):
873 self.sink.write(data.translate(self.table))
874
875 class BufferedFilter(Filter):
876
877 """A buffered filter is one that doesn't modify the source data
878 sent to the sink, but instead holds it for a time. The standard
879 variety only sends the data along when it receives a flush
880 command."""
881
882 def __init__(self):
883 Filter.__init__(self)
884 self.buffer = ''
885
886 def write(self, data):
887 self.buffer += data
888
889 def flush(self):
890 if self.buffer:
891 self.sink.write(self.buffer)
892 self._flush()
893
894 class SizeBufferedFilter(BufferedFilter):
895
896 """A size-buffered filter only in fixed size chunks (excepting the
897 final chunk)."""
898
899 def __init__(self, bufferSize):
900 BufferedFilter.__init__(self)
901 self.bufferSize = bufferSize
902
903 def write(self, data):
904 BufferedFilter.write(self, data)
905 while len(self.buffer) > self.bufferSize:
906 chunk, self.buffer = self.buffer[:self.bufferSize], self.buffer[self.bufferSize:]
907 self.sink.write(chunk)
908
909 class LineBufferedFilter(BufferedFilter):
910
911 """A line-buffered filter only lets data through when it sees
912 whole lines."""
913
914 def __init__(self):
915 BufferedFilter.__init__(self)
916
917 def write(self, data):
918 BufferedFilter.write(self, data)
919 chunks = self.buffer.split('\n')
920 for chunk in chunks[:-1]:
921 self.sink.write(chunk + '\n')
922 self.buffer = chunks[-1]
923
924 class MaximallyBufferedFilter(BufferedFilter):
925
926 """A maximally-buffered filter only lets its data through on the final
927 close. It ignores flushes."""
928
929 def __init__(self):
930 BufferedFilter.__init__(self)
931
932 def flush(self): pass
933
934 def close(self):
935 if self.buffer:
936 BufferedFilter.flush(self)
937 self.sink.close()
938
939
940 class Context:
941
942 """An interpreter context, which encapsulates a name, an input
943 file object, and a parser object."""
944
945 DEFAULT_UNIT = 'lines'
946
947 def __init__(self, name, line=0, units=DEFAULT_UNIT):
948 self.name = name
949 self.line = line
950 self.units = units
951 self.pause = False
952
953 def bump(self, quantity=1):
954 if self.pause:
955 self.pause = False
956 else:
957 self.line += quantity
958
959 def identify(self):
960 return self.name, self.line
961
962 def __str__(self):
963 if self.units == self.DEFAULT_UNIT:
964 return "%s:%s" % (self.name, self.line)
965 else:
966 return "%s:%s[%s]" % (self.name, self.line, self.units)
967
968
969 class Hook:
970
971 """The base class for implementing hooks."""
972
973 def __init__(self):
974 self.interpreter = None
975
976 def register(self, interpreter):
977 self.interpreter = interpreter
978
979 def deregister(self, interpreter):
980 if interpreter is not self.interpreter:
981 raise Error("hook not associated with this interpreter")
982 self.interpreter = None
983
984 def push(self):
985 self.interpreter.push()
986
987 def pop(self):
988 self.interpreter.pop()
989
990 def null(self): pass
991
992 def atStartup(self): pass
993 def atReady(self): pass
994 def atFinalize(self): pass
995 def atShutdown(self): pass
996 def atParse(self, scanner, locals): pass
997 def atToken(self, token): pass
998 def atHandle(self, meta): pass
999 def atInteract(self): pass
1000
1001 def beforeInclude(self, name, file, locals): pass
1002 def afterInclude(self): pass
1003
1004 def beforeExpand(self, string, locals): pass
1005 def afterExpand(self, result): pass
1006
1007 def beforeFile(self, name, file, locals): pass
1008 def afterFile(self): pass
1009
1010 def beforeBinary(self, name, file, chunkSize, locals): pass
1011 def afterBinary(self): pass
1012
1013 def beforeString(self, name, string, locals): pass
1014 def afterString(self): pass
1015
1016 def beforeQuote(self, string): pass
1017 def afterQuote(self, result): pass
1018
1019 def beforeEscape(self, string, more): pass
1020 def afterEscape(self, result): pass
1021
1022 def beforeControl(self, type, rest, locals): pass
1023 def afterControl(self): pass
1024
1025 def beforeSignificate(self, key, value, locals): pass
1026 def afterSignificate(self): pass
1027
1028 def beforeAtomic(self, name, value, locals): pass
1029 def afterAtomic(self): pass
1030
1031 def beforeMulti(self, name, values, locals): pass
1032 def afterMulti(self): pass
1033
1034 def beforeImport(self, name, locals): pass
1035 def afterImport(self): pass
1036
1037 def beforeClause(self, catch, locals): pass
1038 def afterClause(self, exception, variable): pass
1039
1040 def beforeSerialize(self, expression, locals): pass
1041 def afterSerialize(self): pass
1042
1043 def beforeDefined(self, name, locals): pass
1044 def afterDefined(self, result): pass
1045
1046 def beforeLiteral(self, text): pass
1047 def afterLiteral(self): pass
1048
1049 def beforeEvaluate(self, expression, locals): pass
1050 def afterEvaluate(self, result): pass
1051
1052 def beforeExecute(self, statements, locals): pass
1053 def afterExecute(self): pass
1054
1055 def beforeSingle(self, source, locals): pass
1056 def afterSingle(self): pass
1057
1058 class VerboseHook(Hook):
1059
1060 """A verbose hook that reports all information received by the
1061 hook interface. This class dynamically scans the Hook base class
1062 to ensure that all hook methods are properly represented."""
1063
1064 EXEMPT_ATTRIBUTES = ['register', 'deregister', 'push', 'pop']
1065
1066 def __init__(self, output=sys.stderr):
1067 Hook.__init__(self)
1068 self.output = output
1069 self.indent = 0
1070
1071 class FakeMethod:
1072 """This is a proxy method-like object."""
1073 def __init__(self, hook, name):
1074 self.hook = hook
1075 self.name = name
1076
1077 def __call__(self, **keywords):
1078 self.hook.output.write("%s%s: %s\n" %
1079 (' ' * self.hook.indent,
1080 self.name, repr(keywords)))
1081
1082 for attribute in dir(Hook):
1083 if (attribute[:1] != '_' and
1084 attribute not in self.EXEMPT_ATTRIBUTES):
1085 self.__dict__[attribute] = FakeMethod(self, attribute)
1086
1087
1088 class Token:
1089
1090 """An element of expansion."""
1091
1092 def run(self, interpreter, locals):
1093 raise NotImplementedError
1094
1095 def string(self):
1096 raise NotImplementedError
1097
1098 def __str__(self): return self.string()
1099
1100 class NullToken(Token):
1101 """A chunk of data not containing markups."""
1102 def __init__(self, data):
1103 self.data = data
1104
1105 def run(self, interpreter, locals):
1106 interpreter.write(self.data)
1107
1108 def string(self):
1109 return self.data
1110
1111 class ExpansionToken(Token):
1112 """A token that involves an expansion."""
1113 def __init__(self, prefix, first):
1114 self.prefix = prefix
1115 self.first = first
1116
1117 def scan(self, scanner):
1118 pass
1119
1120 def run(self, interpreter, locals):
1121 pass
1122
1123 class WhitespaceToken(ExpansionToken):
1124 """A whitespace markup."""
1125 def string(self):
1126 return '%s%s' % (self.prefix, self.first)
1127
1128 class LiteralToken(ExpansionToken):
1129 """A literal markup."""
1130 def run(self, interpreter, locals):
1131 interpreter.write(self.first)
1132
1133 def string(self):
1134 return '%s%s' % (self.prefix, self.first)
1135
1136 class PrefixToken(ExpansionToken):
1137 """A prefix markup."""
1138 def run(self, interpreter, locals):
1139 interpreter.write(interpreter.prefix)
1140
1141 def string(self):
1142 return self.prefix * 2
1143
1144 class CommentToken(ExpansionToken):
1145 """A comment markup."""
1146 def scan(self, scanner):
1147 loc = scanner.find('\n')
1148 if loc >= 0:
1149 self.comment = scanner.chop(loc, 1)
1150 else:
1151 raise TransientParseError("comment expects newline")
1152
1153 def string(self):
1154 return '%s#%s\n' % (self.prefix, self.comment)
1155
1156 class ContextNameToken(ExpansionToken):
1157 """A context name change markup."""
1158 def scan(self, scanner):
1159 loc = scanner.find('\n')
1160 if loc >= 0:
1161 self.name = scanner.chop(loc, 1).strip()
1162 else:
1163 raise TransientParseError("context name expects newline")
1164
1165 def run(self, interpreter, locals):
1166 context = interpreter.context()
1167 context.name = self.name
1168
1169 class ContextLineToken(ExpansionToken):
1170 """A context line change markup."""
1171 def scan(self, scanner):
1172 loc = scanner.find('\n')
1173 if loc >= 0:
1174 try:
1175 self.line = int(scanner.chop(loc, 1))
1176 except ValueError:
1177 raise ParseError("context line requires integer")
1178 else:
1179 raise TransientParseError("context line expects newline")
1180
1181 def run(self, interpreter, locals):
1182 context = interpreter.context()
1183 context.line = self.line
1184 context.pause = True
1185
1186 class EscapeToken(ExpansionToken):
1187 """An escape markup."""
1188 def scan(self, scanner):
1189 try:
1190 code = scanner.chop(1)
1191 result = None
1192 if code in '()[]{}\'\"\\': # literals
1193 result = code
1194 elif code == '0': # NUL
1195 result = '\x00'
1196 elif code == 'a': # BEL
1197 result = '\x07'
1198 elif code == 'b': # BS
1199 result = '\x08'
1200 elif code == 'd': # decimal code
1201 decimalCode = scanner.chop(3)
1202 result = chr(int(decimalCode, 10))
1203 elif code == 'e': # ESC
1204 result = '\x1b'
1205 elif code == 'f': # FF
1206 result = '\x0c'
1207 elif code == 'h': # DEL
1208 result = '\x7f'
1209 elif code == 'n': # LF (newline)
1210 result = '\x0a'
1211 elif code == 'N': # Unicode character name
1212 theSubsystem.assertUnicode()
1213 import unicodedata
1214 if scanner.chop(1) != '{':
1215 raise ParseError("Unicode name escape should be \\N{...}")
1216 i = scanner.find('}')
1217 name = scanner.chop(i, 1)
1218 try:
1219 result = unicodedata.lookup(name)
1220 except KeyError:
1221 raise SubsystemError("unknown Unicode character name: %s" % name)
1222 elif code == 'o': # octal code
1223 octalCode = scanner.chop(3)
1224 result = chr(int(octalCode, 8))
1225 elif code == 'q': # quaternary code
1226 quaternaryCode = scanner.chop(4)
1227 result = chr(int(quaternaryCode, 4))
1228 elif code == 'r': # CR
1229 result = '\x0d'
1230 elif code in 's ': # SP
1231 result = ' '
1232 elif code == 't': # HT
1233 result = '\x09'
1234 elif code in 'u': # Unicode 16-bit hex literal
1235 theSubsystem.assertUnicode()
1236 hexCode = scanner.chop(4)
1237 result = _unichr(int(hexCode, 16))
1238 elif code in 'U': # Unicode 32-bit hex literal
1239 theSubsystem.assertUnicode()
1240 hexCode = scanner.chop(8)
1241 result = _unichr(int(hexCode, 16))
1242 elif code == 'v': # VT
1243 result = '\x0b'
1244 elif code == 'x': # hexadecimal code
1245 hexCode = scanner.chop(2)
1246 result = chr(int(hexCode, 16))
1247 elif code == 'z': # EOT
1248 result = '\x04'
1249 elif code == '^': # control character
1250 controlCode = scanner.chop(1).upper()
1251 if controlCode >= '@' and controlCode <= '`':
1252 result = chr(ord(controlCode) - ord('@'))
1253 elif controlCode == '?':
1254 result = '\x7f'
1255 else:
1256 raise ParseError("invalid escape control code")
1257 else:
1258 raise ParseError("unrecognized escape code")
1259 assert result is not None
1260 self.code = result
1261 except ValueError:
1262 raise ParseError("invalid numeric escape code")
1263
1264 def run(self, interpreter, locals):
1265 interpreter.write(self.code)
1266
1267 def string(self):
1268 return '%s\\x%02x' % (self.prefix, ord(self.code))
1269
1270 class SignificatorToken(ExpansionToken):
1271 """A significator markup."""
1272 def scan(self, scanner):
1273 loc = scanner.find('\n')
1274 if loc >= 0:
1275 line = scanner.chop(loc, 1)
1276 if not line:
1277 raise ParseError("significator must have nonblank key")
1278 if line[0] in ' \t\v\n':
1279 raise ParseError("no whitespace between % and key")
1280 # Work around a subtle CPython-Jython difference by stripping
1281 # the string before splitting it: 'a '.split(None, 1) has two
1282 # elements in Jython 2.1).
1283 fields = line.strip().split(None, 1)
1284 if len(fields) == 2 and fields[1] == '':
1285 fields.pop()
1286 self.key = fields[0]
1287 if len(fields) < 2:
1288 fields.append(None)
1289 self.key, self.valueCode = fields
1290 else:
1291 raise TransientParseError("significator expects newline")
1292
1293 def run(self, interpreter, locals):
1294 value = self.valueCode
1295 if value is not None:
1296 value = interpreter.evaluate(value.strip(), locals)
1297 interpreter.significate(self.key, value)
1298
1299 def string(self):
1300 if self.valueCode is None:
1301 return '%s%%%s\n' % (self.prefix, self.key)
1302 else:
1303 return '%s%%%s %s\n' % (self.prefix, self.key, self.valueCode)
1304
1305 class ExpressionToken(ExpansionToken):
1306 """An expression markup."""
1307 def scan(self, scanner):
1308 z = scanner.complex('(', ')', 0)
1309 try:
1310 q = scanner.next('$', 0, z, True)
1311 except ParseError:
1312 q = z
1313 try:
1314 i = scanner.next('?', 0, q, True)
1315 try:
1316 j = scanner.next('!', i, q, True)
1317 except ParseError:
1318 try:
1319 j = scanner.next(':', i, q, True) # DEPRECATED
1320 except ParseError:
1321 j = q
1322 except ParseError:
1323 i = j = q
1324 code = scanner.chop(z, 1)
1325 self.testCode = code[:i]
1326 self.thenCode = code[i + 1:j]
1327 self.elseCode = code[j + 1:q]
1328 self.exceptCode = code[q + 1:z]
1329
1330 def run(self, interpreter, locals):
1331 try:
1332 result = interpreter.evaluate(self.testCode, locals)
1333 if self.thenCode:
1334 if result:
1335 result = interpreter.evaluate(self.thenCode, locals)
1336 else:
1337 if self.elseCode:
1338 result = interpreter.evaluate(self.elseCode, locals)
1339 else:
1340 result = None
1341 except SyntaxError:
1342 # Don't catch syntax errors; let them through.
1343 raise
1344 except:
1345 if self.exceptCode:
1346 result = interpreter.evaluate(self.exceptCode, locals)
1347 else:
1348 raise
1349 if result is not None:
1350 interpreter.write(str(result))
1351
1352 def string(self):
1353 result = self.testCode
1354 if self.thenCode:
1355 result += '?' + self.thenCode
1356 if self.elseCode:
1357 result += '!' + self.elseCode
1358 if self.exceptCode:
1359 result += '$' + self.exceptCode
1360 return '%s(%s)' % (self.prefix, result)
1361
1362 class StringLiteralToken(ExpansionToken):
1363 """A string token markup."""
1364 def scan(self, scanner):
1365 scanner.retreat()
1366 assert scanner[0] == self.first
1367 i = scanner.quote()
1368 self.literal = scanner.chop(i)
1369
1370 def run(self, interpreter, locals):
1371 interpreter.literal(self.literal)
1372
1373 def string(self):
1374 return '%s%s' % (self.prefix, self.literal)
1375
1376 class SimpleExpressionToken(ExpansionToken):
1377 """A simple expression markup."""
1378 def scan(self, scanner):
1379 i = scanner.simple()
1380 self.code = self.first + scanner.chop(i)
1381
1382 def run(self, interpreter, locals):
1383 interpreter.serialize(self.code, locals)
1384
1385 def string(self):
1386 return '%s%s' % (self.prefix, self.code)
1387
1388 class ReprToken(ExpansionToken):
1389 """A repr markup."""
1390 def scan(self, scanner):
1391 i = scanner.next('`', 0)
1392 self.code = scanner.chop(i, 1)
1393
1394 def run(self, interpreter, locals):
1395 interpreter.write(repr(interpreter.evaluate(self.code, locals)))
1396
1397 def string(self):
1398 return '%s`%s`' % (self.prefix, self.code)
1399
1400 class InPlaceToken(ExpansionToken):
1401 """An in-place markup."""
1402 def scan(self, scanner):
1403 i = scanner.next(':', 0)
1404 j = scanner.next(':', i + 1)
1405 self.code = scanner.chop(i, j - i + 1)
1406
1407 def run(self, interpreter, locals):
1408 interpreter.write("%s:%s:" % (interpreter.prefix, self.code))
1409 try:
1410 interpreter.serialize(self.code, locals)
1411 finally:
1412 interpreter.write(":")
1413
1414 def string(self):
1415 return '%s:%s::' % (self.prefix, self.code)
1416
1417 class StatementToken(ExpansionToken):
1418 """A statement markup."""
1419 def scan(self, scanner):
1420 i = scanner.complex('{', '}', 0)
1421 self.code = scanner.chop(i, 1)
1422
1423 def run(self, interpreter, locals):
1424 interpreter.execute(self.code, locals)
1425
1426 def string(self):
1427 return '%s{%s}' % (self.prefix, self.code)
1428
1429 class CustomToken(ExpansionToken):
1430 """A custom markup."""
1431 def scan(self, scanner):
1432 i = scanner.complex('<', '>', 0)
1433 self.contents = scanner.chop(i, 1)
1434
1435 def run(self, interpreter, locals):
1436 interpreter.invokeCallback(self.contents)
1437
1438 def string(self):
1439 return '%s<%s>' % (self.prefix, self.contents)
1440
1441 class ControlToken(ExpansionToken):
1442
1443 """A control token."""
1444
1445 PRIMARY_TYPES = ['if', 'for', 'while', 'try', 'def']
1446 SECONDARY_TYPES = ['elif', 'else', 'except', 'finally']
1447 TERTIARY_TYPES = ['continue', 'break']
1448 GREEDY_TYPES = ['if', 'elif', 'for', 'while', 'def', 'end']
1449 END_TYPES = ['end']
1450
1451 IN_RE = re.compile(r"\bin\b")
1452
1453 def scan(self, scanner):
1454 scanner.acquire()
1455 i = scanner.complex('[', ']', 0)
1456 self.contents = scanner.chop(i, 1)
1457 fields = self.contents.strip().split(' ', 1)
1458 if len(fields) > 1:
1459 self.type, self.rest = fields
1460 else:
1461 self.type = fields[0]
1462 self.rest = None
1463 self.subtokens = []
1464 if self.type in self.GREEDY_TYPES and self.rest is None:
1465 raise ParseError("control '%s' needs arguments" % self.type)
1466 if self.type in self.PRIMARY_TYPES:
1467 self.subscan(scanner, self.type)
1468 self.kind = 'primary'
1469 elif self.type in self.SECONDARY_TYPES:
1470 self.kind = 'secondary'
1471 elif self.type in self.TERTIARY_TYPES:
1472 self.kind = 'tertiary'
1473 elif self.type in self.END_TYPES:
1474 self.kind = 'end'
1475 else:
1476 raise ParseError("unknown control markup: '%s'" % self.type)
1477 scanner.release()
1478
1479 def subscan(self, scanner, primary):
1480 """Do a subscan for contained tokens."""
1481 while True:
1482 token = scanner.one()
1483 if token is None:
1484 raise TransientParseError("control '%s' needs more tokens" % primary)
1485 if (isinstance(token, ControlToken) and
1486 token.type in self.END_TYPES):
1487 if token.rest != primary:
1488 raise ParseError("control must end with 'end %s'" % primary)
1489 break
1490 self.subtokens.append(token)
1491
1492 def build(self, allowed=None):
1493 """Process the list of subtokens and divide it into a list of
1494 2-tuples, consisting of the dividing tokens and the list of
1495 subtokens that follow them. If allowed is specified, it will
1496 represent the list of the only secondary markup types which
1497 are allowed."""
1498 if allowed is None:
1499 allowed = SECONDARY_TYPES
1500 result = []
1501 latest = []
1502 result.append((self, latest))
1503 for subtoken in self.subtokens:
1504 if (isinstance(subtoken, ControlToken) and
1505 subtoken.kind == 'secondary'):
1506 if subtoken.type not in allowed:
1507 raise ParseError("control unexpected secondary: '%s'" % subtoken.type)
1508 latest = []
1509 result.append((subtoken, latest))
1510 else:
1511 latest.append(subtoken)
1512 return result
1513
1514 def run(self, interpreter, locals):
1515 interpreter.invoke('beforeControl', type=self.type, rest=self.rest,
1516 locals=locals)
1517 if self.type == 'if':
1518 info = self.build(['elif', 'else'])
1519 elseTokens = None
1520 if info[-1][0].type == 'else':
1521 elseTokens = info.pop()[1]
1522 for secondary, subtokens in info:
1523 if secondary.type not in ('if', 'elif'):
1524 raise ParseError("control 'if' unexpected secondary: '%s'" % secondary.type)
1525 if interpreter.evaluate(secondary.rest, locals):
1526 self.subrun(subtokens, interpreter, locals)
1527 break
1528 else:
1529 if elseTokens:
1530 self.subrun(elseTokens, interpreter, locals)
1531 elif self.type == 'for':
1532 sides = self.IN_RE.split(self.rest, 1)
1533 if len(sides) != 2:
1534 raise ParseError("control expected 'for x in seq'")
1535 iterator, sequenceCode = sides
1536 info = self.build(['else'])
1537 elseTokens = None
1538 if info[-1][0].type == 'else':
1539 elseTokens = info.pop()[1]
1540 if len(info) != 1:
1541 raise ParseError("control 'for' expects at most one 'else'")
1542 sequence = interpreter.evaluate(sequenceCode, locals)
1543 for element in sequence:
1544 try:
1545 interpreter.assign(iterator, element, locals)
1546 self.subrun(info[0][1], interpreter, locals)
1547 except ContinueFlow:
1548 continue
1549 except BreakFlow:
1550 break
1551 else:
1552 if elseTokens:
1553 self.subrun(elseTokens, interpreter, locals)
1554 elif self.type == 'while':
1555 testCode = self.rest
1556 info = self.build(['else'])
1557 elseTokens = None
1558 if info[-1][0].type == 'else':
1559 elseTokens = info.pop()[1]
1560 if len(info) != 1:
1561 raise ParseError("control 'while' expects at most one 'else'")
1562 atLeastOnce = False
1563 while True:
1564 try:
1565 if not interpreter.evaluate(testCode, locals):
1566 break
1567 atLeastOnce = True
1568 self.subrun(info[0][1], interpreter, locals)
1569 except ContinueFlow:
1570 continue
1571 except BreakFlow:
1572 break
1573 if not atLeastOnce and elseTokens:
1574 self.subrun(elseTokens, interpreter, locals)
1575 elif self.type == 'try':
1576 info = self.build(['except', 'finally'])
1577 if len(info) == 1:
1578 raise ParseError("control 'try' needs 'except' or 'finally'")
1579 type = info[-1][0].type
1580 if type == 'except':
1581 for secondary, _tokens in info[1:]:
1582 if secondary.type != 'except':
1583 raise ParseError("control 'try' cannot have 'except' and 'finally'")
1584 else:
1585 assert type == 'finally'
1586 if len(info) != 2:
1587 raise ParseError("control 'try' can only have one 'finally'")
1588 if type == 'except':
1589 try:
1590 self.subrun(info[0][1], interpreter, locals)
1591 except FlowError:
1592 raise
1593 except Exception:
1594 e = sys.exc_info()[1]
1595 for secondary, tokens in info[1:]:
1596 exception, variable = interpreter.clause(secondary.rest)
1597 if variable is not None:
1598 interpreter.assign(variable, e)
1599 if isinstance(e, exception):
1600 self.subrun(tokens, interpreter, locals)
1601 break
1602 else:
1603 raise
1604 else:
1605 try:
1606 self.subrun(info[0][1], interpreter, locals)
1607 finally:
1608 self.subrun(info[1][1], interpreter, locals)
1609 elif self.type == 'continue':
1610 raise ContinueFlow("control 'continue' without 'for', 'while'")
1611 elif self.type == 'break':
1612 raise BreakFlow("control 'break' without 'for', 'while'")
1613 elif self.type == 'def':
1614 signature = self.rest
1615 definition = self.substring()
1616 code = ('def %s:\n'
1617 ' r"""%s"""\n'
1618 ' return %s.expand(r"""%s""", locals())\n' %
1619 (signature, definition, interpreter.pseudo, definition))
1620 interpreter.execute(code, locals)
1621 elif self.type == 'end':
1622 raise ParseError("control 'end' requires primary markup")
1623 else:
1624 raise ParseError("control '%s' cannot be at this level" % self.type)
1625 interpreter.invoke('afterControl')
1626
1627 def subrun(self, tokens, interpreter, locals):
1628 """Execute a sequence of tokens."""
1629 for token in tokens:
1630 token.run(interpreter, locals)
1631
1632 def substring(self):
1633 return ''.join(str(x) for x in self.subtokens)
1634
1635 def string(self):
1636 if self.kind == 'primary':
1637 return ('%s[%s]%s%s[end %s]' %
1638 (self.prefix, self.contents, self.substring(),
1639 self.prefix, self.type))
1640 else:
1641 return '%s[%s]' % (self.prefix, self.contents)
1642
1643
1644 class Scanner:
1645
1646 """A scanner holds a buffer for lookahead parsing and has the
1647 ability to scan for special symbols and indicators in that
1648 buffer."""
1649
1650 # This is the token mapping table that maps first characters to
1651 # token classes.
1652 TOKEN_MAP = [
1653 (None, PrefixToken),
1654 (' \t\v\r\n', WhitespaceToken),
1655 (')]}', LiteralToken),
1656 ('\\', EscapeToken),
1657 ('#', CommentToken),
1658 ('?', ContextNameToken),
1659 ('!', ContextLineToken),
1660 ('%', SignificatorToken),
1661 ('(', ExpressionToken),
1662 (IDENTIFIER_FIRST_CHARS, SimpleExpressionToken),
1663 ('\'\"', StringLiteralToken),
1664 ('`', ReprToken),
1665 (':', InPlaceToken),
1666 ('[', ControlToken),
1667 ('{', StatementToken),
1668 ('<', CustomToken),
1669 ]
1670
1671 def __init__(self, prefix, data=''):
1672 self.prefix = prefix
1673 self.pointer = 0
1674 self.buffer = data
1675 self.lock = 0
1676
1677 def __nonzero__(self): return self.pointer < len(self.buffer) # 2.x
1678 def __bool__(self): return self.pointer < len(self.buffer) # 3.x
1679 def __len__(self): return len(self.buffer) - self.pointer
1680
1681 def __getitem__(self, index):
1682 if isinstance(index, slice):
1683 assert index.step is None or index.step == 1
1684 return self.__getslice__(index.start, index.stop)
1685 else:
1686 return self.buffer[self.pointer + index]
1687
1688 def __getslice__(self, start, stop):
1689 if start is None:
1690 start = 0
1691 if stop is None:
1692 stop = len(self)
1693 if stop > len(self):
1694 stop = len(self)
1695 return self.buffer[self.pointer + start:self.pointer + stop]
1696
1697 def advance(self, count=1):
1698 """Advance the pointer count characters."""
1699 self.pointer += count
1700
1701 def retreat(self, count=1):
1702 self.pointer = self.pointer - count
1703 if self.pointer < 0:
1704 raise ParseError("can't retreat back over synced out chars")
1705
1706 def set(self, data):
1707 """Start the scanner digesting a new batch of data; start the pointer
1708 over from scratch."""
1709 self.pointer = 0
1710 self.buffer = data
1711
1712 def feed(self, data):
1713 """Feed some more data to the scanner."""
1714 self.buffer += data
1715
1716 def chop(self, count=None, slop=0):
1717 """Chop the first count + slop characters off the front, and return
1718 the first count. If count is not specified, then return
1719 everything."""
1720 if count is None:
1721 assert slop == 0
1722 count = len(self)
1723 if count > len(self):
1724 raise TransientParseError("not enough data to read")
1725 result = self[:count]
1726 self.advance(count + slop)
1727 return result
1728
1729 def acquire(self):
1730 """Lock the scanner so it doesn't destroy data on sync."""
1731 self.lock += 1
1732
1733 def release(self):
1734 """Unlock the scanner."""
1735 self.lock -= 1
1736
1737 def sync(self):
1738 """Sync up the buffer with the read head."""
1739 if self.lock == 0 and self.pointer != 0:
1740 self.buffer = self.buffer[self.pointer:]
1741 self.pointer = 0
1742
1743 def unsync(self):
1744 """Undo changes; reset the read head."""
1745 if self.pointer != 0:
1746 self.lock = 0
1747 self.pointer = 0
1748
1749 def rest(self):
1750 """Get the remainder of the buffer."""
1751 return self[:]
1752
1753 def read(self, i=0, count=1):
1754 """Read count chars starting from i; raise a transient error if
1755 there aren't enough characters remaining."""
1756 if len(self) < i + count:
1757 raise TransientParseError("need more data to read")
1758 else:
1759 return self[i:i + count]
1760
1761 def check(self, i, archetype=None):
1762 """Scan for the next single or triple quote, with the specified
1763 archetype. Return the found quote or None."""
1764 quote = None
1765 if self[i] in '\'\"':
1766 quote = self[i]
1767 if len(self) - i < 3:
1768 for j in range(i, len(self)):
1769 if self[i] == quote:
1770 return quote
1771 else:
1772 raise TransientParseError("need to scan for rest of quote")
1773 if self[i + 1] == self[i + 2] == quote:
1774 quote = quote * 3
1775 if quote is not None:
1776 if archetype is None:
1777 return quote
1778 else:
1779 if archetype == quote:
1780 return quote
1781 elif len(archetype) < len(quote) and archetype[0] == quote[0]:
1782 return archetype
1783 else:
1784 return None
1785 else:
1786 return None
1787
1788 def find(self, sub, start=0, end=None):
1789 """Find the next occurrence of the character, or return -1."""
1790 if end is not None:
1791 return self.rest().find(sub, start, end)
1792 else:
1793 return self.rest().find(sub, start)
1794
1795 def last(self, char, start=0, end=None):
1796 """Find the first character that is _not_ the specified character."""
1797 if end is None:
1798 end = len(self)
1799 i = start
1800 while i < end:
1801 if self[i] != char:
1802 return i
1803 i += 1
1804 else:
1805 raise TransientParseError("expecting other than %s" % char)
1806
1807 def next(self, target, start=0, end=None, mandatory=False):
1808 """Scan for the next occurrence of one of the characters in
1809 the target string; optionally, make the scan mandatory."""
1810 if mandatory:
1811 assert end is not None
1812 quote = None
1813 if end is None:
1814 end = len(self)
1815 i = start
1816 while i < end:
1817 newQuote = self.check(i, quote)
1818 if newQuote:
1819 if newQuote == quote:
1820 quote = None
1821 else:
1822 quote = newQuote
1823 i += len(newQuote)
1824 else:
1825 c = self[i]
1826 if quote:
1827 if c == '\\':
1828 i += 1
1829 else:
1830 if c in target:
1831 return i
1832 i += 1
1833 else:
1834 if mandatory:
1835 raise ParseError("expecting %s, not found" % target)
1836 else:
1837 raise TransientParseError("expecting ending character")
1838
1839 def quote(self, start=0, end=None, mandatory=False):
1840 """Scan for the end of the next quote."""
1841 assert self[start] in '\'\"'
1842 quote = self.check(start)
1843 if end is None:
1844 end = len(self)
1845 i = start + len(quote)
1846 while i < end:
1847 newQuote = self.check(i, quote)
1848 if newQuote:
1849 i += len(newQuote)
1850 if newQuote == quote:
1851 return i
1852 else:
1853 c = self[i]
1854 if c == '\\':
1855 i += 1
1856 i += 1
1857 else:
1858 if mandatory:
1859 raise ParseError("expecting end of string literal")
1860 else:
1861 raise TransientParseError("expecting end of string literal")
1862
1863 def nested(self, enter, exit, start=0, end=None):
1864 """Scan from i for an ending sequence, respecting entries and exits
1865 only."""
1866 depth = 0
1867 if end is None:
1868 end = len(self)
1869 i = start
1870 while i < end:
1871 c = self[i]
1872 if c == enter:
1873 depth += 1
1874 elif c == exit:
1875 depth -= 1
1876 if depth < 0:
1877 return i
1878 i += 1
1879 else:
1880 raise TransientParseError("expecting end of complex expression")
1881
1882 def complex(self, enter, exit, start=0, end=None, skip=None):
1883 """Scan from i for an ending sequence, respecting quotes,
1884 entries and exits."""
1885 quote = None
1886 depth = 0
1887 if end is None:
1888 end = len(self)
1889 last = None
1890 i = start
1891 while i < end:
1892 newQuote = self.check(i, quote)
1893 if newQuote:
1894 if newQuote == quote:
1895 quote = None
1896 else:
1897 quote = newQuote
1898 i += len(newQuote)
1899 else:
1900 c = self[i]
1901 if quote:
1902 if c == '\\':
1903 i += 1
1904 else:
1905 if skip is None or last != skip:
1906 if c == enter:
1907 depth += 1
1908 elif c == exit:
1909 depth -= 1
1910 if depth < 0:
1911 return i
1912 last = c
1913 i += 1
1914 else:
1915 raise TransientParseError("expecting end of complex expression")
1916
1917 def word(self, start=0):
1918 """Scan from i for a simple word."""
1919 length = len(self)
1920 i = start
1921 while i < length:
1922 if not self[i] in IDENTIFIER_CHARS:
1923 return i
1924 i += 1
1925 else:
1926 raise TransientParseError("expecting end of word")
1927
1928 def phrase(self, start=0):
1929 """Scan from i for a phrase (e.g., 'word', 'f(a, b, c)', 'a[i]', or
1930 combinations like 'x[i](a)'."""
1931 # Find the word.
1932 i = self.word(start)
1933 while i < len(self) and self[i] in '([{':
1934 enter = self[i]
1935 if enter == '{':
1936 raise ParseError("curly braces can't open simple expressions")
1937 exit = ENDING_CHARS[enter]
1938 i = self.complex(enter, exit, i + 1) + 1
1939 return i
1940
1941 def simple(self, start=0):
1942 """Scan from i for a simple expression, which consists of one
1943 more phrases separated by dots."""
1944 i = self.phrase(start)
1945 length = len(self)
1946 while i < length and self[i] == '.':
1947 i = self.phrase(i)
1948 # Make sure we don't end with a trailing dot.
1949 while i > 0 and self[i - 1] == '.':
1950 i -= 1
1951 return i
1952
1953 def one(self):
1954 """Parse and return one token, or None if the scanner is empty."""
1955 if not self:
1956 return None
1957 if not self.prefix:
1958 loc = -1
1959 else:
1960 loc = self.find(self.prefix)
1961 if loc < 0:
1962 # If there's no prefix in the buffer, then set the location to
1963 # the end so the whole thing gets processed.
1964 loc = len(self)
1965 if loc == 0:
1966 # If there's a prefix at the beginning of the buffer, process
1967 # an expansion.
1968 prefix = self.chop(1)
1969 assert prefix == self.prefix
1970 first = self.chop(1)
1971 if first == self.prefix:
1972 first = None
1973 for firsts, factory in self.TOKEN_MAP:
1974 if firsts is None:
1975 if first is None:
1976 break
1977 elif first in firsts:
1978 break
1979 else:
1980 raise ParseError("unknown markup: %s%s" % (self.prefix, first))
1981 token = factory(self.prefix, first)
1982 try:
1983 token.scan(self)
1984 except TransientParseError:
1985 # If a transient parse error occurs, reset the buffer pointer
1986 # so we can (conceivably) try again later.
1987 self.unsync()
1988 raise
1989 else:
1990 # Process everything up to loc as a null token.
1991 data = self.chop(loc)
1992 token = NullToken(data)
1993 self.sync()
1994 return token
1995
1996
1997 class Interpreter:
1998
1999 """An interpreter can process chunks of EmPy code."""
2000
2001 # Constants.
2002
2003 VERSION = __version__
2004 SIGNIFICATOR_RE_SUFFIX = SIGNIFICATOR_RE_SUFFIX
2005 SIGNIFICATOR_RE_STRING = None
2006
2007 # Types.
2008
2009 Interpreter = None # define this below to prevent a circular reference
2010 Hook = Hook # DEPRECATED
2011 Filter = Filter # DEPRECATED
2012 NullFilter = NullFilter # DEPRECATED
2013 FunctionFilter = FunctionFilter # DEPRECATED
2014 StringFilter = StringFilter # DEPRECATED
2015 BufferedFilter = BufferedFilter # DEPRECATED
2016 SizeBufferedFilter = SizeBufferedFilter # DEPRECATED
2017 LineBufferedFilter = LineBufferedFilter # DEPRECATED
2018 MaximallyBufferedFilter = MaximallyBufferedFilter # DEPRECATED
2019
2020 # Tables.
2021
2022 ESCAPE_CODES = {0x00: '0', 0x07: 'a', 0x08: 'b', 0x1b: 'e', 0x0c: 'f',
2023 0x7f: 'h', 0x0a: 'n', 0x0d: 'r', 0x09: 't', 0x0b: 'v',
2024 0x04: 'z'}
2025
2026 ASSIGN_TOKEN_RE = re.compile(r"[_a-zA-Z][_a-zA-Z0-9]*|\(|\)|,")
2027
2028 DEFAULT_OPTIONS = {BANGPATH_OPT: True,
2029 BUFFERED_OPT: False,
2030 RAW_OPT: False,
2031 EXIT_OPT: True,
2032 FLATTEN_OPT: False,
2033 OVERRIDE_OPT: True,
2034 CALLBACK_OPT: False}
2035
2036 _wasProxyInstalled = False # was a proxy installed?
2037
2038 # Construction, initialization, destruction.
2039
2040 def __init__(self, output=None, argv=None, prefix=DEFAULT_PREFIX,
2041 pseudo=None, options=None, globals=None, hooks=None):
2042 self.interpreter = self # DEPRECATED
2043 # Set up the stream.
2044 if output is None:
2045 output = UncloseableFile(sys.__stdout__)
2046 self.output = output
2047 self.prefix = prefix
2048 if pseudo is None:
2049 pseudo = DEFAULT_PSEUDOMODULE_NAME
2050 self.pseudo = pseudo
2051 if argv is None:
2052 argv = [DEFAULT_SCRIPT_NAME]
2053 self.argv = argv
2054 self.args = argv[1:]
2055 if options is None:
2056 options = {}
2057 self.options = options
2058 # Initialize any hooks.
2059 self.hooksEnabled = None # special sentinel meaning "false until added"
2060 self.hooks = []
2061 if hooks is None:
2062 hooks = []
2063 for hook in hooks:
2064 self.register(hook)
2065 # Initialize callback.
2066 self.callback = None
2067 # Finalizers.
2068 self.finals = []
2069 # The interpreter stacks.
2070 self.contexts = Stack()
2071 self.streams = Stack()
2072 # Now set up the globals.
2073 self.globals = globals
2074 self.fix()
2075 self.history = Stack()
2076 # Install a proxy stdout if one hasn't been already.
2077 self.installProxy()
2078 # Finally, reset the state of all the stacks.
2079 self.reset()
2080 # Okay, now flatten the namespaces if that option has been set.
2081 if self.options.get(FLATTEN_OPT, False):
2082 self.flatten()
2083 # Set up old pseudomodule attributes.
2084 if prefix is None:
2085 self.SIGNIFICATOR_RE_STRING = None
2086 else:
2087 self.SIGNIFICATOR_RE_STRING = prefix + self.SIGNIFICATOR_RE_SUFFIX
2088 self.Interpreter = self.__class__
2089 # Done. Now declare that we've started up.
2090 self.invoke('atStartup')
2091
2092 def __del__(self):
2093 self.shutdown()
2094
2095 def __repr__(self):
2096 return ('<%s pseudomodule/interpreter at 0x%x>' %
2097 (self.pseudo, id(self)))
2098
2099 def ready(self):
2100 """Declare the interpreter ready for normal operations."""
2101 self.invoke('atReady')
2102
2103 def fix(self):
2104 """Reset the globals, stamping in the pseudomodule."""
2105 if self.globals is None:
2106 self.globals = {}
2107 # Make sure that there is no collision between two interpreters'
2108 # globals.
2109 if self.pseudo in self.globals:
2110 if self.globals[self.pseudo] is not self:
2111 raise Error("interpreter globals collision")
2112 self.globals[self.pseudo] = self
2113
2114 def unfix(self):
2115 """Remove the pseudomodule (if present) from the globals."""
2116 UNWANTED_KEYS = [self.pseudo, '__builtins__']
2117 for unwantedKey in UNWANTED_KEYS:
2118 if unwantedKey in self.globals:
2119 del self.globals[unwantedKey]
2120
2121 def update(self, other):
2122 """Update the current globals dictionary with another dictionary."""
2123 self.globals.update(other)
2124 self.fix()
2125
2126 def clear(self):
2127 """Clear out the globals dictionary with a brand new one."""
2128 self.globals = {}
2129 self.fix()
2130
2131 def save(self, deep=True):
2132 if deep:
2133 copyMethod = copy.deepcopy
2134 else:
2135 copyMethod = copy.copy
2136 """Save a copy of the current globals on the history stack."""
2137 self.unfix()
2138 self.history.push(copyMethod(self.globals))
2139 self.fix()
2140
2141 def restore(self, destructive=True):
2142 """Restore the topmost historic globals."""
2143 if destructive:
2144 fetchMethod = self.history.pop
2145 else:
2146 fetchMethod = self.history.top
2147 self.unfix()
2148 self.globals = fetchMethod()
2149 self.fix()
2150
2151 def shutdown(self):
2152 """Declare this interpreting session over; close the stream file
2153 object. This method is idempotent."""
2154 if self.streams is not None:
2155 try:
2156 self.finalize()
2157 self.invoke('atShutdown')
2158 while self.streams:
2159 stream = self.streams.pop()
2160 stream.close()
2161 finally:
2162 self.streams = None
2163
2164 def ok(self):
2165 """Is the interpreter still active?"""
2166 return self.streams is not None
2167
2168 # Writeable file-like methods.
2169
2170 def write(self, data):
2171 self.stream().write(data)
2172
2173 def writelines(self, stuff):
2174 self.stream().writelines(stuff)
2175
2176 def flush(self):
2177 self.stream().flush()
2178
2179 def close(self):
2180 self.shutdown()
2181
2182 # Stack-related activity.
2183
2184 def context(self):
2185 return self.contexts.top()
2186
2187 def stream(self):
2188 return self.streams.top()
2189
2190 def reset(self):
2191 self.contexts.purge()
2192 self.streams.purge()
2193 self.streams.push(Stream(self.output))
2194 if self.options.get(OVERRIDE_OPT, True):
2195 sys.stdout.clear(self)
2196
2197 def push(self):
2198 if self.options.get(OVERRIDE_OPT, True):
2199 sys.stdout.push(self)
2200
2201 def pop(self):
2202 if self.options.get(OVERRIDE_OPT, True):
2203 sys.stdout.pop(self)
2204
2205 # Higher-level operations.
2206
2207 def include(self, fileOrFilename, locals=None):
2208 """Do an include pass on a file or filename."""
2209 if isinstance(fileOrFilename, _str):
2210 # Either it's a string representing a filename ...
2211 filename = fileOrFilename
2212 name = filename
2213 file = theSubsystem.open(filename, 'r')
2214 else:
2215 # ... or a file object.
2216 file = fileOrFilename
2217 name = "<%s>" % str(file.__class__)
2218 self.invoke('beforeInclude', name=name, file=file, locals=locals)
2219 self.file(file, name, locals)
2220 self.invoke('afterInclude')
2221
2222 def expand(self, data, locals=None):
2223 """Do an explicit expansion on a subordinate stream."""
2224 outFile = StringIO()
2225 stream = Stream(outFile)
2226 self.invoke('beforeExpand', string=data, locals=locals)
2227 self.streams.push(stream)
2228 try:
2229 self.string(data, '<expand>', locals)
2230 stream.flush()
2231 expansion = outFile.getvalue()
2232 self.invoke('afterExpand', result=expansion)
2233 return expansion
2234 finally:
2235 self.streams.pop()
2236
2237 def quote(self, data):
2238 """Quote the given string so that if it were expanded it would
2239 evaluate to the original."""
2240 self.invoke('beforeQuote', string=data)
2241 scanner = Scanner(self.prefix, data)
2242 result = []
2243 i = 0
2244 try:
2245 j = scanner.next(self.prefix, i)
2246 result.append(data[i:j])
2247 result.append(self.prefix * 2)
2248 i = j + 1
2249 except TransientParseError:
2250 pass
2251 result.append(data[i:])
2252 result = ''.join(result)
2253 self.invoke('afterQuote', result=result)
2254 return result
2255
2256 def escape(self, data, more=''):
2257 """Escape a string so that nonprintable characters are replaced
2258 with compatible EmPy expansions."""
2259 self.invoke('beforeEscape', string=data, more=more)
2260 result = []
2261 for char in data:
2262 if char < ' ' or char > '~':
2263 charOrd = ord(char)
2264 if charOrd in Interpreter.ESCAPE_CODES:
2265 result.append(self.prefix + '\\' +
2266 Interpreter.ESCAPE_CODES[charOrd])
2267 else:
2268 result.append(self.prefix + '\\x%02x' % charOrd)
2269 elif char in more:
2270 result.append(self.prefix + '\\' + char)
2271 else:
2272 result.append(char)
2273 result = ''.join(result)
2274 self.invoke('afterEscape', result=result)
2275 return result
2276
2277 # Processing.
2278
2279 def wrap(self, callable, args):
2280 """Wrap around an application of a callable and handle errors.
2281 Return whether no error occurred."""
2282 try:
2283 callable(*args)
2284 self.reset()
2285 return True
2286 except KeyboardInterrupt:
2287 # Handle keyboard interrupts specially: we should always exit
2288 # from these.
2289 e = sys.exc_info()[1]
2290 self.fail(e, True)
2291 except Exception:
2292 # A standard exception (other than a keyboard interrupt).
2293 e = sys.exc_info()[1]
2294 self.fail(e)
2295 except:
2296 # If we get here, then either it's an exception not derived from
2297 # Exception or it's a string exception, so get the error type
2298 # from the sys module.
2299 e = sys.exc_info()[1]
2300 self.fail(e)
2301 # An error occurred if we leak through to here, so do cleanup.
2302 self.reset()
2303 return False
2304
2305 def interact(self):
2306 """Perform interaction."""
2307 self.invoke('atInteract')
2308 done = False
2309 while not done:
2310 result = self.wrap(self.file, (sys.stdin, '<interact>'))
2311 if self.options.get(EXIT_OPT, True):
2312 done = True
2313 else:
2314 if result:
2315 done = True
2316 else:
2317 self.reset()
2318
2319 def fail(self, error, fatal=False):
2320 """Handle an actual error that occurred."""
2321 if self.options.get(BUFFERED_OPT, False):
2322 try:
2323 self.output.abort()
2324 except AttributeError:
2325 # If the output file object doesn't have an abort method,
2326 # something got mismatched, but it's too late to do
2327 # anything about it now anyway, so just ignore it.
2328 pass
2329 meta = self.meta(error)
2330 self.handle(meta)
2331 if self.options.get(RAW_OPT, False):
2332 raise
2333 if fatal or self.options.get(EXIT_OPT, True):
2334 sys.exit(FAILURE_CODE)
2335
2336 def file(self, file, name='<file>', locals=None):
2337 """Parse the entire contents of a file-like object, line by line."""
2338 context = Context(name)
2339 self.contexts.push(context)
2340 self.invoke('beforeFile', name=name, file=file, locals=locals)
2341 scanner = Scanner(self.prefix)
2342 first = True
2343 done = False
2344 while not done:
2345 self.context().bump()
2346 line = file.readline()
2347 if first:
2348 if self.options.get(BANGPATH_OPT, True) and self.prefix:
2349 # Replace a bangpath at the beginning of the first line
2350 # with an EmPy comment.
2351 if line.startswith(BANGPATH):
2352 line = self.prefix + '#' + line[2:]
2353 first = False
2354 if line:
2355 scanner.feed(line)
2356 else:
2357 done = True
2358 self.safe(scanner, done, locals)
2359 self.invoke('afterFile')
2360 self.contexts.pop()
2361
2362 def binary(self, file, name='<binary>', chunkSize=0, locals=None):
2363 """Parse the entire contents of a file-like object, in chunks."""
2364 if chunkSize <= 0:
2365 chunkSize = DEFAULT_CHUNK_SIZE
2366 context = Context(name, units='bytes')
2367 self.contexts.push(context)
2368 self.invoke('beforeBinary', name=name, file=file,
2369 chunkSize=chunkSize, locals=locals)
2370 scanner = Scanner(self.prefix)
2371 done = False
2372 while not done:
2373 chunk = file.read(chunkSize)
2374 if chunk:
2375 scanner.feed(chunk)
2376 else:
2377 done = True
2378 self.safe(scanner, done, locals)
2379 self.context().bump(len(chunk))
2380 self.invoke('afterBinary')
2381 self.contexts.pop()
2382
2383 def string(self, data, name='<string>', locals=None):
2384 """Parse a string."""
2385 context = Context(name)
2386 self.contexts.push(context)
2387 self.invoke('beforeString', name=name, string=data, locals=locals)
2388 context.bump()
2389 scanner = Scanner(self.prefix, data)
2390 self.safe(scanner, True, locals)
2391 self.invoke('afterString')
2392 self.contexts.pop()
2393
2394 def safe(self, scanner, final=False, locals=None):
2395 """Do a protected parse. Catch transient parse errors; if
2396 final is true, then make a final pass with a terminator,
2397 otherwise ignore the transient parse error (more data is
2398 pending)."""
2399 try:
2400 self.parse(scanner, locals)
2401 except TransientParseError:
2402 if final:
2403 # If the buffer doesn't end with a newline, try tacking on
2404 # a dummy terminator.
2405 buffer = scanner.rest()
2406 if buffer and buffer[-1] != '\n':
2407 scanner.feed(self.prefix + '\n')
2408 # A TransientParseError thrown from here is a real parse
2409 # error.
2410 self.parse(scanner, locals)
2411
2412 def parse(self, scanner, locals=None):
2413 """Parse and run as much from this scanner as possible."""
2414 self.invoke('atParse', scanner=scanner, locals=locals)
2415 while True:
2416 token = scanner.one()
2417 if token is None:
2418 break
2419 self.invoke('atToken', token=token)
2420 token.run(self, locals)
2421
2422 # Medium-level evaluation and execution.
2423
2424 def tokenize(self, name):
2425 """Take an lvalue string and return a name or a (possibly recursive)
2426 list of names."""
2427 result = []
2428 stack = [result]
2429 for garbage in self.ASSIGN_TOKEN_RE.split(name):
2430 garbage = garbage.strip()
2431 if garbage:
2432 raise ParseError("unexpected assignment token: '%s'" % garbage)
2433 tokens = self.ASSIGN_TOKEN_RE.findall(name)
2434 # While processing, put a None token at the start of any list in which
2435 # commas actually appear.
2436 for token in tokens:
2437 if token == '(':
2438 stack.append([])
2439 elif token == ')':
2440 top = stack.pop()
2441 if len(top) == 1:
2442 top = top[0] # no None token means that it's not a 1-tuple
2443 elif top[0] is None:
2444 del top[0] # remove the None token for real tuples
2445 stack[-1].append(top)
2446 elif token == ',':
2447 if len(stack[-1]) == 1:
2448 stack[-1].insert(0, None)
2449 else:
2450 stack[-1].append(token)
2451 # If it's a 1-tuple at the top level, turn it into a real subsequence.
2452 if result and result[0] is None:
2453 result = [result[1:]]
2454 if len(result) == 1:
2455 return result[0]
2456 else:
2457 return result
2458
2459 def significate(self, key, value=None, locals=None):
2460 """Declare a significator."""
2461 self.invoke('beforeSignificate', key=key, value=value, locals=locals)
2462 name = '__%s__' % key
2463 self.atomic(name, value, locals)
2464 self.invoke('afterSignificate')
2465
2466 def atomic(self, name, value, locals=None):
2467 """Do an atomic assignment."""
2468 self.invoke('beforeAtomic', name=name, value=value, locals=locals)
2469 if locals is None:
2470 self.globals[name] = value
2471 else:
2472 locals[name] = value
2473 self.invoke('afterAtomic')
2474
2475 def multi(self, names, values, locals=None):
2476 """Do a (potentially recursive) assignment."""
2477 self.invoke('beforeMulti', names=names, values=values, locals=locals)
2478 # No zip in 1.5, so we have to do it manually.
2479 i = 0
2480 try:
2481 values = tuple(values)
2482 except TypeError:
2483 raise TypeError("unpack non-sequence")
2484 if len(names) != len(values):
2485 raise ValueError("unpack tuple of wrong size")
2486 for i in range(len(names)):
2487 name = names[i]
2488 if isinstance(name, _str) or isinstance(name, _unicode):
2489 self.atomic(name, values[i], locals)
2490 else:
2491 self.multi(name, values[i], locals)
2492 self.invoke('afterMulti')
2493
2494 def assign(self, name, value, locals=None):
2495 """Do a potentially complex (including tuple unpacking) assignment."""
2496 left = self.tokenize(name)
2497 # The return value of tokenize can either be a string or a list of
2498 # (lists of) strings.
2499 if isinstance(left, _str) or isinstance(left, _unicode):
2500 self.atomic(left, value, locals)
2501 else:
2502 self.multi(left, value, locals)
2503
2504 def import_(self, name, locals=None):
2505 """Do an import."""
2506 self.invoke('beforeImport', name=name, locals=locals)
2507 self.execute('import %s' % name, locals)
2508 self.invoke('afterImport')
2509
2510 def clause(self, catch, locals=None):
2511 """Given the string representation of an except clause, turn it into
2512 a 2-tuple consisting of the class name, and either a variable name
2513 or None."""
2514 self.invoke('beforeClause', catch=catch, locals=locals)
2515 if catch is None:
2516 exceptionCode, variable = None, None
2517 elif catch.find(',') >= 0:
2518 exceptionCode, variable = catch.strip().split(',', 1)
2519 variable = variable.strip()
2520 else:
2521 exceptionCode, variable = catch.strip(), None
2522 if not exceptionCode:
2523 exception = Exception
2524 else:
2525 exception = self.evaluate(exceptionCode, locals)
2526 self.invoke('afterClause', exception=exception, variable=variable)
2527 return exception, variable
2528
2529 def serialize(self, expression, locals=None):
2530 """Do an expansion, involving evaluating an expression, then
2531 converting it to a string and writing that string to the
2532 output if the evaluation is not None."""
2533 self.invoke('beforeSerialize', expression=expression, locals=locals)
2534 result = self.evaluate(expression, locals)
2535 if result is not None:
2536 self.write(str(result))
2537 self.invoke('afterSerialize')
2538
2539 def defined(self, name, locals=None):
2540 """Return a Boolean indicating whether or not the name is
2541 defined either in the locals or the globals."""
2542 self.invoke('beforeDefined', name=name, locals=locals)
2543 if locals is not None:
2544 if name in locals:
2545 result = True
2546 else:
2547 result = False
2548 elif name in self.globals:
2549 result = True
2550 else:
2551 result = False
2552 self.invoke('afterDefined', result=result)
2553 return result
2554
2555 def literal(self, text):
2556 """Process a string literal."""
2557 self.invoke('beforeLiteral', text=text)
2558 self.serialize(text)
2559 self.invoke('afterLiteral')
2560
2561 # Low-level evaluation and execution.
2562
2563 def evaluate(self, expression, locals=None):
2564 """Evaluate an expression."""
2565 if expression in ('1', 'True'): return True
2566 if expression in ('0', 'False'): return False
2567 self.push()
2568 try:
2569 self.invoke('beforeEvaluate',
2570 expression=expression, locals=locals)
2571 if locals is not None:
2572 result = eval(expression, self.globals, locals)
2573 else:
2574 result = eval(expression, self.globals)
2575 self.invoke('afterEvaluate', result=result)
2576 return result
2577 finally:
2578 self.pop()
2579
2580 def execute(self, statements, locals=None):
2581 """Execute a statement."""
2582 # If there are any carriage returns (as opposed to linefeeds/newlines)
2583 # in the statements code, then remove them. Even on DOS/Windows
2584 # platforms,
2585 if statements.find('\r') >= 0:
2586 statements = statements.replace('\r', '')
2587 # If there are no newlines in the statements code, then strip any
2588 # leading or trailing whitespace.
2589 if statements.find('\n') < 0:
2590 statements = statements.strip()
2591 self.push()
2592 try:
2593 self.invoke('beforeExecute',
2594 statements=statements, locals=locals)
2595 _exec(statements, self.globals, locals)
2596 self.invoke('afterExecute')
2597 finally:
2598 self.pop()
2599
2600 def single(self, source, locals=None):
2601 """Execute an expression or statement, just as if it were
2602 entered into the Python interactive interpreter."""
2603 self.push()
2604 try:
2605 self.invoke('beforeSingle',
2606 source=source, locals=locals)
2607 code = compile(source, '<single>', 'single')
2608 _exec(code, self.globals, locals)
2609 self.invoke('afterSingle')
2610 finally:
2611 self.pop()
2612
2613 # Hooks.
2614
2615 def register(self, hook, prepend=False):
2616 """Register the provided hook."""
2617 hook.register(self)
2618 if self.hooksEnabled is None:
2619 # A special optimization so that hooks can be effectively
2620 # disabled until one is added or they are explicitly turned on.
2621 self.hooksEnabled = True
2622 if prepend:
2623 self.hooks.insert(0, hook)
2624 else:
2625 self.hooks.append(hook)
2626
2627 def deregister(self, hook):
2628 """Remove an already registered hook."""
2629 hook.deregister(self)
2630 self.hooks.remove(hook)
2631
2632 def invoke(self, _name, **keywords):
2633 """Invoke the hook(s) associated with the hook name, should they
2634 exist."""
2635 if self.hooksEnabled:
2636 for hook in self.hooks:
2637 hook.push()
2638 try:
2639 method = getattr(hook, _name)
2640 method(*(), **keywords)
2641 finally:
2642 hook.pop()
2643
2644 def finalize(self):
2645 """Execute any remaining final routines."""
2646 self.push()
2647 self.invoke('atFinalize')
2648 try:
2649 # Pop them off one at a time so they get executed in reverse
2650 # order and we remove them as they're executed in case something
2651 # bad happens.
2652 while self.finals:
2653 final = self.finals.pop()
2654 final()
2655 finally:
2656 self.pop()
2657
2658 # Error handling.
2659
2660 def meta(self, exc=None):
2661 """Construct a MetaError for the interpreter's current state."""
2662 return MetaError(self.contexts.clone(), exc)
2663
2664 def handle(self, meta):
2665 """Handle a MetaError."""
2666 first = True
2667 self.invoke('atHandle', meta=meta)
2668 for context in meta.contexts:
2669 if first:
2670 if meta.exc is not None:
2671 desc = "error: %s: %s" % (meta.exc.__class__, meta.exc)
2672 else:
2673 desc = "error"
2674 else:
2675 desc = "from this context"
2676 first = False
2677 sys.stderr.write('%s: %s\n' % (context, desc))
2678
2679 def installProxy(self):
2680 """Install a proxy if necessary."""
2681 # Unfortunately, there's no surefire way to make sure that installing
2682 # a sys.stdout proxy is idempotent, what with different interpreters
2683 # running from different modules. The best we can do here is to try
2684 # manipulating the proxy's test function ...
2685 try:
2686 sys.stdout._testProxy()
2687 except AttributeError:
2688 # ... if the current stdout object doesn't have one, then check
2689 # to see if we think _this_ particularly Interpreter class has
2690 # installed it before ...
2691 if Interpreter._wasProxyInstalled:
2692 # ... and if so, we have a proxy problem.
2693 raise Error("interpreter stdout proxy lost")
2694 else:
2695 # Otherwise, install the proxy and set the flag.
2696 sys.stdout = ProxyFile(sys.stdout)
2697 Interpreter._wasProxyInstalled = True
2698
2699 #
2700 # Pseudomodule routines.
2701 #
2702
2703 # Identification.
2704
2705 def identify(self):
2706 """Identify the topmost context with a 2-tuple of the name and
2707 line number."""
2708 return self.context().identify()
2709
2710 def atExit(self, callable):
2711 """Register a function to be called at exit."""
2712 self.finals.append(callable)
2713
2714 # Context manipulation.
2715
2716 def pushContext(self, name='<unnamed>', line=0):
2717 """Create a new context and push it."""
2718 self.contexts.push(Context(name, line))
2719
2720 def popContext(self):
2721 """Pop the top context."""
2722 self.contexts.pop()
2723
2724 def setContextName(self, name):
2725 """Set the name of the topmost context."""
2726 context = self.context()
2727 context.name = name
2728
2729 def setContextLine(self, line):
2730 """Set the name of the topmost context."""
2731 context = self.context()
2732 context.line = line
2733
2734 setName = setContextName # DEPRECATED
2735 setLine = setContextLine # DEPRECATED
2736
2737 # Globals manipulation.
2738
2739 def getGlobals(self):
2740 """Retrieve the globals."""
2741 return self.globals
2742
2743 def setGlobals(self, globals):
2744 """Set the globals to the specified dictionary."""
2745 self.globals = globals
2746 self.fix()
2747
2748 def updateGlobals(self, otherGlobals):
2749 """Merge another mapping object into this interpreter's globals."""
2750 self.update(otherGlobals)
2751
2752 def clearGlobals(self):
2753 """Clear out the globals with a brand new dictionary."""
2754 self.clear()
2755
2756 def saveGlobals(self, deep=True):
2757 """Save a copy of the globals off onto the history stack."""
2758 self.save(deep)
2759
2760 def restoreGlobals(self, destructive=True):
2761 """Restore the most recently saved copy of the globals."""
2762 self.restore(destructive)
2763
2764 # Hook support.
2765
2766 def areHooksEnabled(self):
2767 """Return whether or not hooks are presently enabled."""
2768 if self.hooksEnabled is None:
2769 return True
2770 else:
2771 return self.hooksEnabled
2772
2773 def enableHooks(self):
2774 """Enable hooks."""
2775 self.hooksEnabled = True
2776
2777 def disableHooks(self):
2778 """Disable hooks."""
2779 self.hooksEnabled = False
2780
2781 def getHooks(self):
2782 """Get the current hooks."""
2783 return self.hooks[:]
2784
2785 def clearHooks(self):
2786 """Clear all hooks."""
2787 self.hooks = []
2788
2789 def addHook(self, hook, prepend=False):
2790 """Add a new hook; optionally insert it rather than appending it."""
2791 self.register(hook, prepend)
2792
2793 def removeHook(self, hook):
2794 """Remove a preexisting hook."""
2795 self.deregister(hook)
2796
2797 def invokeHook(self, _name, **keywords):
2798 """Manually invoke a hook."""
2799 self.invoke(*(_name,), **keywords)
2800
2801 # Callbacks.
2802
2803 def getCallback(self):
2804 """Get the callback registered with this interpreter, or None."""
2805 return self.callback
2806
2807 def registerCallback(self, callback):
2808 """Register a custom markup callback with this interpreter."""
2809 self.callback = callback
2810
2811 def deregisterCallback(self):
2812 """Remove any previously registered callback with this interpreter."""
2813 self.callback = None
2814
2815 def invokeCallback(self, contents):
2816 """Invoke the callback."""
2817 if self.callback is None:
2818 if self.options.get(CALLBACK_OPT, False):
2819 raise Error("custom markup invoked with no defined callback")
2820 else:
2821 self.callback(contents)
2822
2823 # Pseudomodule manipulation.
2824
2825 def flatten(self, keys=None):
2826 """Flatten the contents of the pseudo-module into the globals
2827 namespace."""
2828 if keys is None:
2829 keys = list(self.__dict__.keys()) + list(self.__class__.__dict__.keys())
2830 dict = {}
2831 for key in keys:
2832 # The pseudomodule is really a class instance, so we need to
2833 # fumble use getattr instead of simply fumbling through the
2834 # instance's __dict__.
2835 dict[key] = getattr(self, key)
2836 # Stomp everything into the globals namespace.
2837 self.globals.update(dict)
2838
2839 # Prefix.
2840
2841 def getPrefix(self):
2842 """Get the current prefix."""
2843 return self.prefix
2844
2845 def setPrefix(self, prefix):
2846 """Set the prefix."""
2847 self.prefix = prefix
2848
2849 # Diversions.
2850
2851 def stopDiverting(self):
2852 """Stop any diverting."""
2853 self.stream().revert()
2854
2855 def createDiversion(self, name):
2856 """Create a diversion (but do not divert to it) if it does not
2857 already exist."""
2858 self.stream().create(name)
2859
2860 def retrieveDiversion(self, name):
2861 """Retrieve the diversion object associated with the name."""
2862 return self.stream().retrieve(name)
2863
2864 def startDiversion(self, name):
2865 """Start diverting to the given diversion name."""
2866 self.stream().divert(name)
2867
2868 def playDiversion(self, name):
2869 """Play the given diversion and then purge it."""
2870 self.stream().undivert(name, True)
2871
2872 def replayDiversion(self, name):
2873 """Replay the diversion without purging it."""
2874 self.stream().undivert(name, False)
2875
2876 def purgeDiversion(self, name):
2877 """Eliminate the given diversion."""
2878 self.stream().purge(name)
2879
2880 def playAllDiversions(self):
2881 """Play all existing diversions and then purge them."""
2882 self.stream().undivertAll(True)
2883
2884 def replayAllDiversions(self):
2885 """Replay all existing diversions without purging them."""
2886 self.stream().undivertAll(False)
2887
2888 def purgeAllDiversions(self):
2889 """Purge all existing diversions."""
2890 self.stream().purgeAll()
2891
2892 def getCurrentDiversion(self):
2893 """Get the name of the current diversion."""
2894 return self.stream().currentDiversion
2895
2896 def getAllDiversions(self):
2897 """Get the names of all existing diversions."""
2898 names = sorted(self.stream().diversions.keys())
2899 return names
2900
2901 # Filter.
2902
2903 def resetFilter(self):
2904 """Reset the filter so that it does no filtering."""
2905 self.stream().install(None)
2906
2907 def nullFilter(self):
2908 """Install a filter that will consume all text."""
2909 self.stream().install(0)
2910
2911 def getFilter(self):
2912 """Get the current filter."""
2913 filter = self.stream().filter
2914 if filter is self.stream().file:
2915 return None
2916 else:
2917 return filter
2918
2919 def setFilter(self, shortcut):
2920 """Set the filter."""
2921 self.stream().install(shortcut)
2922
2923 def attachFilter(self, shortcut):
2924 """Attach a single filter to the end of the current filter chain."""
2925 self.stream().attach(shortcut)
2926
2927
2928 class Document:
2929
2930 """A representation of an individual EmPy document, as used by a
2931 processor."""
2932
2933 def __init__(self, ID, filename):
2934 self.ID = ID
2935 self.filename = filename
2936 self.significators = {}
2937
2938
2939 class Processor:
2940
2941 """An entity which is capable of processing a hierarchy of EmPy
2942 files and building a dictionary of document objects associated
2943 with them describing their significator contents."""
2944
2945 DEFAULT_EMPY_EXTENSIONS = ('.em',)
2946 SIGNIFICATOR_RE = re.compile(SIGNIFICATOR_RE_STRING)
2947
2948 def __init__(self, factory=Document):
2949 self.factory = factory
2950 self.documents = {}
2951
2952 def identifier(self, pathname, filename): return filename
2953
2954 def clear(self):
2955 self.documents = {}
2956
2957 def scan(self, basename, extensions=DEFAULT_EMPY_EXTENSIONS):
2958 if isinstance(extensions, _str):
2959 extensions = (extensions,)
2960 def _noCriteria(x):
2961 return True
2962 def _extensionsCriteria(pathname, extensions=extensions):
2963 if extensions:
2964 for extension in extensions:
2965 if pathname[-len(extension):] == extension:
2966 return True
2967 return False
2968 else:
2969 return True
2970 self.directory(basename, _noCriteria, _extensionsCriteria, None)
2971 self.postprocess()
2972
2973 def postprocess(self):
2974 pass
2975
2976 def directory(self, basename, dirCriteria, fileCriteria, depth=None):
2977 if depth is not None:
2978 if depth <= 0:
2979 return
2980 else:
2981 depth -= 1
2982 filenames = os.listdir(basename)
2983 for filename in filenames:
2984 pathname = os.path.join(basename, filename)
2985 if os.path.isdir(pathname):
2986 if dirCriteria(pathname):
2987 self.directory(pathname, dirCriteria, fileCriteria, depth)
2988 elif os.path.isfile(pathname):
2989 if fileCriteria(pathname):
2990 documentID = self.identifier(pathname, filename)
2991 document = self.factory(documentID, pathname)
2992 self.file(document, open(pathname))
2993 self.documents[documentID] = document
2994
2995 def file(self, document, file):
2996 while True:
2997 line = file.readline()
2998 if not line:
2999 break
3000 self.line(document, line)
3001
3002 def line(self, document, line):
3003 match = self.SIGNIFICATOR_RE.search(line)
3004 if match:
3005 key, valueS = match.groups()
3006 valueS = valueS.strip()
3007 if valueS:
3008 value = eval(valueS)
3009 else:
3010 value = None
3011 document.significators[key] = value
3012
3013
3014 def expand(_data, _globals=None,
3015 _argv=None, _prefix=DEFAULT_PREFIX, _pseudo=None, _options=None, \
3016 **_locals):
3017 """Do an atomic expansion of the given source data, creating and
3018 shutting down an interpreter dedicated to the task. The sys.stdout
3019 object is saved off and then replaced before this function
3020 returns."""
3021 if len(_locals) == 0:
3022 # If there were no keyword arguments specified, don't use a locals
3023 # dictionary at all.
3024 _locals = None
3025 output = NullFile()
3026 interpreter = Interpreter(output, argv=_argv, prefix=_prefix,
3027 pseudo=_pseudo, options=_options,
3028 globals=_globals)
3029 if interpreter.options.get(OVERRIDE_OPT, True):
3030 oldStdout = sys.stdout
3031 try:
3032 result = interpreter.expand(_data, _locals)
3033 finally:
3034 interpreter.shutdown()
3035 if _globals is not None:
3036 interpreter.unfix() # remove pseudomodule to prevent clashes
3037 if interpreter.options.get(OVERRIDE_OPT, True):
3038 sys.stdout = oldStdout
3039 return result
3040
3041 def environment(name, default=None):
3042 """Get data from the current environment. If the default is True
3043 or False, then presume that we're only interested in the existence
3044 or non-existence of the environment variable."""
3045 if name in os.environ:
3046 # Do the True/False test by value for future compatibility.
3047 if default == False or default == True:
3048 return True
3049 else:
3050 return os.environ[name]
3051 else:
3052 return default
3053
3054 def info(table):
3055 DEFAULT_LEFT = 28
3056 maxLeft = 0
3057 maxRight = 0
3058 for left, right in table:
3059 if len(left) > maxLeft:
3060 maxLeft = len(left)
3061 if len(right) > maxRight:
3062 maxRight = len(right)
3063 FORMAT = ' %%-%ds %%s\n' % max(maxLeft, DEFAULT_LEFT)
3064 for left, right in table:
3065 if right.find('\n') >= 0:
3066 for right in right.split('\n'):
3067 sys.stderr.write(FORMAT % (left, right))
3068 left = ''
3069 else:
3070 sys.stderr.write(FORMAT % (left, right))
3071
3072 def usage(verbose=True):
3073 """Print usage information."""
3074 programName = sys.argv[0]
3075 def warn(line=''):
3076 sys.stderr.write("%s\n" % line)
3077 warn("""\
3078 Usage: %s [options] [<filename, or '-' for stdin> [<argument>...]]
3079 Welcome to EmPy version %s.""" % (programName, __version__))
3080 warn()
3081 warn("Valid options:")
3082 info(OPTION_INFO)
3083 if verbose:
3084 warn()
3085 warn("The following markups are supported:")
3086 info(MARKUP_INFO)
3087 warn()
3088 warn("Valid escape sequences are:")
3089 info(ESCAPE_INFO)
3090 warn()
3091 warn("The %s pseudomodule contains the following attributes:" % DEFAULT_PSEUDOMODULE_NAME)
3092 info(PSEUDOMODULE_INFO)
3093 warn()
3094 warn("The following environment variables are recognized:")
3095 info(ENVIRONMENT_INFO)
3096 warn()
3097 warn(USAGE_NOTES)
3098 else:
3099 warn()
3100 warn("Type %s -H for more extensive help." % programName)
3101
3102 def invoke(args):
3103 """Run a standalone instance of an EmPy interpeter."""
3104 # Initialize the options.
3105 _output = None
3106 _options = {BUFFERED_OPT: environment(BUFFERED_ENV, False),
3107 RAW_OPT: environment(RAW_ENV, False),
3108 EXIT_OPT: True,
3109 FLATTEN_OPT: environment(FLATTEN_ENV, False),
3110 OVERRIDE_OPT: not environment(NO_OVERRIDE_ENV, False),
3111 CALLBACK_OPT: False}
3112 _preprocessing = []
3113 _prefix = environment(PREFIX_ENV, DEFAULT_PREFIX)
3114 _pseudo = environment(PSEUDO_ENV, None)
3115 _interactive = environment(INTERACTIVE_ENV, False)
3116 _extraArguments = environment(OPTIONS_ENV)
3117 _binary = -1 # negative for not, 0 for default size, positive for size
3118 _unicode = environment(UNICODE_ENV, False)
3119 _unicodeInputEncoding = environment(INPUT_ENCODING_ENV, None)
3120 _unicodeOutputEncoding = environment(OUTPUT_ENCODING_ENV, None)
3121 _unicodeInputErrors = environment(INPUT_ERRORS_ENV, None)
3122 _unicodeOutputErrors = environment(OUTPUT_ERRORS_ENV, None)
3123 _hooks = []
3124 _pauseAtEnd = False
3125 _relativePath = False
3126 if _extraArguments is not None:
3127 _extraArguments = _extraArguments.split()
3128 args = _extraArguments + args
3129 # Parse the arguments.
3130 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='])
3131 for option, argument in pairs:
3132 if option in ('-V', '--version'):
3133 sys.stderr.write("%s version %s\n" % (__program__, __version__))
3134 return
3135 elif option in ('-h', '--help'):
3136 usage(False)
3137 return
3138 elif option in ('-H', '--extended-help'):
3139 usage(True)
3140 return
3141 elif option in ('-v', '--verbose'):
3142 _hooks.append(VerboseHook())
3143 elif option in ('--null-hook',):
3144 _hooks.append(Hook())
3145 elif option in ('-k', '--suppress-errors'):
3146 _options[EXIT_OPT] = False
3147 _interactive = True # suppress errors implies interactive mode
3148 elif option in ('-m', '--module'):
3149 _pseudo = argument
3150 elif option in ('-f', '--flatten'):
3151 _options[FLATTEN_OPT] = True
3152 elif option in ('-p', '--prefix'):
3153 _prefix = argument
3154 elif option in ('--no-prefix',):
3155 _prefix = None
3156 elif option in ('-r', '--raw-errors'):
3157 _options[RAW_OPT] = True
3158 elif option in ('-i', '--interactive'):
3159 _interactive = True
3160 elif option in ('-n', '--no-override-stdout'):
3161 _options[OVERRIDE_OPT] = False
3162 elif option in ('-o', '--output'):
3163 _output = argument, 'w', _options[BUFFERED_OPT]
3164 elif option in ('-a', '--append'):
3165 _output = argument, 'a', _options[BUFFERED_OPT]
3166 elif option in ('-b', '--buffered-output'):
3167 _options[BUFFERED_OPT] = True
3168 elif option in ('-B',): # DEPRECATED
3169 _options[BUFFERED_OPT] = True
3170 elif option in ('--binary',):
3171 _binary = 0
3172 elif option in ('--chunk-size',):
3173 _binary = int(argument)
3174 elif option in ('-P', '--preprocess'):
3175 _preprocessing.append(('pre', argument))
3176 elif option in ('-I', '--import'):
3177 for module in argument.split(','):
3178 module = module.strip()
3179 _preprocessing.append(('import', module))
3180 elif option in ('-D', '--define'):
3181 _preprocessing.append(('define', argument))
3182 elif option in ('-E', '--execute'):
3183 _preprocessing.append(('exec', argument))
3184 elif option in ('-F', '--execute-file'):
3185 _preprocessing.append(('file', argument))
3186 elif option in ('-u', '--unicode'):
3187 _unicode = True
3188 elif option in ('--pause-at-end',):
3189 _pauseAtEnd = True
3190 elif option in ('--relative-path',):
3191 _relativePath = True
3192 elif option in ('--no-callback-error',):
3193 _options[CALLBACK_OPT] = True
3194 elif option in ('--no-bangpath-processing',):
3195 _options[BANGPATH_OPT] = False
3196 elif option in ('--unicode-encoding',):
3197 _unicodeInputEncoding = _unicodeOutputEncoding = argument
3198 elif option in ('--unicode-input-encoding',):
3199 _unicodeInputEncoding = argument
3200 elif option in ('--unicode-output-encoding',):
3201 _unicodeOutputEncoding = argument
3202 elif option in ('--unicode-errors',):
3203 _unicodeInputErrors = _unicodeOutputErrors = argument
3204 elif option in ('--unicode-input-errors',):
3205 _unicodeInputErrors = argument
3206 elif option in ('--unicode-output-errors',):
3207 _unicodeOutputErrors = argument
3208 # Set up the Unicode subsystem if required.
3209 if (_unicode or
3210 _unicodeInputEncoding or _unicodeOutputEncoding or
3211 _unicodeInputErrors or _unicodeOutputErrors):
3212 theSubsystem.initialize(_unicodeInputEncoding,
3213 _unicodeOutputEncoding,
3214 _unicodeInputErrors, _unicodeOutputErrors)
3215 # Now initialize the output file if something has already been selected.
3216 if _output is not None:
3217 _output = AbstractFile(*_output)
3218 # Set up the main filename and the argument.
3219 if not remainder:
3220 remainder.append('-')
3221 filename, arguments = remainder[0], remainder[1:]
3222 # Set up the interpreter.
3223 if _options[BUFFERED_OPT] and _output is None:
3224 raise ValueError("-b only makes sense with -o or -a arguments")
3225 if _prefix == 'None':
3226 _prefix = None
3227 if (_prefix and isinstance(_prefix, _str) and len(_prefix) != 1):
3228 raise Error("prefix must be single-character string")
3229 interpreter = Interpreter(output=_output,
3230 argv=remainder,
3231 prefix=_prefix,
3232 pseudo=_pseudo,
3233 options=_options,
3234 hooks=_hooks)
3235 try:
3236 # Execute command-line statements.
3237 i = 0
3238 for which, thing in _preprocessing:
3239 if which == 'pre':
3240 command = interpreter.file
3241 target = theSubsystem.open(thing, 'r')
3242 name = thing
3243 elif which == 'define':
3244 command = interpreter.string
3245 if thing.find('=') >= 0:
3246 target = '%s{%s}' % (_prefix, thing)
3247 else:
3248 target = '%s{%s = None}' % (_prefix, thing)
3249 name = '<define:%d>' % i
3250 elif which == 'exec':
3251 command = interpreter.string
3252 target = '%s{%s}' % (_prefix, thing)
3253 name = '<exec:%d>' % i
3254 elif which == 'file':
3255 command = interpreter.string
3256 name = '<file:%d (%s)>' % (i, thing)
3257 target = '%s{exec(open("""%s""").read())}' % (_prefix, thing)
3258 elif which == 'import':
3259 command = interpreter.string
3260 name = '<import:%d>' % i
3261 target = '%s{import %s}' % (_prefix, thing)
3262 else:
3263 assert 0
3264 interpreter.wrap(command, (target, name))
3265 i += 1
3266 # Now process the primary file.
3267 interpreter.ready()
3268 if filename == '-':
3269 if not _interactive:
3270 name = '<stdin>'
3271 path = ''
3272 file = sys.stdin
3273 else:
3274 name, file = None, None
3275 else:
3276 name = filename
3277 file = theSubsystem.open(filename, 'r')
3278 path = os.path.split(filename)[0]
3279 if _relativePath:
3280 sys.path.insert(0, path)
3281 if file is not None:
3282 if _binary < 0:
3283 interpreter.wrap(interpreter.file, (file, name))
3284 else:
3285 chunkSize = _binary
3286 interpreter.wrap(interpreter.binary, (file, name, chunkSize))
3287 # If we're supposed to go interactive afterwards, do it.
3288 if _interactive:
3289 interpreter.interact()
3290 finally:
3291 interpreter.shutdown()
3292 # Finally, if we should pause at the end, do it.
3293 if _pauseAtEnd:
3294 try:
3295 _input()
3296 except EOFError:
3297 pass
3298
3299 def main():
3300 invoke(sys.argv[1:])
3301
3302 if __name__ == '__main__': main()
00 gtk-widgets-72.css: gtk-widgets.css.em
1 $(srcdir)/../em.py -p $$ -D scaling=\'72\' -D gtk=\'$(GTK3_VERSION)\' \
1 empy3 -p $$ -D scaling=\'72\' -D gtk=\'$(GTK3_VERSION)\' \
22 $(srcdir)/gtk-widgets.css.em > \
33 $(top_builddir)/gtk3/theme/3.20/gtk-widgets-72.css
44
55 gtk-widgets-100.css: gtk-widgets.css.em
6 $(srcdir)/../em.py -p $$ -D scaling=\'100\' -D gtk=\'$(GTK3_VERSION)\' \
6 empy3 -p $$ -D scaling=\'100\' -D gtk=\'$(GTK3_VERSION)\' \
77 $(srcdir)/gtk-widgets.css.em > \
88 $(top_builddir)/gtk3/theme/3.20/gtk-widgets-100.css
99
00 SUBDIRS = assets 3.20
11
22 gtk-widgets-72.css: gtk-widgets.css.em
3 $(srcdir)/em.py -p $$ -D scaling=\'72\' -D gtk=\'$(GTK3_VERSION)\' \
3 empy3 -p $$ -D scaling=\'72\' -D gtk=\'$(GTK3_VERSION)\' \
44 $(srcdir)/gtk-widgets.css.em > \
55 $(top_builddir)/gtk3/theme/gtk-widgets-72.css
66
77 gtk-widgets-100.css: gtk-widgets.css.em
8 $(srcdir)/em.py -p $$ -D scaling=\'100\' -D gtk=\'$(GTK3_VERSION)\' \
8 empy3 -p $$ -D scaling=\'100\' -D gtk=\'$(GTK3_VERSION)\' \
99 $(srcdir)/gtk-widgets.css.em > \
1010 $(top_builddir)/gtk3/theme/gtk-widgets-100.css
1111
1212 settings-72.ini: settings.ini.em
13 $(srcdir)/em.py -p $$ -D scaling=\'72\' $(srcdir)/settings.ini.em > \
13 empy3 -p $$ -D scaling=\'72\' $(srcdir)/settings.ini.em > \
1414 $(top_builddir)/gtk3/theme/settings-72.ini
1515
1616 settings-100.ini: settings.ini.em
17 $(srcdir)/em.py -p $$ -D scaling=\'100\' $(srcdir)/settings.ini.em > \
17 empy3 -p $$ -D scaling=\'100\' $(srcdir)/settings.ini.em > \
1818 $(top_builddir)/gtk3/theme/settings-100.ini
1919
2020 clean:
5858 rm -rf $(DESTDIR)$(datadir)/themes/sugar-72/gtk-3.0
5959 rm -rf $(DESTDIR)$(datadir)/themes/sugar-100/gtk-3.0
6060
61 EXTRA_DIST = gtk-widgets.css.em settings.ini.em em.py gtk.css
61 EXTRA_DIST = gtk-widgets.css.em settings.ini.em gtk.css
6262 CLEANFILES = $(GEN_FILES)
+0
-3303
gtk3/theme/em.py less more
0 #!/usr/bin/env python
1 #
2 # $Id: //projects/empy/em.py#146 $ $Date: 2017-02-12 $
3
4 """
5 A system for processing Python as markup embedded in text.
6 """
7
8
9 __program__ = 'empy'
10 __version__ = '3.3.3'
11 __url__ = 'http://www.alcyone.com/software/empy/'
12 __author__ = 'Erik Max Francis <max@alcyone.com>'
13 __copyright__ = 'Copyright (C) 2002-2017 Erik Max Francis'
14 __license__ = 'LGPL'
15
16
17 import copy
18 import getopt
19 import inspect
20 import os
21 import re
22 import sys
23 import types
24
25 # 2.x/3.0 compatbility
26 try:
27 from StringIO import StringIO
28 except ImportError:
29 from io import StringIO
30
31 try:
32 _unicode = unicode # bytes will be undefined in 3.x releases
33 _str = str
34 _unichr = unichr
35 _input = raw_input
36 def _exec(code, globals, locals=None):
37 if globals is None:
38 exec("""exec code""")
39 else:
40 if locals is None:
41 exec("""exec code in globals""")
42 else:
43 exec("""exec code in globals, locals""")
44 except NameError:
45 _unicode = str
46 _str = bytes
47 _unichr = chr
48 _input = input
49 try:
50 _exec = __builtins__.__dict__['exec']
51 except AttributeError:
52 _exec = __builtins__['exec']
53
54 # Some basic defaults.
55 FAILURE_CODE = 1
56 DEFAULT_PREFIX = '@'
57 DEFAULT_PSEUDOMODULE_NAME = 'empy'
58 DEFAULT_SCRIPT_NAME = '?'
59 SIGNIFICATOR_RE_SUFFIX = r"%(\S+)\s*(.*)\s*$"
60 SIGNIFICATOR_RE_STRING = DEFAULT_PREFIX + SIGNIFICATOR_RE_SUFFIX
61 BANGPATH = '#!'
62 DEFAULT_CHUNK_SIZE = 8192
63 DEFAULT_ERRORS = 'strict'
64
65 # Character information.
66 IDENTIFIER_FIRST_CHARS = '_abcdefghijklmnopqrstuvwxyz' \
67 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
68 IDENTIFIER_CHARS = IDENTIFIER_FIRST_CHARS + '0123456789.'
69 ENDING_CHARS = {'(': ')', '[': ']', '{': '}'}
70
71 # Environment variable names.
72 OPTIONS_ENV = 'EMPY_OPTIONS'
73 PREFIX_ENV = 'EMPY_PREFIX'
74 PSEUDO_ENV = 'EMPY_PSEUDO'
75 FLATTEN_ENV = 'EMPY_FLATTEN'
76 RAW_ENV = 'EMPY_RAW_ERRORS'
77 INTERACTIVE_ENV = 'EMPY_INTERACTIVE'
78 BUFFERED_ENV = 'EMPY_BUFFERED_OUTPUT'
79 NO_OVERRIDE_ENV = 'EMPY_NO_OVERRIDE'
80 UNICODE_ENV = 'EMPY_UNICODE'
81 INPUT_ENCODING_ENV = 'EMPY_UNICODE_INPUT_ENCODING'
82 OUTPUT_ENCODING_ENV = 'EMPY_UNICODE_OUTPUT_ENCODING'
83 INPUT_ERRORS_ENV = 'EMPY_UNICODE_INPUT_ERRORS'
84 OUTPUT_ERRORS_ENV = 'EMPY_UNICODE_OUTPUT_ERRORS'
85
86 # Interpreter options.
87 BANGPATH_OPT = 'processBangpaths' # process bangpaths as comments?
88 BUFFERED_OPT = 'bufferedOutput' # fully buffered output?
89 RAW_OPT = 'rawErrors' # raw errors?
90 EXIT_OPT = 'exitOnError' # exit on error?
91 FLATTEN_OPT = 'flatten' # flatten pseudomodule namespace?
92 OVERRIDE_OPT = 'override' # override sys.stdout with proxy?
93 CALLBACK_OPT = 'noCallbackError' # is no custom callback an error?
94
95 # Usage info.
96 OPTION_INFO = [
97 ("-V --version", "Print version and exit"),
98 ("-h --help", "Print usage and exit"),
99 ("-H --extended-help", "Print extended usage and exit"),
100 ("-k --suppress-errors", "Do not exit on errors; go interactive"),
101 ("-p --prefix=<char>", "Change prefix to something other than @"),
102 (" --no-prefix", "Do not do any markup processing at all"),
103 ("-m --module=<name>", "Change the internal pseudomodule name"),
104 ("-f --flatten", "Flatten the members of pseudmodule to start"),
105 ("-r --raw-errors", "Show raw Python errors"),
106 ("-i --interactive", "Go into interactive mode after processing"),
107 ("-n --no-override-stdout", "Do not override sys.stdout with proxy"),
108 ("-o --output=<filename>", "Specify file for output as write"),
109 ("-a --append=<filename>", "Specify file for output as append"),
110 ("-b --buffered-output", "Fully buffer output including open"),
111 (" --binary", "Treat the file as a binary"),
112 (" --chunk-size=<chunk>", "Use this chunk size for reading binaries"),
113 ("-P --preprocess=<filename>", "Interpret EmPy file before main processing"),
114 ("-I --import=<modules>", "Import Python modules before processing"),
115 ("-D --define=<definition>", "Execute Python assignment statement"),
116 ("-E --execute=<statement>", "Execute Python statement before processing"),
117 ("-F --execute-file=<filename>", "Execute Python file before processing"),
118 (" --pause-at-end", "Prompt at the ending of processing"),
119 (" --relative-path", "Add path of EmPy script to sys.path"),
120 (" --no-callback-error", "Custom markup without callback is error"),
121 (" --no-bangpath-processing", "Suppress bangpaths as comments"),
122 ("-u --unicode", "Enable Unicode subsystem (Python 2+ only)"),
123 (" --unicode-encoding=<e>", "Set both input and output encodings"),
124 (" --unicode-input-encoding=<e>", "Set input encoding"),
125 (" --unicode-output-encoding=<e>", "Set output encoding"),
126 (" --unicode-errors=<E>", "Set both input and output error handler"),
127 (" --unicode-input-errors=<E>", "Set input error handler"),
128 (" --unicode-output-errors=<E>", "Set output error handler"),
129 ]
130
131 USAGE_NOTES = """\
132 Notes: Whitespace immediately inside parentheses of @(...) are
133 ignored. Whitespace immediately inside braces of @{...} are ignored,
134 unless ... spans multiple lines. Use @{ ... }@ to suppress newline
135 following expansion. Simple expressions ignore trailing dots; `@x.'
136 means `@(x).'. A #! at the start of a file is treated as a @#
137 comment."""
138
139 MARKUP_INFO = [
140 ("@# ... NL", "Comment; remove everything up to newline"),
141 ("@? NAME NL", "Set the current context name"),
142 ("@! INTEGER NL", "Set the current context line number"),
143 ("@ WHITESPACE", "Remove following whitespace; line continuation"),
144 ("@\\ ESCAPE_CODE", "A C-style escape sequence"),
145 ("@@", "Literal @; @ is escaped (duplicated prefix)"),
146 ("@), @], @}", "Literal close parenthesis, bracket, brace"),
147 ("@ STRING_LITERAL", "Replace with string literal contents"),
148 ("@( EXPRESSION )", "Evaluate expression and substitute with str"),
149 ("@( TEST [? THEN [! ELSE]] )", "If test is true, evaluate then, otherwise else"),
150 ("@( TRY $ CATCH )", "Expand try expression, or catch if it raises"),
151 ("@ SIMPLE_EXPRESSION", "Evaluate simple expression and substitute;\n"
152 "e.g., @x, @x.y, @f(a, b), @l[i], etc."),
153 ("@` EXPRESSION `", "Evaluate expression and substitute with repr"),
154 ("@: EXPRESSION : [DUMMY] :", "Evaluates to @:...:expansion:"),
155 ("@{ STATEMENTS }", "Statements are executed for side effects"),
156 ("@[ CONTROL ]", "Control markups: if E; elif E; for N in E;\n"
157 "while E; try; except E, N; finally; continue;\n"
158 "break; end X"),
159 ("@%% KEY WHITESPACE VALUE NL", "Significator form of __KEY__ = VALUE"),
160 ("@< CONTENTS >", "Custom markup; meaning provided by user"),
161 ]
162
163 ESCAPE_INFO = [
164 ("@\\0", "NUL, null"),
165 ("@\\a", "BEL, bell"),
166 ("@\\b", "BS, backspace"),
167 ("@\\dDDD", "three-digit decimal code DDD"),
168 ("@\\e", "ESC, escape"),
169 ("@\\f", "FF, form feed"),
170 ("@\\h", "DEL, delete"),
171 ("@\\n", "LF, linefeed, newline"),
172 ("@\\N{NAME}", "Unicode character named NAME"),
173 ("@\\oOOO", "three-digit octal code OOO"),
174 ("@\\qQQQQ", "four-digit quaternary code QQQQ"),
175 ("@\\r", "CR, carriage return"),
176 ("@\\s", "SP, space"),
177 ("@\\t", "HT, horizontal tab"),
178 ("@\\uHHHH", "16-bit hexadecimal Unicode HHHH"),
179 ("@\\UHHHHHHHH", "32-bit hexadecimal Unicode HHHHHHHH"),
180 ("@\\v", "VT, vertical tab"),
181 ("@\\xHH", "two-digit hexadecimal code HH"),
182 ("@\\z", "EOT, end of transmission"),
183 ]
184
185 PSEUDOMODULE_INFO = [
186 ("VERSION", "String representing EmPy version"),
187 ("SIGNIFICATOR_RE_STRING", "Regular expression matching significators"),
188 ("SIGNIFICATOR_RE_SUFFIX", "The above stub, lacking the prefix"),
189 ("interpreter", "Currently-executing interpreter instance"),
190 ("argv", "The EmPy script name and command line arguments"),
191 ("args", "The command line arguments only"),
192 ("identify()", "Identify top context as name, line"),
193 ("setContextName(name)", "Set the name of the current context"),
194 ("setContextLine(line)", "Set the line number of the current context"),
195 ("atExit(callable)", "Invoke no-argument function at shutdown"),
196 ("getGlobals()", "Retrieve this interpreter's globals"),
197 ("setGlobals(dict)", "Set this interpreter's globals"),
198 ("updateGlobals(dict)", "Merge dictionary into interpreter's globals"),
199 ("clearGlobals()", "Start globals over anew"),
200 ("saveGlobals([deep])", "Save a copy of the globals"),
201 ("restoreGlobals([pop])", "Restore the most recently saved globals"),
202 ("defined(name, [loc])", "Find if the name is defined"),
203 ("evaluate(expression, [loc])", "Evaluate the expression"),
204 ("serialize(expression, [loc])", "Evaluate and serialize the expression"),
205 ("execute(statements, [loc])", "Execute the statements"),
206 ("single(source, [loc])", "Execute the 'single' object"),
207 ("atomic(name, value, [loc])", "Perform an atomic assignment"),
208 ("assign(name, value, [loc])", "Perform an arbitrary assignment"),
209 ("significate(key, [value])", "Significate the given key, value pair"),
210 ("include(file, [loc])", "Include filename or file-like object"),
211 ("expand(string, [loc])", "Explicitly expand string and return"),
212 ("string(data, [name], [loc])", "Process string-like object"),
213 ("quote(string)", "Quote prefixes in provided string and return"),
214 ("flatten([keys])", "Flatten module contents into globals namespace"),
215 ("getPrefix()", "Get current prefix"),
216 ("setPrefix(char)", "Set new prefix"),
217 ("stopDiverting()", "Stop diverting; data sent directly to output"),
218 ("createDiversion(name)", "Create a diversion but do not divert to it"),
219 ("retrieveDiversion(name)", "Retrieve the actual named diversion object"),
220 ("startDiversion(name)", "Start diverting to given diversion"),
221 ("playDiversion(name)", "Recall diversion and then eliminate it"),
222 ("replayDiversion(name)", "Recall diversion but retain it"),
223 ("purgeDiversion(name)", "Erase diversion"),
224 ("playAllDiversions()", "Stop diverting and play all diversions in order"),
225 ("replayAllDiversions()", "Stop diverting and replay all diversions"),
226 ("purgeAllDiversions()", "Stop diverting and purge all diversions"),
227 ("getFilter()", "Get current filter"),
228 ("resetFilter()", "Reset filter; no filtering"),
229 ("nullFilter()", "Install null filter"),
230 ("setFilter(shortcut)", "Install new filter or filter chain"),
231 ("attachFilter(shortcut)", "Attach single filter to end of current chain"),
232 ("areHooksEnabled()", "Return whether or not hooks are enabled"),
233 ("enableHooks()", "Enable hooks (default)"),
234 ("disableHooks()", "Disable hook invocation"),
235 ("getHooks()", "Get all the hooks"),
236 ("clearHooks()", "Clear all hooks"),
237 ("addHook(hook, [i])", "Register the hook (optionally insert)"),
238 ("removeHook(hook)", "Remove an already-registered hook from name"),
239 ("invokeHook(name_, ...)", "Manually invoke hook"),
240 ("getCallback()", "Get interpreter callback"),
241 ("registerCallback(callback)", "Register callback with interpreter"),
242 ("deregisterCallback()", "Deregister callback from interpreter"),
243 ("invokeCallback(contents)", "Invoke the callback directly"),
244 ("Interpreter", "The interpreter class"),
245 ]
246
247 ENVIRONMENT_INFO = [
248 (OPTIONS_ENV, "Specified options will be included"),
249 (PREFIX_ENV, "Specify the default prefix: -p <value>"),
250 (PSEUDO_ENV, "Specify name of pseudomodule: -m <value>"),
251 (FLATTEN_ENV, "Flatten empy pseudomodule if defined: -f"),
252 (RAW_ENV, "Show raw errors if defined: -r"),
253 (INTERACTIVE_ENV, "Enter interactive mode if defined: -i"),
254 (BUFFERED_ENV, "Fully buffered output if defined: -b"),
255 (NO_OVERRIDE_ENV, "Do not override sys.stdout if defined: -n"),
256 (UNICODE_ENV, "Enable Unicode subsystem: -n"),
257 (INPUT_ENCODING_ENV, "Unicode input encoding"),
258 (OUTPUT_ENCODING_ENV, "Unicode output encoding"),
259 (INPUT_ERRORS_ENV, "Unicode input error handler"),
260 (OUTPUT_ERRORS_ENV, "Unicode output error handler"),
261 ]
262
263 class Error(Exception):
264 """The base class for all EmPy errors."""
265 pass
266
267 EmpyError = EmPyError = Error # DEPRECATED
268
269 class DiversionError(Error):
270 """An error related to diversions."""
271 pass
272
273 class FilterError(Error):
274 """An error related to filters."""
275 pass
276
277 class StackUnderflowError(Error):
278 """A stack underflow."""
279 pass
280
281 class SubsystemError(Error):
282 """An error associated with the Unicode subsystem."""
283 pass
284
285 class FlowError(Error):
286 """An exception related to control flow."""
287 pass
288
289 class ContinueFlow(FlowError):
290 """A continue control flow."""
291 pass
292
293 class BreakFlow(FlowError):
294 """A break control flow."""
295 pass
296
297 class ParseError(Error):
298 """A parse error occurred."""
299 pass
300
301 class TransientParseError(ParseError):
302 """A parse error occurred which may be resolved by feeding more data.
303 Such an error reaching the toplevel is an unexpected EOF error."""
304 pass
305
306
307 class MetaError(Exception):
308
309 """A wrapper around a real Python exception for including a copy of
310 the context."""
311
312 def __init__(self, contexts, exc):
313 Exception.__init__(self, exc)
314 self.contexts = contexts
315 self.exc = exc
316
317 def __str__(self):
318 backtrace = [str(x) for x in self.contexts]
319 return "%s: %s (%s)" % (self.exc.__class__, self.exc,
320 (', '.join(backtrace)))
321
322
323 class Subsystem:
324
325 """The subsystem class defers file creation so that it can create
326 Unicode-wrapped files if desired (and possible)."""
327
328 def __init__(self):
329 self.useUnicode = False
330 self.inputEncoding = None
331 self.outputEncoding = None
332 self.errors = None
333
334 def initialize(self, inputEncoding=None, outputEncoding=None,
335 inputErrors=None, outputErrors=None):
336 self.useUnicode = True
337 defaultEncoding = sys.getdefaultencoding()
338 if inputEncoding is None:
339 inputEncoding = defaultEncoding
340 self.inputEncoding = inputEncoding
341 if outputEncoding is None:
342 outputEncoding = defaultEncoding
343 self.outputEncoding = outputEncoding
344 if inputErrors is None:
345 inputErrors = DEFAULT_ERRORS
346 self.inputErrors = inputErrors
347 if outputErrors is None:
348 outputErrors = DEFAULT_ERRORS
349 self.outputErrors = outputErrors
350
351 def assertUnicode(self):
352 if not self.useUnicode:
353 raise SubsystemError("Unicode subsystem unavailable")
354
355 def open(self, name, mode=None):
356 if self.useUnicode:
357 return self.unicodeOpen(name, mode)
358 else:
359 return self.defaultOpen(name, mode)
360
361 def defaultOpen(self, name, mode=None):
362 if mode is None:
363 mode = 'r'
364 return open(name, mode)
365
366 def unicodeOpen(self, name, mode=None):
367 import codecs
368 if mode is None:
369 mode = 'rb'
370 if mode.find('w') >= 0 or mode.find('a') >= 0:
371 encoding = self.outputEncoding
372 errors = self.outputErrors
373 else:
374 encoding = self.inputEncoding
375 errors = self.inputErrors
376 return codecs.open(name, mode, encoding, errors)
377
378 theSubsystem = Subsystem()
379
380
381 class Stack:
382
383 """A simple stack that behaves as a sequence (with 0 being the top
384 of the stack, not the bottom)."""
385
386 def __init__(self, seq=None):
387 if seq is None:
388 seq = []
389 self.data = seq
390
391 def top(self):
392 """Access the top element on the stack."""
393 try:
394 return self.data[-1]
395 except IndexError:
396 raise StackUnderflowError("stack is empty for top")
397
398 def pop(self):
399 """Pop the top element off the stack and return it."""
400 try:
401 return self.data.pop()
402 except IndexError:
403 raise StackUnderflowError("stack is empty for pop")
404
405 def push(self, object):
406 """Push an element onto the top of the stack."""
407 self.data.append(object)
408
409 def filter(self, function):
410 """Filter the elements of the stack through the function."""
411 self.data = list(filter(function, self.data))
412
413 def purge(self):
414 """Purge the stack."""
415 self.data = []
416
417 def clone(self):
418 """Create a duplicate of this stack."""
419 return self.__class__(self.data[:])
420
421 def __nonzero__(self): return len(self.data) != 0 # 2.x
422 def __bool__(self): return len(self.data) != 0 # 3.x
423 def __len__(self): return len(self.data)
424 def __getitem__(self, index): return self.data[-(index + 1)]
425
426 def __repr__(self):
427 return ('<%s instance at 0x%x [%s]>' %
428 (self.__class__, id(self),
429 ', '.join(repr(x) for x in self.data)))
430
431
432 class AbstractFile:
433
434 """An abstracted file that, when buffered, will totally buffer the
435 file, including even the file open."""
436
437 def __init__(self, filename, mode='w', buffered=False):
438 # The calls below might throw, so start off by marking this
439 # file as "done." This way destruction of a not-completely-
440 # initialized AbstractFile will generate no further errors.
441 self.done = True
442 self.filename = filename
443 self.mode = mode
444 self.buffered = buffered
445 if buffered:
446 self.bufferFile = StringIO()
447 else:
448 self.bufferFile = theSubsystem.open(filename, mode)
449 # Okay, we got this far, so the AbstractFile is initialized.
450 # Flag it as "not done."
451 self.done = False
452
453 def __del__(self):
454 self.close()
455
456 def write(self, data):
457 self.bufferFile.write(data)
458
459 def writelines(self, data):
460 self.bufferFile.writelines(data)
461
462 def flush(self):
463 self.bufferFile.flush()
464
465 def close(self):
466 if not self.done:
467 self.commit()
468 self.done = True
469
470 def commit(self):
471 if self.buffered:
472 file = theSubsystem.open(self.filename, self.mode)
473 file.write(self.bufferFile.getvalue())
474 file.close()
475 else:
476 self.bufferFile.close()
477
478 def abort(self):
479 if self.buffered:
480 self.bufferFile = None
481 else:
482 self.bufferFile.close()
483 self.bufferFile = None
484 self.done = True
485
486
487 class Diversion:
488
489 """The representation of an active diversion. Diversions act as
490 (writable) file objects, and then can be recalled either as pure
491 strings or (readable) file objects."""
492
493 def __init__(self):
494 self.file = StringIO()
495
496 # These methods define the writable file-like interface for the
497 # diversion.
498
499 def write(self, data):
500 self.file.write(data)
501
502 def writelines(self, lines):
503 for line in lines:
504 self.write(line)
505
506 def flush(self):
507 self.file.flush()
508
509 def close(self):
510 self.file.close()
511
512 # These methods are specific to diversions.
513
514 def asString(self):
515 """Return the diversion as a string."""
516 return self.file.getvalue()
517
518 def asFile(self):
519 """Return the diversion as a file."""
520 return StringIO(self.file.getvalue())
521
522
523 class Stream:
524
525 """A wrapper around an (output) file object which supports
526 diversions and filtering."""
527
528 def __init__(self, file):
529 self.file = file
530 self.currentDiversion = None
531 self.diversions = {}
532 self.filter = file
533 self.done = False
534
535 def write(self, data):
536 if self.currentDiversion is None:
537 self.filter.write(data)
538 else:
539 self.diversions[self.currentDiversion].write(data)
540
541 def writelines(self, lines):
542 for line in lines:
543 self.write(line)
544
545 def flush(self):
546 self.filter.flush()
547
548 def close(self):
549 if not self.done:
550 self.undivertAll(True)
551 self.filter.close()
552 self.done = True
553
554 def shortcut(self, shortcut):
555 """Take a filter shortcut and translate it into a filter, returning
556 it. Sequences don't count here; these should be detected
557 independently."""
558 if shortcut == 0:
559 return NullFilter()
560 elif (isinstance(shortcut, types.FunctionType) or
561 inspect.ismethoddescriptor(shortcut) or
562 isinstance(shortcut, types.BuiltinFunctionType) or
563 isinstance(shortcut, types.BuiltinMethodType) or
564 isinstance(shortcut, types.LambdaType)):
565 return FunctionFilter(shortcut)
566 elif isinstance(shortcut, _str) or isinstance(shortcut, _unicode):
567 return StringFilter(filter)
568 elif isinstance(shortcut, dict):
569 raise NotImplementedError("mapping filters not yet supported")
570 else:
571 # Presume it's a plain old filter.
572 return shortcut
573
574 def last(self):
575 """Find the last filter in the current filter chain, or None if
576 there are no filters installed."""
577 if self.filter is None:
578 return None
579 thisFilter, lastFilter = self.filter, None
580 while thisFilter is not None and thisFilter is not self.file:
581 lastFilter = thisFilter
582 thisFilter = thisFilter.next()
583 return lastFilter
584
585 def install(self, shortcut=None):
586 """Install a new filter; None means no filter. Handle all the
587 special shortcuts for filters here."""
588 # Before starting, execute a flush.
589 self.filter.flush()
590 if shortcut is None or shortcut == [] or shortcut == ():
591 # Shortcuts for "no filter."
592 self.filter = self.file
593 else:
594 if isinstance(shortcut, list) or isinstance(shortcut, tuple):
595 shortcuts = list(shortcut)
596 else:
597 shortcuts = [shortcut]
598 # Run through the shortcut filter names, replacing them with
599 # full-fledged instances of Filter.
600 filters = []
601 for shortcut in shortcuts:
602 filters.append(self.shortcut(shortcut))
603 if len(filters) > 1:
604 # If there's more than one filter provided, chain them
605 # together.
606 lastFilter = None
607 for filter in filters:
608 if lastFilter is not None:
609 lastFilter.attach(filter)
610 lastFilter = filter
611 lastFilter.attach(self.file)
612 self.filter = filters[0]
613 else:
614 # If there's only one filter, assume that it's alone or it's
615 # part of a chain that has already been manually chained;
616 # just find the end.
617 filter = filters[0]
618 lastFilter = filter.last()
619 lastFilter.attach(self.file)
620 self.filter = filter
621
622 def attach(self, shortcut):
623 """Attached a solitary filter (no sequences allowed here) at the
624 end of the current filter chain."""
625 lastFilter = self.last()
626 if lastFilter is None:
627 # Just install it from scratch if there is no active filter.
628 self.install(shortcut)
629 else:
630 # Attach the last filter to this one, and this one to the file.
631 filter = self.shortcut(shortcut)
632 lastFilter.attach(filter)
633 filter.attach(self.file)
634
635 def revert(self):
636 """Reset any current diversions."""
637 self.currentDiversion = None
638
639 def create(self, name):
640 """Create a diversion if one does not already exist, but do not
641 divert to it yet."""
642 if name is None:
643 raise DiversionError("diversion name must be non-None")
644 if name not in self.diversions:
645 self.diversions[name] = Diversion()
646
647 def retrieve(self, name):
648 """Retrieve the given diversion."""
649 if name is None:
650 raise DiversionError("diversion name must be non-None")
651 if name in self.diversions:
652 return self.diversions[name]
653 else:
654 raise DiversionError("nonexistent diversion: %s" % name)
655
656 def divert(self, name):
657 """Start diverting."""
658 if name is None:
659 raise DiversionError("diversion name must be non-None")
660 self.create(name)
661 self.currentDiversion = name
662
663 def undivert(self, name, purgeAfterwards=False):
664 """Undivert a particular diversion."""
665 if name is None:
666 raise DiversionError("diversion name must be non-None")
667 if name in self.diversions:
668 diversion = self.diversions[name]
669 self.filter.write(diversion.asString())
670 if purgeAfterwards:
671 self.purge(name)
672 else:
673 raise DiversionError("nonexistent diversion: %s" % name)
674
675 def purge(self, name):
676 """Purge the specified diversion."""
677 if name is None:
678 raise DiversionError("diversion name must be non-None")
679 if name in self.diversions:
680 del self.diversions[name]
681 if self.currentDiversion == name:
682 self.currentDiversion = None
683
684 def undivertAll(self, purgeAfterwards=True):
685 """Undivert all pending diversions."""
686 if self.diversions:
687 self.revert() # revert before undiverting!
688 names = sorted(self.diversions.keys())
689 for name in names:
690 self.undivert(name)
691 if purgeAfterwards:
692 self.purge(name)
693
694 def purgeAll(self):
695 """Eliminate all existing diversions."""
696 if self.diversions:
697 self.diversions = {}
698 self.currentDiversion = None
699
700
701 class NullFile:
702
703 """A simple class that supports all the file-like object methods
704 but simply does nothing at all."""
705
706 def __init__(self): pass
707 def write(self, data): pass
708 def writelines(self, lines): pass
709 def flush(self): pass
710 def close(self): pass
711
712
713 class UncloseableFile:
714
715 """A simple class which wraps around a delegate file-like object
716 and lets everything through except close calls."""
717
718 def __init__(self, delegate):
719 self.delegate = delegate
720
721 def write(self, data):
722 self.delegate.write(data)
723
724 def writelines(self, lines):
725 self.delegate.writelines(data)
726
727 def flush(self):
728 self.delegate.flush()
729
730 def close(self):
731 """Eat this one."""
732 pass
733
734
735 class ProxyFile:
736
737 """The proxy file object that is intended to take the place of
738 sys.stdout. The proxy can manage a stack of file objects it is
739 writing to, and an underlying raw file object."""
740
741 def __init__(self, bottom):
742 self.stack = Stack()
743 self.bottom = bottom
744
745 def current(self):
746 """Get the current stream to write to."""
747 if self.stack:
748 return self.stack[-1][1]
749 else:
750 return self.bottom
751
752 def push(self, interpreter):
753 self.stack.push((interpreter, interpreter.stream()))
754
755 def pop(self, interpreter):
756 result = self.stack.pop()
757 assert interpreter is result[0]
758
759 def clear(self, interpreter):
760 self.stack.filter(lambda x, i=interpreter: x[0] is not i)
761
762 def write(self, data):
763 self.current().write(data)
764
765 def writelines(self, lines):
766 self.current().writelines(lines)
767
768 def flush(self):
769 self.current().flush()
770
771 def close(self):
772 """Close the current file. If the current file is the bottom, then
773 close it and dispose of it."""
774 current = self.current()
775 if current is self.bottom:
776 self.bottom = None
777 current.close()
778
779 def _testProxy(self): pass
780
781
782 class Filter:
783
784 """An abstract filter."""
785
786 def __init__(self):
787 if self.__class__ is Filter:
788 raise NotImplementedError
789 self.sink = None
790
791 def next(self):
792 """Return the next filter/file-like object in the sequence, or None."""
793 return self.sink
794
795 def __next__(self): return self.next()
796
797 def write(self, data):
798 """The standard write method; this must be overridden in subclasses."""
799 raise NotImplementedError
800
801 def writelines(self, lines):
802 """Standard writelines wrapper."""
803 for line in lines:
804 self.write(line)
805
806 def _flush(self):
807 """The _flush method should always flush the sink and should not
808 be overridden."""
809 self.sink.flush()
810
811 def flush(self):
812 """The flush method can be overridden."""
813 self._flush()
814
815 def close(self):
816 """Close the filter. Do an explicit flush first, then close the
817 sink."""
818 self.flush()
819 self.sink.close()
820
821 def attach(self, filter):
822 """Attach a filter to this one."""
823 if self.sink is not None:
824 # If it's already attached, detach it first.
825 self.detach()
826 self.sink = filter
827
828 def detach(self):
829 """Detach a filter from its sink."""
830 self.flush()
831 self._flush() # do a guaranteed flush to just to be safe
832 self.sink = None
833
834 def last(self):
835 """Find the last filter in this chain."""
836 this, last = self, self
837 while this is not None:
838 last = this
839 this = this.next()
840 return last
841
842 class NullFilter(Filter):
843
844 """A filter that never sends any output to its sink."""
845
846 def write(self, data): pass
847
848 class FunctionFilter(Filter):
849
850 """A filter that works simply by pumping its input through a
851 function which maps strings into strings."""
852
853 def __init__(self, function):
854 Filter.__init__(self)
855 self.function = function
856
857 def write(self, data):
858 self.sink.write(self.function(data))
859
860 class StringFilter(Filter):
861
862 """A filter that takes a translation string (256 characters) and
863 filters any incoming data through it."""
864
865 def __init__(self, table):
866 if not ((isinstance(table, _str) or isinstance(table, _unicode))
867 and len(table) == 256):
868 raise FilterError("table must be 256-character string")
869 Filter.__init__(self)
870 self.table = table
871
872 def write(self, data):
873 self.sink.write(data.translate(self.table))
874
875 class BufferedFilter(Filter):
876
877 """A buffered filter is one that doesn't modify the source data
878 sent to the sink, but instead holds it for a time. The standard
879 variety only sends the data along when it receives a flush
880 command."""
881
882 def __init__(self):
883 Filter.__init__(self)
884 self.buffer = ''
885
886 def write(self, data):
887 self.buffer += data
888
889 def flush(self):
890 if self.buffer:
891 self.sink.write(self.buffer)
892 self._flush()
893
894 class SizeBufferedFilter(BufferedFilter):
895
896 """A size-buffered filter only in fixed size chunks (excepting the
897 final chunk)."""
898
899 def __init__(self, bufferSize):
900 BufferedFilter.__init__(self)
901 self.bufferSize = bufferSize
902
903 def write(self, data):
904 BufferedFilter.write(self, data)
905 while len(self.buffer) > self.bufferSize:
906 chunk, self.buffer = self.buffer[:self.bufferSize], self.buffer[self.bufferSize:]
907 self.sink.write(chunk)
908
909 class LineBufferedFilter(BufferedFilter):
910
911 """A line-buffered filter only lets data through when it sees
912 whole lines."""
913
914 def __init__(self):
915 BufferedFilter.__init__(self)
916
917 def write(self, data):
918 BufferedFilter.write(self, data)
919 chunks = self.buffer.split('\n')
920 for chunk in chunks[:-1]:
921 self.sink.write(chunk + '\n')
922 self.buffer = chunks[-1]
923
924 class MaximallyBufferedFilter(BufferedFilter):
925
926 """A maximally-buffered filter only lets its data through on the final
927 close. It ignores flushes."""
928
929 def __init__(self):
930 BufferedFilter.__init__(self)
931
932 def flush(self): pass
933
934 def close(self):
935 if self.buffer:
936 BufferedFilter.flush(self)
937 self.sink.close()
938
939
940 class Context:
941
942 """An interpreter context, which encapsulates a name, an input
943 file object, and a parser object."""
944
945 DEFAULT_UNIT = 'lines'
946
947 def __init__(self, name, line=0, units=DEFAULT_UNIT):
948 self.name = name
949 self.line = line
950 self.units = units
951 self.pause = False
952
953 def bump(self, quantity=1):
954 if self.pause:
955 self.pause = False
956 else:
957 self.line += quantity
958
959 def identify(self):
960 return self.name, self.line
961
962 def __str__(self):
963 if self.units == self.DEFAULT_UNIT:
964 return "%s:%s" % (self.name, self.line)
965 else:
966 return "%s:%s[%s]" % (self.name, self.line, self.units)
967
968
969 class Hook:
970
971 """The base class for implementing hooks."""
972
973 def __init__(self):
974 self.interpreter = None
975
976 def register(self, interpreter):
977 self.interpreter = interpreter
978
979 def deregister(self, interpreter):
980 if interpreter is not self.interpreter:
981 raise Error("hook not associated with this interpreter")
982 self.interpreter = None
983
984 def push(self):
985 self.interpreter.push()
986
987 def pop(self):
988 self.interpreter.pop()
989
990 def null(self): pass
991
992 def atStartup(self): pass
993 def atReady(self): pass
994 def atFinalize(self): pass
995 def atShutdown(self): pass
996 def atParse(self, scanner, locals): pass
997 def atToken(self, token): pass
998 def atHandle(self, meta): pass
999 def atInteract(self): pass
1000
1001 def beforeInclude(self, name, file, locals): pass
1002 def afterInclude(self): pass
1003
1004 def beforeExpand(self, string, locals): pass
1005 def afterExpand(self, result): pass
1006
1007 def beforeFile(self, name, file, locals): pass
1008 def afterFile(self): pass
1009
1010 def beforeBinary(self, name, file, chunkSize, locals): pass
1011 def afterBinary(self): pass
1012
1013 def beforeString(self, name, string, locals): pass
1014 def afterString(self): pass
1015
1016 def beforeQuote(self, string): pass
1017 def afterQuote(self, result): pass
1018
1019 def beforeEscape(self, string, more): pass
1020 def afterEscape(self, result): pass
1021
1022 def beforeControl(self, type, rest, locals): pass
1023 def afterControl(self): pass
1024
1025 def beforeSignificate(self, key, value, locals): pass
1026 def afterSignificate(self): pass
1027
1028 def beforeAtomic(self, name, value, locals): pass
1029 def afterAtomic(self): pass
1030
1031 def beforeMulti(self, name, values, locals): pass
1032 def afterMulti(self): pass
1033
1034 def beforeImport(self, name, locals): pass
1035 def afterImport(self): pass
1036
1037 def beforeClause(self, catch, locals): pass
1038 def afterClause(self, exception, variable): pass
1039
1040 def beforeSerialize(self, expression, locals): pass
1041 def afterSerialize(self): pass
1042
1043 def beforeDefined(self, name, locals): pass
1044 def afterDefined(self, result): pass
1045
1046 def beforeLiteral(self, text): pass
1047 def afterLiteral(self): pass
1048
1049 def beforeEvaluate(self, expression, locals): pass
1050 def afterEvaluate(self, result): pass
1051
1052 def beforeExecute(self, statements, locals): pass
1053 def afterExecute(self): pass
1054
1055 def beforeSingle(self, source, locals): pass
1056 def afterSingle(self): pass
1057
1058 class VerboseHook(Hook):
1059
1060 """A verbose hook that reports all information received by the
1061 hook interface. This class dynamically scans the Hook base class
1062 to ensure that all hook methods are properly represented."""
1063
1064 EXEMPT_ATTRIBUTES = ['register', 'deregister', 'push', 'pop']
1065
1066 def __init__(self, output=sys.stderr):
1067 Hook.__init__(self)
1068 self.output = output
1069 self.indent = 0
1070
1071 class FakeMethod:
1072 """This is a proxy method-like object."""
1073 def __init__(self, hook, name):
1074 self.hook = hook
1075 self.name = name
1076
1077 def __call__(self, **keywords):
1078 self.hook.output.write("%s%s: %s\n" %
1079 (' ' * self.hook.indent,
1080 self.name, repr(keywords)))
1081
1082 for attribute in dir(Hook):
1083 if (attribute[:1] != '_' and
1084 attribute not in self.EXEMPT_ATTRIBUTES):
1085 self.__dict__[attribute] = FakeMethod(self, attribute)
1086
1087
1088 class Token:
1089
1090 """An element of expansion."""
1091
1092 def run(self, interpreter, locals):
1093 raise NotImplementedError
1094
1095 def string(self):
1096 raise NotImplementedError
1097
1098 def __str__(self): return self.string()
1099
1100 class NullToken(Token):
1101 """A chunk of data not containing markups."""
1102 def __init__(self, data):
1103 self.data = data
1104
1105 def run(self, interpreter, locals):
1106 interpreter.write(self.data)
1107
1108 def string(self):
1109 return self.data
1110
1111 class ExpansionToken(Token):
1112 """A token that involves an expansion."""
1113 def __init__(self, prefix, first):
1114 self.prefix = prefix
1115 self.first = first
1116
1117 def scan(self, scanner):
1118 pass
1119
1120 def run(self, interpreter, locals):
1121 pass
1122
1123 class WhitespaceToken(ExpansionToken):
1124 """A whitespace markup."""
1125 def string(self):
1126 return '%s%s' % (self.prefix, self.first)
1127
1128 class LiteralToken(ExpansionToken):
1129 """A literal markup."""
1130 def run(self, interpreter, locals):
1131 interpreter.write(self.first)
1132
1133 def string(self):
1134 return '%s%s' % (self.prefix, self.first)
1135
1136 class PrefixToken(ExpansionToken):
1137 """A prefix markup."""
1138 def run(self, interpreter, locals):
1139 interpreter.write(interpreter.prefix)
1140
1141 def string(self):
1142 return self.prefix * 2
1143
1144 class CommentToken(ExpansionToken):
1145 """A comment markup."""
1146 def scan(self, scanner):
1147 loc = scanner.find('\n')
1148 if loc >= 0:
1149 self.comment = scanner.chop(loc, 1)
1150 else:
1151 raise TransientParseError("comment expects newline")
1152
1153 def string(self):
1154 return '%s#%s\n' % (self.prefix, self.comment)
1155
1156 class ContextNameToken(ExpansionToken):
1157 """A context name change markup."""
1158 def scan(self, scanner):
1159 loc = scanner.find('\n')
1160 if loc >= 0:
1161 self.name = scanner.chop(loc, 1).strip()
1162 else:
1163 raise TransientParseError("context name expects newline")
1164
1165 def run(self, interpreter, locals):
1166 context = interpreter.context()
1167 context.name = self.name
1168
1169 class ContextLineToken(ExpansionToken):
1170 """A context line change markup."""
1171 def scan(self, scanner):
1172 loc = scanner.find('\n')
1173 if loc >= 0:
1174 try:
1175 self.line = int(scanner.chop(loc, 1))
1176 except ValueError:
1177 raise ParseError("context line requires integer")
1178 else:
1179 raise TransientParseError("context line expects newline")
1180
1181 def run(self, interpreter, locals):
1182 context = interpreter.context()
1183 context.line = self.line
1184 context.pause = True
1185
1186 class EscapeToken(ExpansionToken):
1187 """An escape markup."""
1188 def scan(self, scanner):
1189 try:
1190 code = scanner.chop(1)
1191 result = None
1192 if code in '()[]{}\'\"\\': # literals
1193 result = code
1194 elif code == '0': # NUL
1195 result = '\x00'
1196 elif code == 'a': # BEL
1197 result = '\x07'
1198 elif code == 'b': # BS
1199 result = '\x08'
1200 elif code == 'd': # decimal code
1201 decimalCode = scanner.chop(3)
1202 result = chr(int(decimalCode, 10))
1203 elif code == 'e': # ESC
1204 result = '\x1b'
1205 elif code == 'f': # FF
1206 result = '\x0c'
1207 elif code == 'h': # DEL
1208 result = '\x7f'
1209 elif code == 'n': # LF (newline)
1210 result = '\x0a'
1211 elif code == 'N': # Unicode character name
1212 theSubsystem.assertUnicode()
1213 import unicodedata
1214 if scanner.chop(1) != '{':
1215 raise ParseError("Unicode name escape should be \\N{...}")
1216 i = scanner.find('}')
1217 name = scanner.chop(i, 1)
1218 try:
1219 result = unicodedata.lookup(name)
1220 except KeyError:
1221 raise SubsystemError("unknown Unicode character name: %s" % name)
1222 elif code == 'o': # octal code
1223 octalCode = scanner.chop(3)
1224 result = chr(int(octalCode, 8))
1225 elif code == 'q': # quaternary code
1226 quaternaryCode = scanner.chop(4)
1227 result = chr(int(quaternaryCode, 4))
1228 elif code == 'r': # CR
1229 result = '\x0d'
1230 elif code in 's ': # SP
1231 result = ' '
1232 elif code == 't': # HT
1233 result = '\x09'
1234 elif code in 'u': # Unicode 16-bit hex literal
1235 theSubsystem.assertUnicode()
1236 hexCode = scanner.chop(4)
1237 result = _unichr(int(hexCode, 16))
1238 elif code in 'U': # Unicode 32-bit hex literal
1239 theSubsystem.assertUnicode()
1240 hexCode = scanner.chop(8)
1241 result = _unichr(int(hexCode, 16))
1242 elif code == 'v': # VT
1243 result = '\x0b'
1244 elif code == 'x': # hexadecimal code
1245 hexCode = scanner.chop(2)
1246 result = chr(int(hexCode, 16))
1247 elif code == 'z': # EOT
1248 result = '\x04'
1249 elif code == '^': # control character
1250 controlCode = scanner.chop(1).upper()
1251 if controlCode >= '@' and controlCode <= '`':
1252 result = chr(ord(controlCode) - ord('@'))
1253 elif controlCode == '?':
1254 result = '\x7f'
1255 else:
1256 raise ParseError("invalid escape control code")
1257 else:
1258 raise ParseError("unrecognized escape code")
1259 assert result is not None
1260 self.code = result
1261 except ValueError:
1262 raise ParseError("invalid numeric escape code")
1263
1264 def run(self, interpreter, locals):
1265 interpreter.write(self.code)
1266
1267 def string(self):
1268 return '%s\\x%02x' % (self.prefix, ord(self.code))
1269
1270 class SignificatorToken(ExpansionToken):
1271 """A significator markup."""
1272 def scan(self, scanner):
1273 loc = scanner.find('\n')
1274 if loc >= 0:
1275 line = scanner.chop(loc, 1)
1276 if not line:
1277 raise ParseError("significator must have nonblank key")
1278 if line[0] in ' \t\v\n':
1279 raise ParseError("no whitespace between % and key")
1280 # Work around a subtle CPython-Jython difference by stripping
1281 # the string before splitting it: 'a '.split(None, 1) has two
1282 # elements in Jython 2.1).
1283 fields = line.strip().split(None, 1)
1284 if len(fields) == 2 and fields[1] == '':
1285 fields.pop()
1286 self.key = fields[0]
1287 if len(fields) < 2:
1288 fields.append(None)
1289 self.key, self.valueCode = fields
1290 else:
1291 raise TransientParseError("significator expects newline")
1292
1293 def run(self, interpreter, locals):
1294 value = self.valueCode
1295 if value is not None:
1296 value = interpreter.evaluate(value.strip(), locals)
1297 interpreter.significate(self.key, value)
1298
1299 def string(self):
1300 if self.valueCode is None:
1301 return '%s%%%s\n' % (self.prefix, self.key)
1302 else:
1303 return '%s%%%s %s\n' % (self.prefix, self.key, self.valueCode)
1304
1305 class ExpressionToken(ExpansionToken):
1306 """An expression markup."""
1307 def scan(self, scanner):
1308 z = scanner.complex('(', ')', 0)
1309 try:
1310 q = scanner.next('$', 0, z, True)
1311 except ParseError:
1312 q = z
1313 try:
1314 i = scanner.next('?', 0, q, True)
1315 try:
1316 j = scanner.next('!', i, q, True)
1317 except ParseError:
1318 try:
1319 j = scanner.next(':', i, q, True) # DEPRECATED
1320 except ParseError:
1321 j = q
1322 except ParseError:
1323 i = j = q
1324 code = scanner.chop(z, 1)
1325 self.testCode = code[:i]
1326 self.thenCode = code[i + 1:j]
1327 self.elseCode = code[j + 1:q]
1328 self.exceptCode = code[q + 1:z]
1329
1330 def run(self, interpreter, locals):
1331 try:
1332 result = interpreter.evaluate(self.testCode, locals)
1333 if self.thenCode:
1334 if result:
1335 result = interpreter.evaluate(self.thenCode, locals)
1336 else:
1337 if self.elseCode:
1338 result = interpreter.evaluate(self.elseCode, locals)
1339 else:
1340 result = None
1341 except SyntaxError:
1342 # Don't catch syntax errors; let them through.
1343 raise
1344 except:
1345 if self.exceptCode:
1346 result = interpreter.evaluate(self.exceptCode, locals)
1347 else:
1348 raise
1349 if result is not None:
1350 interpreter.write(str(result))
1351
1352 def string(self):
1353 result = self.testCode
1354 if self.thenCode:
1355 result += '?' + self.thenCode
1356 if self.elseCode:
1357 result += '!' + self.elseCode
1358 if self.exceptCode:
1359 result += '$' + self.exceptCode
1360 return '%s(%s)' % (self.prefix, result)
1361
1362 class StringLiteralToken(ExpansionToken):
1363 """A string token markup."""
1364 def scan(self, scanner):
1365 scanner.retreat()
1366 assert scanner[0] == self.first
1367 i = scanner.quote()
1368 self.literal = scanner.chop(i)
1369
1370 def run(self, interpreter, locals):
1371 interpreter.literal(self.literal)
1372
1373 def string(self):
1374 return '%s%s' % (self.prefix, self.literal)
1375
1376 class SimpleExpressionToken(ExpansionToken):
1377 """A simple expression markup."""
1378 def scan(self, scanner):
1379 i = scanner.simple()
1380 self.code = self.first + scanner.chop(i)
1381
1382 def run(self, interpreter, locals):
1383 interpreter.serialize(self.code, locals)
1384
1385 def string(self):
1386 return '%s%s' % (self.prefix, self.code)
1387
1388 class ReprToken(ExpansionToken):
1389 """A repr markup."""
1390 def scan(self, scanner):
1391 i = scanner.next('`', 0)
1392 self.code = scanner.chop(i, 1)
1393
1394 def run(self, interpreter, locals):
1395 interpreter.write(repr(interpreter.evaluate(self.code, locals)))
1396
1397 def string(self):
1398 return '%s`%s`' % (self.prefix, self.code)
1399
1400 class InPlaceToken(ExpansionToken):
1401 """An in-place markup."""
1402 def scan(self, scanner):
1403 i = scanner.next(':', 0)
1404 j = scanner.next(':', i + 1)
1405 self.code = scanner.chop(i, j - i + 1)
1406
1407 def run(self, interpreter, locals):
1408 interpreter.write("%s:%s:" % (interpreter.prefix, self.code))
1409 try:
1410 interpreter.serialize(self.code, locals)
1411 finally:
1412 interpreter.write(":")
1413
1414 def string(self):
1415 return '%s:%s::' % (self.prefix, self.code)
1416
1417 class StatementToken(ExpansionToken):
1418 """A statement markup."""
1419 def scan(self, scanner):
1420 i = scanner.complex('{', '}', 0)
1421 self.code = scanner.chop(i, 1)
1422
1423 def run(self, interpreter, locals):
1424 interpreter.execute(self.code, locals)
1425
1426 def string(self):
1427 return '%s{%s}' % (self.prefix, self.code)
1428
1429 class CustomToken(ExpansionToken):
1430 """A custom markup."""
1431 def scan(self, scanner):
1432 i = scanner.complex('<', '>', 0)
1433 self.contents = scanner.chop(i, 1)
1434
1435 def run(self, interpreter, locals):
1436 interpreter.invokeCallback(self.contents)
1437
1438 def string(self):
1439 return '%s<%s>' % (self.prefix, self.contents)
1440
1441 class ControlToken(ExpansionToken):
1442
1443 """A control token."""
1444
1445 PRIMARY_TYPES = ['if', 'for', 'while', 'try', 'def']
1446 SECONDARY_TYPES = ['elif', 'else', 'except', 'finally']
1447 TERTIARY_TYPES = ['continue', 'break']
1448 GREEDY_TYPES = ['if', 'elif', 'for', 'while', 'def', 'end']
1449 END_TYPES = ['end']
1450
1451 IN_RE = re.compile(r"\bin\b")
1452
1453 def scan(self, scanner):
1454 scanner.acquire()
1455 i = scanner.complex('[', ']', 0)
1456 self.contents = scanner.chop(i, 1)
1457 fields = self.contents.strip().split(' ', 1)
1458 if len(fields) > 1:
1459 self.type, self.rest = fields
1460 else:
1461 self.type = fields[0]
1462 self.rest = None
1463 self.subtokens = []
1464 if self.type in self.GREEDY_TYPES and self.rest is None:
1465 raise ParseError("control '%s' needs arguments" % self.type)
1466 if self.type in self.PRIMARY_TYPES:
1467 self.subscan(scanner, self.type)
1468 self.kind = 'primary'
1469 elif self.type in self.SECONDARY_TYPES:
1470 self.kind = 'secondary'
1471 elif self.type in self.TERTIARY_TYPES:
1472 self.kind = 'tertiary'
1473 elif self.type in self.END_TYPES:
1474 self.kind = 'end'
1475 else:
1476 raise ParseError("unknown control markup: '%s'" % self.type)
1477 scanner.release()
1478
1479 def subscan(self, scanner, primary):
1480 """Do a subscan for contained tokens."""
1481 while True:
1482 token = scanner.one()
1483 if token is None:
1484 raise TransientParseError("control '%s' needs more tokens" % primary)
1485 if (isinstance(token, ControlToken) and
1486 token.type in self.END_TYPES):
1487 if token.rest != primary:
1488 raise ParseError("control must end with 'end %s'" % primary)
1489 break
1490 self.subtokens.append(token)
1491
1492 def build(self, allowed=None):
1493 """Process the list of subtokens and divide it into a list of
1494 2-tuples, consisting of the dividing tokens and the list of
1495 subtokens that follow them. If allowed is specified, it will
1496 represent the list of the only secondary markup types which
1497 are allowed."""
1498 if allowed is None:
1499 allowed = SECONDARY_TYPES
1500 result = []
1501 latest = []
1502 result.append((self, latest))
1503 for subtoken in self.subtokens:
1504 if (isinstance(subtoken, ControlToken) and
1505 subtoken.kind == 'secondary'):
1506 if subtoken.type not in allowed:
1507 raise ParseError("control unexpected secondary: '%s'" % subtoken.type)
1508 latest = []
1509 result.append((subtoken, latest))
1510 else:
1511 latest.append(subtoken)
1512 return result
1513
1514 def run(self, interpreter, locals):
1515 interpreter.invoke('beforeControl', type=self.type, rest=self.rest,
1516 locals=locals)
1517 if self.type == 'if':
1518 info = self.build(['elif', 'else'])
1519 elseTokens = None
1520 if info[-1][0].type == 'else':
1521 elseTokens = info.pop()[1]
1522 for secondary, subtokens in info:
1523 if secondary.type not in ('if', 'elif'):
1524 raise ParseError("control 'if' unexpected secondary: '%s'" % secondary.type)
1525 if interpreter.evaluate(secondary.rest, locals):
1526 self.subrun(subtokens, interpreter, locals)
1527 break
1528 else:
1529 if elseTokens:
1530 self.subrun(elseTokens, interpreter, locals)
1531 elif self.type == 'for':
1532 sides = self.IN_RE.split(self.rest, 1)
1533 if len(sides) != 2:
1534 raise ParseError("control expected 'for x in seq'")
1535 iterator, sequenceCode = sides
1536 info = self.build(['else'])
1537 elseTokens = None
1538 if info[-1][0].type == 'else':
1539 elseTokens = info.pop()[1]
1540 if len(info) != 1:
1541 raise ParseError("control 'for' expects at most one 'else'")
1542 sequence = interpreter.evaluate(sequenceCode, locals)
1543 for element in sequence:
1544 try:
1545 interpreter.assign(iterator, element, locals)
1546 self.subrun(info[0][1], interpreter, locals)
1547 except ContinueFlow:
1548 continue
1549 except BreakFlow:
1550 break
1551 else:
1552 if elseTokens:
1553 self.subrun(elseTokens, interpreter, locals)
1554 elif self.type == 'while':
1555 testCode = self.rest
1556 info = self.build(['else'])
1557 elseTokens = None
1558 if info[-1][0].type == 'else':
1559 elseTokens = info.pop()[1]
1560 if len(info) != 1:
1561 raise ParseError("control 'while' expects at most one 'else'")
1562 atLeastOnce = False
1563 while True:
1564 try:
1565 if not interpreter.evaluate(testCode, locals):
1566 break
1567 atLeastOnce = True
1568 self.subrun(info[0][1], interpreter, locals)
1569 except ContinueFlow:
1570 continue
1571 except BreakFlow:
1572 break
1573 if not atLeastOnce and elseTokens:
1574 self.subrun(elseTokens, interpreter, locals)
1575 elif self.type == 'try':
1576 info = self.build(['except', 'finally'])
1577 if len(info) == 1:
1578 raise ParseError("control 'try' needs 'except' or 'finally'")
1579 type = info[-1][0].type
1580 if type == 'except':
1581 for secondary, _tokens in info[1:]:
1582 if secondary.type != 'except':
1583 raise ParseError("control 'try' cannot have 'except' and 'finally'")
1584 else:
1585 assert type == 'finally'
1586 if len(info) != 2:
1587 raise ParseError("control 'try' can only have one 'finally'")
1588 if type == 'except':
1589 try:
1590 self.subrun(info[0][1], interpreter, locals)
1591 except FlowError:
1592 raise
1593 except Exception:
1594 e = sys.exc_info()[1]
1595 for secondary, tokens in info[1:]:
1596 exception, variable = interpreter.clause(secondary.rest)
1597 if variable is not None:
1598 interpreter.assign(variable, e)
1599 if isinstance(e, exception):
1600 self.subrun(tokens, interpreter, locals)
1601 break
1602 else:
1603 raise
1604 else:
1605 try:
1606 self.subrun(info[0][1], interpreter, locals)
1607 finally:
1608 self.subrun(info[1][1], interpreter, locals)
1609 elif self.type == 'continue':
1610 raise ContinueFlow("control 'continue' without 'for', 'while'")
1611 elif self.type == 'break':
1612 raise BreakFlow("control 'break' without 'for', 'while'")
1613 elif self.type == 'def':
1614 signature = self.rest
1615 definition = self.substring()
1616 code = ('def %s:\n'
1617 ' r"""%s"""\n'
1618 ' return %s.expand(r"""%s""", locals())\n' %
1619 (signature, definition, interpreter.pseudo, definition))
1620 interpreter.execute(code, locals)
1621 elif self.type == 'end':
1622 raise ParseError("control 'end' requires primary markup")
1623 else:
1624 raise ParseError("control '%s' cannot be at this level" % self.type)
1625 interpreter.invoke('afterControl')
1626
1627 def subrun(self, tokens, interpreter, locals):
1628 """Execute a sequence of tokens."""
1629 for token in tokens:
1630 token.run(interpreter, locals)
1631
1632 def substring(self):
1633 return ''.join(str(x) for x in self.subtokens)
1634
1635 def string(self):
1636 if self.kind == 'primary':
1637 return ('%s[%s]%s%s[end %s]' %
1638 (self.prefix, self.contents, self.substring(),
1639 self.prefix, self.type))
1640 else:
1641 return '%s[%s]' % (self.prefix, self.contents)
1642
1643
1644 class Scanner:
1645
1646 """A scanner holds a buffer for lookahead parsing and has the
1647 ability to scan for special symbols and indicators in that
1648 buffer."""
1649
1650 # This is the token mapping table that maps first characters to
1651 # token classes.
1652 TOKEN_MAP = [
1653 (None, PrefixToken),
1654 (' \t\v\r\n', WhitespaceToken),
1655 (')]}', LiteralToken),
1656 ('\\', EscapeToken),
1657 ('#', CommentToken),
1658 ('?', ContextNameToken),
1659 ('!', ContextLineToken),
1660 ('%', SignificatorToken),
1661 ('(', ExpressionToken),
1662 (IDENTIFIER_FIRST_CHARS, SimpleExpressionToken),
1663 ('\'\"', StringLiteralToken),
1664 ('`', ReprToken),
1665 (':', InPlaceToken),
1666 ('[', ControlToken),
1667 ('{', StatementToken),
1668 ('<', CustomToken),
1669 ]
1670
1671 def __init__(self, prefix, data=''):
1672 self.prefix = prefix
1673 self.pointer = 0
1674 self.buffer = data
1675 self.lock = 0
1676
1677 def __nonzero__(self): return self.pointer < len(self.buffer) # 2.x
1678 def __bool__(self): return self.pointer < len(self.buffer) # 3.x
1679 def __len__(self): return len(self.buffer) - self.pointer
1680
1681 def __getitem__(self, index):
1682 if isinstance(index, slice):
1683 assert index.step is None or index.step == 1
1684 return self.__getslice__(index.start, index.stop)
1685 else:
1686 return self.buffer[self.pointer + index]
1687
1688 def __getslice__(self, start, stop):
1689 if start is None:
1690 start = 0
1691 if stop is None:
1692 stop = len(self)
1693 if stop > len(self):
1694 stop = len(self)
1695 return self.buffer[self.pointer + start:self.pointer + stop]
1696
1697 def advance(self, count=1):
1698 """Advance the pointer count characters."""
1699 self.pointer += count
1700
1701 def retreat(self, count=1):
1702 self.pointer = self.pointer - count
1703 if self.pointer < 0:
1704 raise ParseError("can't retreat back over synced out chars")
1705
1706 def set(self, data):
1707 """Start the scanner digesting a new batch of data; start the pointer
1708 over from scratch."""
1709 self.pointer = 0
1710 self.buffer = data
1711
1712 def feed(self, data):
1713 """Feed some more data to the scanner."""
1714 self.buffer += data
1715
1716 def chop(self, count=None, slop=0):
1717 """Chop the first count + slop characters off the front, and return
1718 the first count. If count is not specified, then return
1719 everything."""
1720 if count is None:
1721 assert slop == 0
1722 count = len(self)
1723 if count > len(self):
1724 raise TransientParseError("not enough data to read")
1725 result = self[:count]
1726 self.advance(count + slop)
1727 return result
1728
1729 def acquire(self):
1730 """Lock the scanner so it doesn't destroy data on sync."""
1731 self.lock += 1
1732
1733 def release(self):
1734 """Unlock the scanner."""
1735 self.lock -= 1
1736
1737 def sync(self):
1738 """Sync up the buffer with the read head."""
1739 if self.lock == 0 and self.pointer != 0:
1740 self.buffer = self.buffer[self.pointer:]
1741 self.pointer = 0
1742
1743 def unsync(self):
1744 """Undo changes; reset the read head."""
1745 if self.pointer != 0:
1746 self.lock = 0
1747 self.pointer = 0
1748
1749 def rest(self):
1750 """Get the remainder of the buffer."""
1751 return self[:]
1752
1753 def read(self, i=0, count=1):
1754 """Read count chars starting from i; raise a transient error if
1755 there aren't enough characters remaining."""
1756 if len(self) < i + count:
1757 raise TransientParseError("need more data to read")
1758 else:
1759 return self[i:i + count]
1760
1761 def check(self, i, archetype=None):
1762 """Scan for the next single or triple quote, with the specified
1763 archetype. Return the found quote or None."""
1764 quote = None
1765 if self[i] in '\'\"':
1766 quote = self[i]
1767 if len(self) - i < 3:
1768 for j in range(i, len(self)):
1769 if self[i] == quote:
1770 return quote
1771 else:
1772 raise TransientParseError("need to scan for rest of quote")
1773 if self[i + 1] == self[i + 2] == quote:
1774 quote = quote * 3
1775 if quote is not None:
1776 if archetype is None:
1777 return quote
1778 else:
1779 if archetype == quote:
1780 return quote
1781 elif len(archetype) < len(quote) and archetype[0] == quote[0]:
1782 return archetype
1783 else:
1784 return None
1785 else:
1786 return None
1787
1788 def find(self, sub, start=0, end=None):
1789 """Find the next occurrence of the character, or return -1."""
1790 if end is not None:
1791 return self.rest().find(sub, start, end)
1792 else:
1793 return self.rest().find(sub, start)
1794
1795 def last(self, char, start=0, end=None):
1796 """Find the first character that is _not_ the specified character."""
1797 if end is None:
1798 end = len(self)
1799 i = start
1800 while i < end:
1801 if self[i] != char:
1802 return i
1803 i += 1
1804 else:
1805 raise TransientParseError("expecting other than %s" % char)
1806
1807 def next(self, target, start=0, end=None, mandatory=False):
1808 """Scan for the next occurrence of one of the characters in
1809 the target string; optionally, make the scan mandatory."""
1810 if mandatory:
1811 assert end is not None
1812 quote = None
1813 if end is None:
1814 end = len(self)
1815 i = start
1816 while i < end:
1817 newQuote = self.check(i, quote)
1818 if newQuote:
1819 if newQuote == quote:
1820 quote = None
1821 else:
1822 quote = newQuote
1823 i += len(newQuote)
1824 else:
1825 c = self[i]
1826 if quote:
1827 if c == '\\':
1828 i += 1
1829 else:
1830 if c in target:
1831 return i
1832 i += 1
1833 else:
1834 if mandatory:
1835 raise ParseError("expecting %s, not found" % target)
1836 else:
1837 raise TransientParseError("expecting ending character")
1838
1839 def quote(self, start=0, end=None, mandatory=False):
1840 """Scan for the end of the next quote."""
1841 assert self[start] in '\'\"'
1842 quote = self.check(start)
1843 if end is None:
1844 end = len(self)
1845 i = start + len(quote)
1846 while i < end:
1847 newQuote = self.check(i, quote)
1848 if newQuote:
1849 i += len(newQuote)
1850 if newQuote == quote:
1851 return i
1852 else:
1853 c = self[i]
1854 if c == '\\':
1855 i += 1
1856 i += 1
1857 else:
1858 if mandatory:
1859 raise ParseError("expecting end of string literal")
1860 else:
1861 raise TransientParseError("expecting end of string literal")
1862
1863 def nested(self, enter, exit, start=0, end=None):
1864 """Scan from i for an ending sequence, respecting entries and exits
1865 only."""
1866 depth = 0
1867 if end is None:
1868 end = len(self)
1869 i = start
1870 while i < end:
1871 c = self[i]
1872 if c == enter:
1873 depth += 1
1874 elif c == exit:
1875 depth -= 1
1876 if depth < 0:
1877 return i
1878 i += 1
1879 else:
1880 raise TransientParseError("expecting end of complex expression")
1881
1882 def complex(self, enter, exit, start=0, end=None, skip=None):
1883 """Scan from i for an ending sequence, respecting quotes,
1884 entries and exits."""
1885 quote = None
1886 depth = 0
1887 if end is None:
1888 end = len(self)
1889 last = None
1890 i = start
1891 while i < end:
1892 newQuote = self.check(i, quote)
1893 if newQuote:
1894 if newQuote == quote:
1895 quote = None
1896 else:
1897 quote = newQuote
1898 i += len(newQuote)
1899 else:
1900 c = self[i]
1901 if quote:
1902 if c == '\\':
1903 i += 1
1904 else:
1905 if skip is None or last != skip:
1906 if c == enter:
1907 depth += 1
1908 elif c == exit:
1909 depth -= 1
1910 if depth < 0:
1911 return i
1912 last = c
1913 i += 1
1914 else:
1915 raise TransientParseError("expecting end of complex expression")
1916
1917 def word(self, start=0):
1918 """Scan from i for a simple word."""
1919 length = len(self)
1920 i = start
1921 while i < length:
1922 if not self[i] in IDENTIFIER_CHARS:
1923 return i
1924 i += 1
1925 else:
1926 raise TransientParseError("expecting end of word")
1927
1928 def phrase(self, start=0):
1929 """Scan from i for a phrase (e.g., 'word', 'f(a, b, c)', 'a[i]', or
1930 combinations like 'x[i](a)'."""
1931 # Find the word.
1932 i = self.word(start)
1933 while i < len(self) and self[i] in '([{':
1934 enter = self[i]
1935 if enter == '{':
1936 raise ParseError("curly braces can't open simple expressions")
1937 exit = ENDING_CHARS[enter]
1938 i = self.complex(enter, exit, i + 1) + 1
1939 return i
1940
1941 def simple(self, start=0):
1942 """Scan from i for a simple expression, which consists of one
1943 more phrases separated by dots."""
1944 i = self.phrase(start)
1945 length = len(self)
1946 while i < length and self[i] == '.':
1947 i = self.phrase(i)
1948 # Make sure we don't end with a trailing dot.
1949 while i > 0 and self[i - 1] == '.':
1950 i -= 1
1951 return i
1952
1953 def one(self):
1954 """Parse and return one token, or None if the scanner is empty."""
1955 if not self:
1956 return None
1957 if not self.prefix:
1958 loc = -1
1959 else:
1960 loc = self.find(self.prefix)
1961 if loc < 0:
1962 # If there's no prefix in the buffer, then set the location to
1963 # the end so the whole thing gets processed.
1964 loc = len(self)
1965 if loc == 0:
1966 # If there's a prefix at the beginning of the buffer, process
1967 # an expansion.
1968 prefix = self.chop(1)
1969 assert prefix == self.prefix
1970 first = self.chop(1)
1971 if first == self.prefix:
1972 first = None
1973 for firsts, factory in self.TOKEN_MAP:
1974 if firsts is None:
1975 if first is None:
1976 break
1977 elif first in firsts:
1978 break
1979 else:
1980 raise ParseError("unknown markup: %s%s" % (self.prefix, first))
1981 token = factory(self.prefix, first)
1982 try:
1983 token.scan(self)
1984 except TransientParseError:
1985 # If a transient parse error occurs, reset the buffer pointer
1986 # so we can (conceivably) try again later.
1987 self.unsync()
1988 raise
1989 else:
1990 # Process everything up to loc as a null token.
1991 data = self.chop(loc)
1992 token = NullToken(data)
1993 self.sync()
1994 return token
1995
1996
1997 class Interpreter:
1998
1999 """An interpreter can process chunks of EmPy code."""
2000
2001 # Constants.
2002
2003 VERSION = __version__
2004 SIGNIFICATOR_RE_SUFFIX = SIGNIFICATOR_RE_SUFFIX
2005 SIGNIFICATOR_RE_STRING = None
2006
2007 # Types.
2008
2009 Interpreter = None # define this below to prevent a circular reference
2010 Hook = Hook # DEPRECATED
2011 Filter = Filter # DEPRECATED
2012 NullFilter = NullFilter # DEPRECATED
2013 FunctionFilter = FunctionFilter # DEPRECATED
2014 StringFilter = StringFilter # DEPRECATED
2015 BufferedFilter = BufferedFilter # DEPRECATED
2016 SizeBufferedFilter = SizeBufferedFilter # DEPRECATED
2017 LineBufferedFilter = LineBufferedFilter # DEPRECATED
2018 MaximallyBufferedFilter = MaximallyBufferedFilter # DEPRECATED
2019
2020 # Tables.
2021
2022 ESCAPE_CODES = {0x00: '0', 0x07: 'a', 0x08: 'b', 0x1b: 'e', 0x0c: 'f',
2023 0x7f: 'h', 0x0a: 'n', 0x0d: 'r', 0x09: 't', 0x0b: 'v',
2024 0x04: 'z'}
2025
2026 ASSIGN_TOKEN_RE = re.compile(r"[_a-zA-Z][_a-zA-Z0-9]*|\(|\)|,")
2027
2028 DEFAULT_OPTIONS = {BANGPATH_OPT: True,
2029 BUFFERED_OPT: False,
2030 RAW_OPT: False,
2031 EXIT_OPT: True,
2032 FLATTEN_OPT: False,
2033 OVERRIDE_OPT: True,
2034 CALLBACK_OPT: False}
2035
2036 _wasProxyInstalled = False # was a proxy installed?
2037
2038 # Construction, initialization, destruction.
2039
2040 def __init__(self, output=None, argv=None, prefix=DEFAULT_PREFIX,
2041 pseudo=None, options=None, globals=None, hooks=None):
2042 self.interpreter = self # DEPRECATED
2043 # Set up the stream.
2044 if output is None:
2045 output = UncloseableFile(sys.__stdout__)
2046 self.output = output
2047 self.prefix = prefix
2048 if pseudo is None:
2049 pseudo = DEFAULT_PSEUDOMODULE_NAME
2050 self.pseudo = pseudo
2051 if argv is None:
2052 argv = [DEFAULT_SCRIPT_NAME]
2053 self.argv = argv
2054 self.args = argv[1:]
2055 if options is None:
2056 options = {}
2057 self.options = options
2058 # Initialize any hooks.
2059 self.hooksEnabled = None # special sentinel meaning "false until added"
2060 self.hooks = []
2061 if hooks is None:
2062 hooks = []
2063 for hook in hooks:
2064 self.register(hook)
2065 # Initialize callback.
2066 self.callback = None
2067 # Finalizers.
2068 self.finals = []
2069 # The interpreter stacks.
2070 self.contexts = Stack()
2071 self.streams = Stack()
2072 # Now set up the globals.
2073 self.globals = globals
2074 self.fix()
2075 self.history = Stack()
2076 # Install a proxy stdout if one hasn't been already.
2077 self.installProxy()
2078 # Finally, reset the state of all the stacks.
2079 self.reset()
2080 # Okay, now flatten the namespaces if that option has been set.
2081 if self.options.get(FLATTEN_OPT, False):
2082 self.flatten()
2083 # Set up old pseudomodule attributes.
2084 if prefix is None:
2085 self.SIGNIFICATOR_RE_STRING = None
2086 else:
2087 self.SIGNIFICATOR_RE_STRING = prefix + self.SIGNIFICATOR_RE_SUFFIX
2088 self.Interpreter = self.__class__
2089 # Done. Now declare that we've started up.
2090 self.invoke('atStartup')
2091
2092 def __del__(self):
2093 self.shutdown()
2094
2095 def __repr__(self):
2096 return ('<%s pseudomodule/interpreter at 0x%x>' %
2097 (self.pseudo, id(self)))
2098
2099 def ready(self):
2100 """Declare the interpreter ready for normal operations."""
2101 self.invoke('atReady')
2102
2103 def fix(self):
2104 """Reset the globals, stamping in the pseudomodule."""
2105 if self.globals is None:
2106 self.globals = {}
2107 # Make sure that there is no collision between two interpreters'
2108 # globals.
2109 if self.pseudo in self.globals:
2110 if self.globals[self.pseudo] is not self:
2111 raise Error("interpreter globals collision")
2112 self.globals[self.pseudo] = self
2113
2114 def unfix(self):
2115 """Remove the pseudomodule (if present) from the globals."""
2116 UNWANTED_KEYS = [self.pseudo, '__builtins__']
2117 for unwantedKey in UNWANTED_KEYS:
2118 if unwantedKey in self.globals:
2119 del self.globals[unwantedKey]
2120
2121 def update(self, other):
2122 """Update the current globals dictionary with another dictionary."""
2123 self.globals.update(other)
2124 self.fix()
2125
2126 def clear(self):
2127 """Clear out the globals dictionary with a brand new one."""
2128 self.globals = {}
2129 self.fix()
2130
2131 def save(self, deep=True):
2132 if deep:
2133 copyMethod = copy.deepcopy
2134 else:
2135 copyMethod = copy.copy
2136 """Save a copy of the current globals on the history stack."""
2137 self.unfix()
2138 self.history.push(copyMethod(self.globals))
2139 self.fix()
2140
2141 def restore(self, destructive=True):
2142 """Restore the topmost historic globals."""
2143 if destructive:
2144 fetchMethod = self.history.pop
2145 else:
2146 fetchMethod = self.history.top
2147 self.unfix()
2148 self.globals = fetchMethod()
2149 self.fix()
2150
2151 def shutdown(self):
2152 """Declare this interpreting session over; close the stream file
2153 object. This method is idempotent."""
2154 if self.streams is not None:
2155 try:
2156 self.finalize()
2157 self.invoke('atShutdown')
2158 while self.streams:
2159 stream = self.streams.pop()
2160 stream.close()
2161 finally:
2162 self.streams = None
2163
2164 def ok(self):
2165 """Is the interpreter still active?"""
2166 return self.streams is not None
2167
2168 # Writeable file-like methods.
2169
2170 def write(self, data):
2171 self.stream().write(data)
2172
2173 def writelines(self, stuff):
2174 self.stream().writelines(stuff)
2175
2176 def flush(self):
2177 self.stream().flush()
2178
2179 def close(self):
2180 self.shutdown()
2181
2182 # Stack-related activity.
2183
2184 def context(self):
2185 return self.contexts.top()
2186
2187 def stream(self):
2188 return self.streams.top()
2189
2190 def reset(self):
2191 self.contexts.purge()
2192 self.streams.purge()
2193 self.streams.push(Stream(self.output))
2194 if self.options.get(OVERRIDE_OPT, True):
2195 sys.stdout.clear(self)
2196
2197 def push(self):
2198 if self.options.get(OVERRIDE_OPT, True):
2199 sys.stdout.push(self)
2200
2201 def pop(self):
2202 if self.options.get(OVERRIDE_OPT, True):
2203 sys.stdout.pop(self)
2204
2205 # Higher-level operations.
2206
2207 def include(self, fileOrFilename, locals=None):
2208 """Do an include pass on a file or filename."""
2209 if isinstance(fileOrFilename, _str):
2210 # Either it's a string representing a filename ...
2211 filename = fileOrFilename
2212 name = filename
2213 file = theSubsystem.open(filename, 'r')
2214 else:
2215 # ... or a file object.
2216 file = fileOrFilename
2217 name = "<%s>" % str(file.__class__)
2218 self.invoke('beforeInclude', name=name, file=file, locals=locals)
2219 self.file(file, name, locals)
2220 self.invoke('afterInclude')
2221
2222 def expand(self, data, locals=None):
2223 """Do an explicit expansion on a subordinate stream."""
2224 outFile = StringIO()
2225 stream = Stream(outFile)
2226 self.invoke('beforeExpand', string=data, locals=locals)
2227 self.streams.push(stream)
2228 try:
2229 self.string(data, '<expand>', locals)
2230 stream.flush()
2231 expansion = outFile.getvalue()
2232 self.invoke('afterExpand', result=expansion)
2233 return expansion
2234 finally:
2235 self.streams.pop()
2236
2237 def quote(self, data):
2238 """Quote the given string so that if it were expanded it would
2239 evaluate to the original."""
2240 self.invoke('beforeQuote', string=data)
2241 scanner = Scanner(self.prefix, data)
2242 result = []
2243 i = 0
2244 try:
2245 j = scanner.next(self.prefix, i)
2246 result.append(data[i:j])
2247 result.append(self.prefix * 2)
2248 i = j + 1
2249 except TransientParseError:
2250 pass
2251 result.append(data[i:])
2252 result = ''.join(result)
2253 self.invoke('afterQuote', result=result)
2254 return result
2255
2256 def escape(self, data, more=''):
2257 """Escape a string so that nonprintable characters are replaced
2258 with compatible EmPy expansions."""
2259 self.invoke('beforeEscape', string=data, more=more)
2260 result = []
2261 for char in data:
2262 if char < ' ' or char > '~':
2263 charOrd = ord(char)
2264 if charOrd in Interpreter.ESCAPE_CODES:
2265 result.append(self.prefix + '\\' +
2266 Interpreter.ESCAPE_CODES[charOrd])
2267 else:
2268 result.append(self.prefix + '\\x%02x' % charOrd)
2269 elif char in more:
2270 result.append(self.prefix + '\\' + char)
2271 else:
2272 result.append(char)
2273 result = ''.join(result)
2274 self.invoke('afterEscape', result=result)
2275 return result
2276
2277 # Processing.
2278
2279 def wrap(self, callable, args):
2280 """Wrap around an application of a callable and handle errors.
2281 Return whether no error occurred."""
2282 try:
2283 callable(*args)
2284 self.reset()
2285 return True
2286 except KeyboardInterrupt:
2287 # Handle keyboard interrupts specially: we should always exit
2288 # from these.
2289 e = sys.exc_info()[1]
2290 self.fail(e, True)
2291 except Exception:
2292 # A standard exception (other than a keyboard interrupt).
2293 e = sys.exc_info()[1]
2294 self.fail(e)
2295 except:
2296 # If we get here, then either it's an exception not derived from
2297 # Exception or it's a string exception, so get the error type
2298 # from the sys module.
2299 e = sys.exc_info()[1]
2300 self.fail(e)
2301 # An error occurred if we leak through to here, so do cleanup.
2302 self.reset()
2303 return False
2304
2305 def interact(self):
2306 """Perform interaction."""
2307 self.invoke('atInteract')
2308 done = False
2309 while not done:
2310 result = self.wrap(self.file, (sys.stdin, '<interact>'))
2311 if self.options.get(EXIT_OPT, True):
2312 done = True
2313 else:
2314 if result:
2315 done = True
2316 else:
2317 self.reset()
2318
2319 def fail(self, error, fatal=False):
2320 """Handle an actual error that occurred."""
2321 if self.options.get(BUFFERED_OPT, False):
2322 try:
2323 self.output.abort()
2324 except AttributeError:
2325 # If the output file object doesn't have an abort method,
2326 # something got mismatched, but it's too late to do
2327 # anything about it now anyway, so just ignore it.
2328 pass
2329 meta = self.meta(error)
2330 self.handle(meta)
2331 if self.options.get(RAW_OPT, False):
2332 raise
2333 if fatal or self.options.get(EXIT_OPT, True):
2334 sys.exit(FAILURE_CODE)
2335
2336 def file(self, file, name='<file>', locals=None):
2337 """Parse the entire contents of a file-like object, line by line."""
2338 context = Context(name)
2339 self.contexts.push(context)
2340 self.invoke('beforeFile', name=name, file=file, locals=locals)
2341 scanner = Scanner(self.prefix)
2342 first = True
2343 done = False
2344 while not done:
2345 self.context().bump()
2346 line = file.readline()
2347 if first:
2348 if self.options.get(BANGPATH_OPT, True) and self.prefix:
2349 # Replace a bangpath at the beginning of the first line
2350 # with an EmPy comment.
2351 if line.startswith(BANGPATH):
2352 line = self.prefix + '#' + line[2:]
2353 first = False
2354 if line:
2355 scanner.feed(line)
2356 else:
2357 done = True
2358 self.safe(scanner, done, locals)
2359 self.invoke('afterFile')
2360 self.contexts.pop()
2361
2362 def binary(self, file, name='<binary>', chunkSize=0, locals=None):
2363 """Parse the entire contents of a file-like object, in chunks."""
2364 if chunkSize <= 0:
2365 chunkSize = DEFAULT_CHUNK_SIZE
2366 context = Context(name, units='bytes')
2367 self.contexts.push(context)
2368 self.invoke('beforeBinary', name=name, file=file,
2369 chunkSize=chunkSize, locals=locals)
2370 scanner = Scanner(self.prefix)
2371 done = False
2372 while not done:
2373 chunk = file.read(chunkSize)
2374 if chunk:
2375 scanner.feed(chunk)
2376 else:
2377 done = True
2378 self.safe(scanner, done, locals)
2379 self.context().bump(len(chunk))
2380 self.invoke('afterBinary')
2381 self.contexts.pop()
2382
2383 def string(self, data, name='<string>', locals=None):
2384 """Parse a string."""
2385 context = Context(name)
2386 self.contexts.push(context)
2387 self.invoke('beforeString', name=name, string=data, locals=locals)
2388 context.bump()
2389 scanner = Scanner(self.prefix, data)
2390 self.safe(scanner, True, locals)
2391 self.invoke('afterString')
2392 self.contexts.pop()
2393
2394 def safe(self, scanner, final=False, locals=None):
2395 """Do a protected parse. Catch transient parse errors; if
2396 final is true, then make a final pass with a terminator,
2397 otherwise ignore the transient parse error (more data is
2398 pending)."""
2399 try:
2400 self.parse(scanner, locals)
2401 except TransientParseError:
2402 if final:
2403 # If the buffer doesn't end with a newline, try tacking on
2404 # a dummy terminator.
2405 buffer = scanner.rest()
2406 if buffer and buffer[-1] != '\n':
2407 scanner.feed(self.prefix + '\n')
2408 # A TransientParseError thrown from here is a real parse
2409 # error.
2410 self.parse(scanner, locals)
2411
2412 def parse(self, scanner, locals=None):
2413 """Parse and run as much from this scanner as possible."""
2414 self.invoke('atParse', scanner=scanner, locals=locals)
2415 while True:
2416 token = scanner.one()
2417 if token is None:
2418 break
2419 self.invoke('atToken', token=token)
2420 token.run(self, locals)
2421
2422 # Medium-level evaluation and execution.
2423
2424 def tokenize(self, name):
2425 """Take an lvalue string and return a name or a (possibly recursive)
2426 list of names."""
2427 result = []
2428 stack = [result]
2429 for garbage in self.ASSIGN_TOKEN_RE.split(name):
2430 garbage = garbage.strip()
2431 if garbage:
2432 raise ParseError("unexpected assignment token: '%s'" % garbage)
2433 tokens = self.ASSIGN_TOKEN_RE.findall(name)
2434 # While processing, put a None token at the start of any list in which
2435 # commas actually appear.
2436 for token in tokens:
2437 if token == '(':
2438 stack.append([])
2439 elif token == ')':
2440 top = stack.pop()
2441 if len(top) == 1:
2442 top = top[0] # no None token means that it's not a 1-tuple
2443 elif top[0] is None:
2444 del top[0] # remove the None token for real tuples
2445 stack[-1].append(top)
2446 elif token == ',':
2447 if len(stack[-1]) == 1:
2448 stack[-1].insert(0, None)
2449 else:
2450 stack[-1].append(token)
2451 # If it's a 1-tuple at the top level, turn it into a real subsequence.
2452 if result and result[0] is None:
2453 result = [result[1:]]
2454 if len(result) == 1:
2455 return result[0]
2456 else:
2457 return result
2458
2459 def significate(self, key, value=None, locals=None):
2460 """Declare a significator."""
2461 self.invoke('beforeSignificate', key=key, value=value, locals=locals)
2462 name = '__%s__' % key
2463 self.atomic(name, value, locals)
2464 self.invoke('afterSignificate')
2465
2466 def atomic(self, name, value, locals=None):
2467 """Do an atomic assignment."""
2468 self.invoke('beforeAtomic', name=name, value=value, locals=locals)
2469 if locals is None:
2470 self.globals[name] = value
2471 else:
2472 locals[name] = value
2473 self.invoke('afterAtomic')
2474
2475 def multi(self, names, values, locals=None):
2476 """Do a (potentially recursive) assignment."""
2477 self.invoke('beforeMulti', names=names, values=values, locals=locals)
2478 # No zip in 1.5, so we have to do it manually.
2479 i = 0
2480 try:
2481 values = tuple(values)
2482 except TypeError:
2483 raise TypeError("unpack non-sequence")
2484 if len(names) != len(values):
2485 raise ValueError("unpack tuple of wrong size")
2486 for i in range(len(names)):
2487 name = names[i]
2488 if isinstance(name, _str) or isinstance(name, _unicode):
2489 self.atomic(name, values[i], locals)
2490 else:
2491 self.multi(name, values[i], locals)
2492 self.invoke('afterMulti')
2493
2494 def assign(self, name, value, locals=None):
2495 """Do a potentially complex (including tuple unpacking) assignment."""
2496 left = self.tokenize(name)
2497 # The return value of tokenize can either be a string or a list of
2498 # (lists of) strings.
2499 if isinstance(left, _str) or isinstance(left, _unicode):
2500 self.atomic(left, value, locals)
2501 else:
2502 self.multi(left, value, locals)
2503
2504 def import_(self, name, locals=None):
2505 """Do an import."""
2506 self.invoke('beforeImport', name=name, locals=locals)
2507 self.execute('import %s' % name, locals)
2508 self.invoke('afterImport')
2509
2510 def clause(self, catch, locals=None):
2511 """Given the string representation of an except clause, turn it into
2512 a 2-tuple consisting of the class name, and either a variable name
2513 or None."""
2514 self.invoke('beforeClause', catch=catch, locals=locals)
2515 if catch is None:
2516 exceptionCode, variable = None, None
2517 elif catch.find(',') >= 0:
2518 exceptionCode, variable = catch.strip().split(',', 1)
2519 variable = variable.strip()
2520 else:
2521 exceptionCode, variable = catch.strip(), None
2522 if not exceptionCode:
2523 exception = Exception
2524 else:
2525 exception = self.evaluate(exceptionCode, locals)
2526 self.invoke('afterClause', exception=exception, variable=variable)
2527 return exception, variable
2528
2529 def serialize(self, expression, locals=None):
2530 """Do an expansion, involving evaluating an expression, then
2531 converting it to a string and writing that string to the
2532 output if the evaluation is not None."""
2533 self.invoke('beforeSerialize', expression=expression, locals=locals)
2534 result = self.evaluate(expression, locals)
2535 if result is not None:
2536 self.write(str(result))
2537 self.invoke('afterSerialize')
2538
2539 def defined(self, name, locals=None):
2540 """Return a Boolean indicating whether or not the name is
2541 defined either in the locals or the globals."""
2542 self.invoke('beforeDefined', name=name, locals=locals)
2543 if locals is not None:
2544 if name in locals:
2545 result = True
2546 else:
2547 result = False
2548 elif name in self.globals:
2549 result = True
2550 else:
2551 result = False
2552 self.invoke('afterDefined', result=result)
2553 return result
2554
2555 def literal(self, text):
2556 """Process a string literal."""
2557 self.invoke('beforeLiteral', text=text)
2558 self.serialize(text)
2559 self.invoke('afterLiteral')
2560
2561 # Low-level evaluation and execution.
2562
2563 def evaluate(self, expression, locals=None):
2564 """Evaluate an expression."""
2565 if expression in ('1', 'True'): return True
2566 if expression in ('0', 'False'): return False
2567 self.push()
2568 try:
2569 self.invoke('beforeEvaluate',
2570 expression=expression, locals=locals)
2571 if locals is not None:
2572 result = eval(expression, self.globals, locals)
2573 else:
2574 result = eval(expression, self.globals)
2575 self.invoke('afterEvaluate', result=result)
2576 return result
2577 finally:
2578 self.pop()
2579
2580 def execute(self, statements, locals=None):
2581 """Execute a statement."""
2582 # If there are any carriage returns (as opposed to linefeeds/newlines)
2583 # in the statements code, then remove them. Even on DOS/Windows
2584 # platforms,
2585 if statements.find('\r') >= 0:
2586 statements = statements.replace('\r', '')
2587 # If there are no newlines in the statements code, then strip any
2588 # leading or trailing whitespace.
2589 if statements.find('\n') < 0:
2590 statements = statements.strip()
2591 self.push()
2592 try:
2593 self.invoke('beforeExecute',
2594 statements=statements, locals=locals)
2595 _exec(statements, self.globals, locals)
2596 self.invoke('afterExecute')
2597 finally:
2598 self.pop()
2599
2600 def single(self, source, locals=None):
2601 """Execute an expression or statement, just as if it were
2602 entered into the Python interactive interpreter."""
2603 self.push()
2604 try:
2605 self.invoke('beforeSingle',
2606 source=source, locals=locals)
2607 code = compile(source, '<single>', 'single')
2608 _exec(code, self.globals, locals)
2609 self.invoke('afterSingle')
2610 finally:
2611 self.pop()
2612
2613 # Hooks.
2614
2615 def register(self, hook, prepend=False):
2616 """Register the provided hook."""
2617 hook.register(self)
2618 if self.hooksEnabled is None:
2619 # A special optimization so that hooks can be effectively
2620 # disabled until one is added or they are explicitly turned on.
2621 self.hooksEnabled = True
2622 if prepend:
2623 self.hooks.insert(0, hook)
2624 else:
2625 self.hooks.append(hook)
2626
2627 def deregister(self, hook):
2628 """Remove an already registered hook."""
2629 hook.deregister(self)
2630 self.hooks.remove(hook)
2631
2632 def invoke(self, _name, **keywords):
2633 """Invoke the hook(s) associated with the hook name, should they
2634 exist."""
2635 if self.hooksEnabled:
2636 for hook in self.hooks:
2637 hook.push()
2638 try:
2639 method = getattr(hook, _name)
2640 method(*(), **keywords)
2641 finally:
2642 hook.pop()
2643
2644 def finalize(self):
2645 """Execute any remaining final routines."""
2646 self.push()
2647 self.invoke('atFinalize')
2648 try:
2649 # Pop them off one at a time so they get executed in reverse
2650 # order and we remove them as they're executed in case something
2651 # bad happens.
2652 while self.finals:
2653 final = self.finals.pop()
2654 final()
2655 finally:
2656 self.pop()
2657
2658 # Error handling.
2659
2660 def meta(self, exc=None):
2661 """Construct a MetaError for the interpreter's current state."""
2662 return MetaError(self.contexts.clone(), exc)
2663
2664 def handle(self, meta):
2665 """Handle a MetaError."""
2666 first = True
2667 self.invoke('atHandle', meta=meta)
2668 for context in meta.contexts:
2669 if first:
2670 if meta.exc is not None:
2671 desc = "error: %s: %s" % (meta.exc.__class__, meta.exc)
2672 else:
2673 desc = "error"
2674 else:
2675 desc = "from this context"
2676 first = False
2677 sys.stderr.write('%s: %s\n' % (context, desc))
2678
2679 def installProxy(self):
2680 """Install a proxy if necessary."""
2681 # Unfortunately, there's no surefire way to make sure that installing
2682 # a sys.stdout proxy is idempotent, what with different interpreters
2683 # running from different modules. The best we can do here is to try
2684 # manipulating the proxy's test function ...
2685 try:
2686 sys.stdout._testProxy()
2687 except AttributeError:
2688 # ... if the current stdout object doesn't have one, then check
2689 # to see if we think _this_ particularly Interpreter class has
2690 # installed it before ...
2691 if Interpreter._wasProxyInstalled:
2692 # ... and if so, we have a proxy problem.
2693 raise Error("interpreter stdout proxy lost")
2694 else:
2695 # Otherwise, install the proxy and set the flag.
2696 sys.stdout = ProxyFile(sys.stdout)
2697 Interpreter._wasProxyInstalled = True
2698
2699 #
2700 # Pseudomodule routines.
2701 #
2702
2703 # Identification.
2704
2705 def identify(self):
2706 """Identify the topmost context with a 2-tuple of the name and
2707 line number."""
2708 return self.context().identify()
2709
2710 def atExit(self, callable):
2711 """Register a function to be called at exit."""
2712 self.finals.append(callable)
2713
2714 # Context manipulation.
2715
2716 def pushContext(self, name='<unnamed>', line=0):
2717 """Create a new context and push it."""
2718 self.contexts.push(Context(name, line))
2719
2720 def popContext(self):
2721 """Pop the top context."""
2722 self.contexts.pop()
2723
2724 def setContextName(self, name):
2725 """Set the name of the topmost context."""
2726 context = self.context()
2727 context.name = name
2728
2729 def setContextLine(self, line):
2730 """Set the name of the topmost context."""
2731 context = self.context()
2732 context.line = line
2733
2734 setName = setContextName # DEPRECATED
2735 setLine = setContextLine # DEPRECATED
2736
2737 # Globals manipulation.
2738
2739 def getGlobals(self):
2740 """Retrieve the globals."""
2741 return self.globals
2742
2743 def setGlobals(self, globals):
2744 """Set the globals to the specified dictionary."""
2745 self.globals = globals
2746 self.fix()
2747
2748 def updateGlobals(self, otherGlobals):
2749 """Merge another mapping object into this interpreter's globals."""
2750 self.update(otherGlobals)
2751
2752 def clearGlobals(self):
2753 """Clear out the globals with a brand new dictionary."""
2754 self.clear()
2755
2756 def saveGlobals(self, deep=True):
2757 """Save a copy of the globals off onto the history stack."""
2758 self.save(deep)
2759
2760 def restoreGlobals(self, destructive=True):
2761 """Restore the most recently saved copy of the globals."""
2762 self.restore(destructive)
2763
2764 # Hook support.
2765
2766 def areHooksEnabled(self):
2767 """Return whether or not hooks are presently enabled."""
2768 if self.hooksEnabled is None:
2769 return True
2770 else:
2771 return self.hooksEnabled
2772
2773 def enableHooks(self):
2774 """Enable hooks."""
2775 self.hooksEnabled = True
2776
2777 def disableHooks(self):
2778 """Disable hooks."""
2779 self.hooksEnabled = False
2780
2781 def getHooks(self):
2782 """Get the current hooks."""
2783 return self.hooks[:]
2784
2785 def clearHooks(self):
2786 """Clear all hooks."""
2787 self.hooks = []
2788
2789 def addHook(self, hook, prepend=False):
2790 """Add a new hook; optionally insert it rather than appending it."""
2791 self.register(hook, prepend)
2792
2793 def removeHook(self, hook):
2794 """Remove a preexisting hook."""
2795 self.deregister(hook)
2796
2797 def invokeHook(self, _name, **keywords):
2798 """Manually invoke a hook."""
2799 self.invoke(*(_name,), **keywords)
2800
2801 # Callbacks.
2802
2803 def getCallback(self):
2804 """Get the callback registered with this interpreter, or None."""
2805 return self.callback
2806
2807 def registerCallback(self, callback):
2808 """Register a custom markup callback with this interpreter."""
2809 self.callback = callback
2810
2811 def deregisterCallback(self):
2812 """Remove any previously registered callback with this interpreter."""
2813 self.callback = None
2814
2815 def invokeCallback(self, contents):
2816 """Invoke the callback."""
2817 if self.callback is None:
2818 if self.options.get(CALLBACK_OPT, False):
2819 raise Error("custom markup invoked with no defined callback")
2820 else:
2821 self.callback(contents)
2822
2823 # Pseudomodule manipulation.
2824
2825 def flatten(self, keys=None):
2826 """Flatten the contents of the pseudo-module into the globals
2827 namespace."""
2828 if keys is None:
2829 keys = list(self.__dict__.keys()) + list(self.__class__.__dict__.keys())
2830 dict = {}
2831 for key in keys:
2832 # The pseudomodule is really a class instance, so we need to
2833 # fumble use getattr instead of simply fumbling through the
2834 # instance's __dict__.
2835 dict[key] = getattr(self, key)
2836 # Stomp everything into the globals namespace.
2837 self.globals.update(dict)
2838
2839 # Prefix.
2840
2841 def getPrefix(self):
2842 """Get the current prefix."""
2843 return self.prefix
2844
2845 def setPrefix(self, prefix):
2846 """Set the prefix."""
2847 self.prefix = prefix
2848
2849 # Diversions.
2850
2851 def stopDiverting(self):
2852 """Stop any diverting."""
2853 self.stream().revert()
2854
2855 def createDiversion(self, name):
2856 """Create a diversion (but do not divert to it) if it does not
2857 already exist."""
2858 self.stream().create(name)
2859
2860 def retrieveDiversion(self, name):
2861 """Retrieve the diversion object associated with the name."""
2862 return self.stream().retrieve(name)
2863
2864 def startDiversion(self, name):
2865 """Start diverting to the given diversion name."""
2866 self.stream().divert(name)
2867
2868 def playDiversion(self, name):
2869 """Play the given diversion and then purge it."""
2870 self.stream().undivert(name, True)
2871
2872 def replayDiversion(self, name):
2873 """Replay the diversion without purging it."""
2874 self.stream().undivert(name, False)
2875
2876 def purgeDiversion(self, name):
2877 """Eliminate the given diversion."""
2878 self.stream().purge(name)
2879
2880 def playAllDiversions(self):
2881 """Play all existing diversions and then purge them."""
2882 self.stream().undivertAll(True)
2883
2884 def replayAllDiversions(self):
2885 """Replay all existing diversions without purging them."""
2886 self.stream().undivertAll(False)
2887
2888 def purgeAllDiversions(self):
2889 """Purge all existing diversions."""
2890 self.stream().purgeAll()
2891
2892 def getCurrentDiversion(self):
2893 """Get the name of the current diversion."""
2894 return self.stream().currentDiversion
2895
2896 def getAllDiversions(self):
2897 """Get the names of all existing diversions."""
2898 names = sorted(self.stream().diversions.keys())
2899 return names
2900
2901 # Filter.
2902
2903 def resetFilter(self):
2904 """Reset the filter so that it does no filtering."""
2905 self.stream().install(None)
2906
2907 def nullFilter(self):
2908 """Install a filter that will consume all text."""
2909 self.stream().install(0)
2910
2911 def getFilter(self):
2912 """Get the current filter."""
2913 filter = self.stream().filter
2914 if filter is self.stream().file:
2915 return None
2916 else:
2917 return filter
2918
2919 def setFilter(self, shortcut):
2920 """Set the filter."""
2921 self.stream().install(shortcut)
2922
2923 def attachFilter(self, shortcut):
2924 """Attach a single filter to the end of the current filter chain."""
2925 self.stream().attach(shortcut)
2926
2927
2928 class Document:
2929
2930 """A representation of an individual EmPy document, as used by a
2931 processor."""
2932
2933 def __init__(self, ID, filename):
2934 self.ID = ID
2935 self.filename = filename
2936 self.significators = {}
2937
2938
2939 class Processor:
2940
2941 """An entity which is capable of processing a hierarchy of EmPy
2942 files and building a dictionary of document objects associated
2943 with them describing their significator contents."""
2944
2945 DEFAULT_EMPY_EXTENSIONS = ('.em',)
2946 SIGNIFICATOR_RE = re.compile(SIGNIFICATOR_RE_STRING)
2947
2948 def __init__(self, factory=Document):
2949 self.factory = factory
2950 self.documents = {}
2951
2952 def identifier(self, pathname, filename): return filename
2953
2954 def clear(self):
2955 self.documents = {}
2956
2957 def scan(self, basename, extensions=DEFAULT_EMPY_EXTENSIONS):
2958 if isinstance(extensions, _str):
2959 extensions = (extensions,)
2960 def _noCriteria(x):
2961 return True
2962 def _extensionsCriteria(pathname, extensions=extensions):
2963 if extensions:
2964 for extension in extensions:
2965 if pathname[-len(extension):] == extension:
2966 return True
2967 return False
2968 else:
2969 return True
2970 self.directory(basename, _noCriteria, _extensionsCriteria, None)
2971 self.postprocess()
2972
2973 def postprocess(self):
2974 pass
2975
2976 def directory(self, basename, dirCriteria, fileCriteria, depth=None):
2977 if depth is not None:
2978 if depth <= 0:
2979 return
2980 else:
2981 depth -= 1
2982 filenames = os.listdir(basename)
2983 for filename in filenames:
2984 pathname = os.path.join(basename, filename)
2985 if os.path.isdir(pathname):
2986 if dirCriteria(pathname):
2987 self.directory(pathname, dirCriteria, fileCriteria, depth)
2988 elif os.path.isfile(pathname):
2989 if fileCriteria(pathname):
2990 documentID = self.identifier(pathname, filename)
2991 document = self.factory(documentID, pathname)
2992 self.file(document, open(pathname))
2993 self.documents[documentID] = document
2994
2995 def file(self, document, file):
2996 while True:
2997 line = file.readline()
2998 if not line:
2999 break
3000 self.line(document, line)
3001
3002 def line(self, document, line):
3003 match = self.SIGNIFICATOR_RE.search(line)
3004 if match:
3005 key, valueS = match.groups()
3006 valueS = valueS.strip()
3007 if valueS:
3008 value = eval(valueS)
3009 else:
3010 value = None
3011 document.significators[key] = value
3012
3013
3014 def expand(_data, _globals=None,
3015 _argv=None, _prefix=DEFAULT_PREFIX, _pseudo=None, _options=None, \
3016 **_locals):
3017 """Do an atomic expansion of the given source data, creating and
3018 shutting down an interpreter dedicated to the task. The sys.stdout
3019 object is saved off and then replaced before this function
3020 returns."""
3021 if len(_locals) == 0:
3022 # If there were no keyword arguments specified, don't use a locals
3023 # dictionary at all.
3024 _locals = None
3025 output = NullFile()
3026 interpreter = Interpreter(output, argv=_argv, prefix=_prefix,
3027 pseudo=_pseudo, options=_options,
3028 globals=_globals)
3029 if interpreter.options.get(OVERRIDE_OPT, True):
3030 oldStdout = sys.stdout
3031 try:
3032 result = interpreter.expand(_data, _locals)
3033 finally:
3034 interpreter.shutdown()
3035 if _globals is not None:
3036 interpreter.unfix() # remove pseudomodule to prevent clashes
3037 if interpreter.options.get(OVERRIDE_OPT, True):
3038 sys.stdout = oldStdout
3039 return result
3040
3041 def environment(name, default=None):
3042 """Get data from the current environment. If the default is True
3043 or False, then presume that we're only interested in the existence
3044 or non-existence of the environment variable."""
3045 if name in os.environ:
3046 # Do the True/False test by value for future compatibility.
3047 if default == False or default == True:
3048 return True
3049 else:
3050 return os.environ[name]
3051 else:
3052 return default
3053
3054 def info(table):
3055 DEFAULT_LEFT = 28
3056 maxLeft = 0
3057 maxRight = 0
3058 for left, right in table:
3059 if len(left) > maxLeft:
3060 maxLeft = len(left)
3061 if len(right) > maxRight:
3062 maxRight = len(right)
3063 FORMAT = ' %%-%ds %%s\n' % max(maxLeft, DEFAULT_LEFT)
3064 for left, right in table:
3065 if right.find('\n') >= 0:
3066 for right in right.split('\n'):
3067 sys.stderr.write(FORMAT % (left, right))
3068 left = ''
3069 else:
3070 sys.stderr.write(FORMAT % (left, right))
3071
3072 def usage(verbose=True):
3073 """Print usage information."""
3074 programName = sys.argv[0]
3075 def warn(line=''):
3076 sys.stderr.write("%s\n" % line)
3077 warn("""\
3078 Usage: %s [options] [<filename, or '-' for stdin> [<argument>...]]
3079 Welcome to EmPy version %s.""" % (programName, __version__))
3080 warn()
3081 warn("Valid options:")
3082 info(OPTION_INFO)
3083 if verbose:
3084 warn()
3085 warn("The following markups are supported:")
3086 info(MARKUP_INFO)
3087 warn()
3088 warn("Valid escape sequences are:")
3089 info(ESCAPE_INFO)
3090 warn()
3091 warn("The %s pseudomodule contains the following attributes:" % DEFAULT_PSEUDOMODULE_NAME)
3092 info(PSEUDOMODULE_INFO)
3093 warn()
3094 warn("The following environment variables are recognized:")
3095 info(ENVIRONMENT_INFO)
3096 warn()
3097 warn(USAGE_NOTES)
3098 else:
3099 warn()
3100 warn("Type %s -H for more extensive help." % programName)
3101
3102 def invoke(args):
3103 """Run a standalone instance of an EmPy interpeter."""
3104 # Initialize the options.
3105 _output = None
3106 _options = {BUFFERED_OPT: environment(BUFFERED_ENV, False),
3107 RAW_OPT: environment(RAW_ENV, False),
3108 EXIT_OPT: True,
3109 FLATTEN_OPT: environment(FLATTEN_ENV, False),
3110 OVERRIDE_OPT: not environment(NO_OVERRIDE_ENV, False),
3111 CALLBACK_OPT: False}
3112 _preprocessing = []
3113 _prefix = environment(PREFIX_ENV, DEFAULT_PREFIX)
3114 _pseudo = environment(PSEUDO_ENV, None)
3115 _interactive = environment(INTERACTIVE_ENV, False)
3116 _extraArguments = environment(OPTIONS_ENV)
3117 _binary = -1 # negative for not, 0 for default size, positive for size
3118 _unicode = environment(UNICODE_ENV, False)
3119 _unicodeInputEncoding = environment(INPUT_ENCODING_ENV, None)
3120 _unicodeOutputEncoding = environment(OUTPUT_ENCODING_ENV, None)
3121 _unicodeInputErrors = environment(INPUT_ERRORS_ENV, None)
3122 _unicodeOutputErrors = environment(OUTPUT_ERRORS_ENV, None)
3123 _hooks = []
3124 _pauseAtEnd = False
3125 _relativePath = False
3126 if _extraArguments is not None:
3127 _extraArguments = _extraArguments.split()
3128 args = _extraArguments + args
3129 # Parse the arguments.
3130 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='])
3131 for option, argument in pairs:
3132 if option in ('-V', '--version'):
3133 sys.stderr.write("%s version %s\n" % (__program__, __version__))
3134 return
3135 elif option in ('-h', '--help'):
3136 usage(False)
3137 return
3138 elif option in ('-H', '--extended-help'):
3139 usage(True)
3140 return
3141 elif option in ('-v', '--verbose'):
3142 _hooks.append(VerboseHook())
3143 elif option in ('--null-hook',):
3144 _hooks.append(Hook())
3145 elif option in ('-k', '--suppress-errors'):
3146 _options[EXIT_OPT] = False
3147 _interactive = True # suppress errors implies interactive mode
3148 elif option in ('-m', '--module'):
3149 _pseudo = argument
3150 elif option in ('-f', '--flatten'):
3151 _options[FLATTEN_OPT] = True
3152 elif option in ('-p', '--prefix'):
3153 _prefix = argument
3154 elif option in ('--no-prefix',):
3155 _prefix = None
3156 elif option in ('-r', '--raw-errors'):
3157 _options[RAW_OPT] = True
3158 elif option in ('-i', '--interactive'):
3159 _interactive = True
3160 elif option in ('-n', '--no-override-stdout'):
3161 _options[OVERRIDE_OPT] = False
3162 elif option in ('-o', '--output'):
3163 _output = argument, 'w', _options[BUFFERED_OPT]
3164 elif option in ('-a', '--append'):
3165 _output = argument, 'a', _options[BUFFERED_OPT]
3166 elif option in ('-b', '--buffered-output'):
3167 _options[BUFFERED_OPT] = True
3168 elif option in ('-B',): # DEPRECATED
3169 _options[BUFFERED_OPT] = True
3170 elif option in ('--binary',):
3171 _binary = 0
3172 elif option in ('--chunk-size',):
3173 _binary = int(argument)
3174 elif option in ('-P', '--preprocess'):
3175 _preprocessing.append(('pre', argument))
3176 elif option in ('-I', '--import'):
3177 for module in argument.split(','):
3178 module = module.strip()
3179 _preprocessing.append(('import', module))
3180 elif option in ('-D', '--define'):
3181 _preprocessing.append(('define', argument))
3182 elif option in ('-E', '--execute'):
3183 _preprocessing.append(('exec', argument))
3184 elif option in ('-F', '--execute-file'):
3185 _preprocessing.append(('file', argument))
3186 elif option in ('-u', '--unicode'):
3187 _unicode = True
3188 elif option in ('--pause-at-end',):
3189 _pauseAtEnd = True
3190 elif option in ('--relative-path',):
3191 _relativePath = True
3192 elif option in ('--no-callback-error',):
3193 _options[CALLBACK_OPT] = True
3194 elif option in ('--no-bangpath-processing',):
3195 _options[BANGPATH_OPT] = False
3196 elif option in ('--unicode-encoding',):
3197 _unicodeInputEncoding = _unicodeOutputEncoding = argument
3198 elif option in ('--unicode-input-encoding',):
3199 _unicodeInputEncoding = argument
3200 elif option in ('--unicode-output-encoding',):
3201 _unicodeOutputEncoding = argument
3202 elif option in ('--unicode-errors',):
3203 _unicodeInputErrors = _unicodeOutputErrors = argument
3204 elif option in ('--unicode-input-errors',):
3205 _unicodeInputErrors = argument
3206 elif option in ('--unicode-output-errors',):
3207 _unicodeOutputErrors = argument
3208 # Set up the Unicode subsystem if required.
3209 if (_unicode or
3210 _unicodeInputEncoding or _unicodeOutputEncoding or
3211 _unicodeInputErrors or _unicodeOutputErrors):
3212 theSubsystem.initialize(_unicodeInputEncoding,
3213 _unicodeOutputEncoding,
3214 _unicodeInputErrors, _unicodeOutputErrors)
3215 # Now initialize the output file if something has already been selected.
3216 if _output is not None:
3217 _output = AbstractFile(*_output)
3218 # Set up the main filename and the argument.
3219 if not remainder:
3220 remainder.append('-')
3221 filename, arguments = remainder[0], remainder[1:]
3222 # Set up the interpreter.
3223 if _options[BUFFERED_OPT] and _output is None:
3224 raise ValueError("-b only makes sense with -o or -a arguments")
3225 if _prefix == 'None':
3226 _prefix = None
3227 if (_prefix and isinstance(_prefix, _str) and len(_prefix) != 1):
3228 raise Error("prefix must be single-character string")
3229 interpreter = Interpreter(output=_output,
3230 argv=remainder,
3231 prefix=_prefix,
3232 pseudo=_pseudo,
3233 options=_options,
3234 hooks=_hooks)
3235 try:
3236 # Execute command-line statements.
3237 i = 0
3238 for which, thing in _preprocessing:
3239 if which == 'pre':
3240 command = interpreter.file
3241 target = theSubsystem.open(thing, 'r')
3242 name = thing
3243 elif which == 'define':
3244 command = interpreter.string
3245 if thing.find('=') >= 0:
3246 target = '%s{%s}' % (_prefix, thing)
3247 else:
3248 target = '%s{%s = None}' % (_prefix, thing)
3249 name = '<define:%d>' % i
3250 elif which == 'exec':
3251 command = interpreter.string
3252 target = '%s{%s}' % (_prefix, thing)
3253 name = '<exec:%d>' % i
3254 elif which == 'file':
3255 command = interpreter.string
3256 name = '<file:%d (%s)>' % (i, thing)
3257 target = '%s{exec(open("""%s""").read())}' % (_prefix, thing)
3258 elif which == 'import':
3259 command = interpreter.string
3260 name = '<import:%d>' % i
3261 target = '%s{import %s}' % (_prefix, thing)
3262 else:
3263 assert 0
3264 interpreter.wrap(command, (target, name))
3265 i += 1
3266 # Now process the primary file.
3267 interpreter.ready()
3268 if filename == '-':
3269 if not _interactive:
3270 name = '<stdin>'
3271 path = ''
3272 file = sys.stdin
3273 else:
3274 name, file = None, None
3275 else:
3276 name = filename
3277 file = theSubsystem.open(filename, 'r')
3278 path = os.path.split(filename)[0]
3279 if _relativePath:
3280 sys.path.insert(0, path)
3281 if file is not None:
3282 if _binary < 0:
3283 interpreter.wrap(interpreter.file, (file, name))
3284 else:
3285 chunkSize = _binary
3286 interpreter.wrap(interpreter.binary, (file, name, chunkSize))
3287 # If we're supposed to go interactive afterwards, do it.
3288 if _interactive:
3289 interpreter.interact()
3290 finally:
3291 interpreter.shutdown()
3292 # Finally, if we should pause at the end, do it.
3293 if _pauseAtEnd:
3294 try:
3295 _input()
3296 except EOFError:
3297 pass
3298
3299 def main():
3300 invoke(sys.argv[1:])
3301
3302 if __name__ == '__main__': main()
0 #!/usr/bin/env python2
0 #!/usr/bin/env python3
11
22 # Copyright (C) 2006, Red Hat, Inc.
33 #