diff options
Diffstat (limited to 'Demo')
-rw-r--r-- | Demo/parser/Makefile | 6 | ||||
-rw-r--r-- | Demo/parser/README | 17 | ||||
-rw-r--r-- | Demo/parser/example.py | 133 | ||||
-rw-r--r-- | Demo/parser/pprint.py | 97 | ||||
-rw-r--r-- | Demo/parser/simple.py | 1 |
5 files changed, 150 insertions, 104 deletions
diff --git a/Demo/parser/Makefile b/Demo/parser/Makefile index 648bf6e..d71e811 100644 --- a/Demo/parser/Makefile +++ b/Demo/parser/Makefile @@ -3,6 +3,10 @@ parser.dvi: parser.tex ../../Doc/libparser.tex # Use a new name for this; the included file uses 'clean' already.... clean-parser: - rm -f *.log *.aux *.dvi *.pyc + rm -f *.log *.aux *.dvi *.pyc *.ps + +dist: + (cd ../..; \ + tar cf - `cat Demo/parser/FILES` | gzip >parsermodule-1.4.tar.gz) include ../../Doc/Makefile diff --git a/Demo/parser/README b/Demo/parser/README index 03696c3..3d775aa 100644 --- a/Demo/parser/README +++ b/Demo/parser/README @@ -4,12 +4,29 @@ to the Python Library Reference for more information. Files: ------ + FILES -- list of files associated with the parser module. + + README -- this file. + example.py -- module that uses the `parser' module to extract information from the parse tree of Python source code. + docstring.py -- sample source file containing only a module docstring. + + simple.py -- sample source containing a "short form" definition. + source.py -- sample source code used to demonstrate ability to handle nested constructs easily using the functions and classes in example.py. + pprint.py -- function to pretty-print Python values. + + test_parser.py program to put the parser module through it's paces. + + parser.tex -- LaTex driver file for formatting the parser module + documentation separately from the library reference. + + Makefile -- `make' rule set to format the parser module manual. + Enjoy! diff --git a/Demo/parser/example.py b/Demo/parser/example.py index c428aff..363f5bb 100644 --- a/Demo/parser/example.py +++ b/Demo/parser/example.py @@ -1,6 +1,8 @@ """Simple code to extract class & function docstrings from a module. - +This code is used as an example in the library reference manual in the +section on using the parser module. Refer to the manual for a thorough +discussion of the operation of this code. """ import symbol @@ -23,12 +25,35 @@ def get_docs(fileName): return ModuleInfo(tup, basename) -class DefnInfo: +class SuiteInfoBase: _docstring = '' _name = '' - def __init__(self, tree): - self._name = tree[2][1] + def __init__(self, tree = None): + self._class_info = {} + self._function_info = {} + if tree: + self._extract_info(tree) + + def _extract_info(self, tree): + # extract docstring + if len(tree) == 2: + found, vars = match(DOCSTRING_STMT_PATTERN[1], tree[1]) + else: + found, vars = match(DOCSTRING_STMT_PATTERN, tree[3]) + if found: + self._docstring = eval(vars['docstring']) + # discover inner definitions + for node in tree[1:]: + found, vars = match(COMPOUND_STMT_PATTERN, node) + if found: + cstmt = vars['compound'] + if cstmt[0] == symbol.funcdef: + name = cstmt[2][1] + self._function_info[name] = FunctionInfo(cstmt) + elif cstmt[0] == symbol.classdef: + name = cstmt[2][1] + self._class_info[name] = ClassInfo(cstmt) def get_docstring(self): return self._docstring @@ -36,38 +61,21 @@ class DefnInfo: def get_name(self): return self._name -class SuiteInfoBase(DefnInfo): - def __init__(self): - self._class_info = {} - self._function_info = {} - def get_class_names(self): return self._class_info.keys() def get_class_info(self, name): return self._class_info[name] - def _extract_info(self, tree): - if len(tree) >= 4: - found, vars = match(DOCSTRING_STMT_PATTERN, tree[3]) - if found: - self._docstring = eval(vars['docstring']) - for node in tree[1:]: - if (node[0] == symbol.stmt - and node[1][0] == symbol.compound_stmt): - if node[1][1][0] == symbol.funcdef: - name = node[1][1][2][1] - self._function_info[name] = \ - FunctionInfo(node[1][1]) - elif node[1][1][0] == symbol.classdef: - name = node[1][1][2][1] - self._class_info[name] = ClassInfo(node[1][1]) - - -class SuiteInfo(SuiteInfoBase): - def __init__(self, tree): - SuiteInfoBase.__init__(self) - self._extract_info(tree) + def __getitem__(self, name): + try: + return self._class_info[name] + except KeyError: + return self._function_info[name] + + +class SuiteFuncInfo: + # Mixin class providing access to function names and info. def get_function_names(self): return self._function_info.keys() @@ -76,23 +84,16 @@ class SuiteInfo(SuiteInfoBase): return self._function_info[name] -class FunctionInfo(SuiteInfo): - def __init__(self, tree): - DefnInfo.__init__(self, tree) - suite = tree[-1] - if len(suite) >= 4: - found, vars = match(DOCSTRING_STMT_PATTERN, suite[3]) - if found: - self._docstring = eval(vars['docstring']) - SuiteInfoBase.__init__(self) - self._extract_info(suite) +class FunctionInfo(SuiteInfoBase, SuiteFuncInfo): + def __init__(self, tree = None): + self._name = tree[2][1] + SuiteInfoBase.__init__(self, tree and tree[-1] or None) class ClassInfo(SuiteInfoBase): - def __init__(self, tree): - SuiteInfoBase.__init__(self) - DefnInfo.__init__(self, tree) - self._extract_info(tree[-1]) + def __init__(self, tree = None): + self._name = tree[2][1] + SuiteInfoBase.__init__(self, tree and tree[-1] or None) def get_method_names(self): return self._function_info.keys() @@ -101,19 +102,40 @@ class ClassInfo(SuiteInfoBase): return self._function_info[name] -class ModuleInfo(SuiteInfo): - def __init__(self, tree, name="<string>"): +class ModuleInfo(SuiteInfoBase, SuiteFuncInfo): + def __init__(self, tree = None, name = "<string>"): self._name = name - SuiteInfo.__init__(self, tree) - found, vars = match(DOCSTRING_STMT_PATTERN, tree[1]) - if found: - self._docstring = vars["docstring"] + SuiteInfoBase.__init__(self, tree) + if tree: + found, vars = match(DOCSTRING_STMT_PATTERN, tree[1]) + if found: + self._docstring = vars["docstring"] from types import ListType, TupleType def match(pattern, data, vars=None): - """ + """Match `data' to `pattern', with variable extraction. + + pattern + Pattern to match against, possibly containing variables. + + data + Data to be checked and against which variables are extracted. + + vars + Dictionary of variables which have already been found. If not + provided, an empty dictionary is created. + + The `pattern' value may contain variables of the form ['varname'] which + are allowed to match anything. The value that is matched is returned as + part of a dictionary which maps 'varname' to the matched value. 'varname' + is not required to be a string object, but using strings makes patterns + and the code which uses them more readable. + + This function returns two values: a boolean indicating whether a match + was found and a dictionary mapping variable names to their associated + values. """ if vars is None: vars = {} @@ -131,6 +153,15 @@ def match(pattern, data, vars=None): return same, vars +# This pattern identifies compound statements, allowing them to be readily +# differentiated from simple statements. +# +COMPOUND_STMT_PATTERN = ( + symbol.stmt, + (symbol.compound_stmt, ['compound']) + ) + + # This pattern will match a 'stmt' node which *might* represent a docstring; # docstrings require that the statement which provides the docstring be the # first statement in the class or function, which this pattern does not check. diff --git a/Demo/parser/pprint.py b/Demo/parser/pprint.py index c4b8158..36d1888 100644 --- a/Demo/parser/pprint.py +++ b/Demo/parser/pprint.py @@ -1,7 +1,7 @@ # pprint.py # # Author: Fred L. Drake, Jr. -# fdrake@vt.edu +# fdrake@cnri.reston.va.us, fdrake@intr.net # # This is a simple little module I wrote to make life easier. I didn't # see anything quite like it in the library, though I may have overlooked @@ -9,34 +9,28 @@ # tuples with fairly non-descriptive content. This is modelled very much # after Lisp/Scheme - style pretty-printing of lists. If you find it # useful, thank small children who sleep at night. -# """Support to pretty-print lists, tuples, & dictionaries recursively. -Very simple, but at least somewhat useful, especially in debugging -data structures. - -INDENT_PER_LEVEL -- Amount of indentation to use for each new - recursive level. The default is 1. This - must be a non-negative integer, and may be - set by the caller before calling pprint(). - -MAX_WIDTH -- Maximum width of the display. This is only - used if the representation *can* be kept - less than MAX_WIDTH characters wide. May - be set by the user before calling pprint(). - -TAB_WIDTH -- The width represented by a single tab. This - value is typically 8, but 4 is the default - under MacOS. Can be changed by the user if - desired, but is probably not a good idea. - -pprint(seq [, stream]) -- The pretty-printer. This takes a Python - object (presumably a sequence, but that - doesn't matter) and an optional output - stream. See the function documentation - for details. -""" +Very simple, but useful, especially in debugging data structures. + +Constants +--------- +INDENT_PER_LEVEL + Amount of indentation to use for each new recursive level. The + default is 1. This must be a non-negative integer, and may be set + by the caller before calling pprint(). + +MAX_WIDTH + Maximum width of the display. This is only used if the + representation *can* be kept less than MAX_WIDTH characters wide. + May be set by the user before calling pprint(). + +TAB_WIDTH + The width represented by a single tab. This value is typically 8, + but 4 is the default under MacOS. Can be changed by the user if + desired, but is probably not a good idea. +""" INDENT_PER_LEVEL = 1 @@ -46,46 +40,45 @@ import os TAB_WIDTH = (os.name == 'mac' and 4) or 8 del os +from types import DictType, ListType, TupleType def _indentation(cols): - "Create tabbed indentation string COLS columns wide." - - # This is used to reduce the byte-count for the output, allowing - # files created using this module to use as little external storage - # as possible. This is primarily intended to minimize impact on - # a user's quota when storing resource files, or for creating output - # intended for transmission. + """Create tabbed indentation string. + cols + Width of the indentation, in columns. + """ return ((cols / TAB_WIDTH) * '\t') + ((cols % TAB_WIDTH) * ' ') - def pprint(seq, stream = None, indent = 0, allowance = 0): """Pretty-print a list, tuple, or dictionary. - pprint(seq [, stream]) ==> None - - If STREAM is provided, output is written to that stream, otherwise - sys.stdout is used. Indentation is done according to - INDENT_PER_LEVEL, which may be set to any non-negative integer - before calling this function. The output written on the stream is - a perfectly valid representation of the Python object passed in, - with indentation to suite human-readable interpretation. The - output can be used as input without error, given readable - representations of all sequence elements are available via repr(). - Output is restricted to MAX_WIDTH columns where possible. The - STREAM parameter must support the write() method with a single - parameter, which will always be a string. The output stream may be - a StringIO.StringIO object if the result is needed as a string. + seq + List, tuple, or dictionary object to be pretty-printed. Other + object types are permitted by are not specially interpreted. + + stream + Output stream. If not provided, `sys.stdout' is used. This + parameter must support the `write()' method with a single + parameter, which will always be a string. It may be a + `StringIO.StringIO' object if the result is needed as a + string. + + Indentation is done according to `INDENT_PER_LEVEL', which may be + set to any non-negative integer before calling this function. The + output written on the stream is a perfectly valid representation + of the Python object passed in, with indentation to assist + human-readable interpretation. The output can be used as input + without error, given readable representations of all elements are + available via `repr()'. Output is restricted to `MAX_WIDTH' + columns where possible. """ - if stream is None: import sys stream = sys.stdout - from types import DictType, ListType, TupleType - rep = `seq` typ = type(seq) sepLines = len(rep) > (MAX_WIDTH - 1 - indent - allowance) @@ -140,4 +133,4 @@ def pprint(seq, stream = None, indent = 0, allowance = 0): # -# end of pprint.py +# end of file diff --git a/Demo/parser/simple.py b/Demo/parser/simple.py new file mode 100644 index 0000000..184e2fe --- /dev/null +++ b/Demo/parser/simple.py @@ -0,0 +1 @@ +def f(): "maybe a docstring" |