cpython/Demo/parser/example.py

191 lines
5.6 KiB
Python
Raw Normal View History

1996-08-21 13:28:53 -03:00
"""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.
1996-08-21 13:28:53 -03:00
"""
import os
import parser
1996-08-21 13:28:53 -03:00
import symbol
import token
import types
from types import ListType, TupleType
1996-08-21 13:28:53 -03:00
def get_docs(fileName):
"""Retrieve information from the parse tree of a source file.
fileName
1998-09-14 13:44:15 -03:00
Name of the file to read Python source code from.
1996-08-21 13:28:53 -03:00
"""
source = open(fileName).read()
basename = os.path.basename(os.path.splitext(fileName)[0])
ast = parser.suite(source)
return ModuleInfo(ast.totuple(), basename)
1996-08-21 13:28:53 -03:00
class SuiteInfoBase:
1996-08-21 13:28:53 -03:00
_docstring = ''
_name = ''
def __init__(self, tree = None):
1998-09-14 13:44:15 -03:00
self._class_info = {}
self._function_info = {}
if tree:
self._extract_info(tree)
def _extract_info(self, tree):
1998-09-14 13:44:15 -03:00
# 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)
1996-08-21 13:28:53 -03:00
def get_docstring(self):
1998-09-14 13:44:15 -03:00
return self._docstring
1996-08-21 13:28:53 -03:00
def get_name(self):
1998-09-14 13:44:15 -03:00
return self._name
1996-08-21 13:28:53 -03:00
def get_class_names(self):
1998-09-14 13:44:15 -03:00
return self._class_info.keys()
1996-08-21 13:28:53 -03:00
def get_class_info(self, name):
1998-09-14 13:44:15 -03:00
return self._class_info[name]
1996-08-21 13:28:53 -03:00
def __getitem__(self, name):
1998-09-14 13:44:15 -03:00
try:
return self._class_info[name]
except KeyError:
return self._function_info[name]
class SuiteFuncInfo:
# Mixin class providing access to function names and info.
1996-08-21 13:28:53 -03:00
def get_function_names(self):
1998-09-14 13:44:15 -03:00
return self._function_info.keys()
1996-08-21 13:28:53 -03:00
def get_function_info(self, name):
1998-09-14 13:44:15 -03:00
return self._function_info[name]
1996-08-21 13:28:53 -03:00
class FunctionInfo(SuiteInfoBase, SuiteFuncInfo):
def __init__(self, tree = None):
1998-09-14 13:44:15 -03:00
self._name = tree[2][1]
SuiteInfoBase.__init__(self, tree and tree[-1] or None)
1996-08-21 13:28:53 -03:00
class ClassInfo(SuiteInfoBase):
def __init__(self, tree = None):
1998-09-14 13:44:15 -03:00
self._name = tree[2][1]
SuiteInfoBase.__init__(self, tree and tree[-1] or None)
1996-08-21 13:28:53 -03:00
def get_method_names(self):
1998-09-14 13:44:15 -03:00
return self._function_info.keys()
1996-08-21 13:28:53 -03:00
def get_method_info(self, name):
1998-09-14 13:44:15 -03:00
return self._function_info[name]
1996-08-21 13:28:53 -03:00
class ModuleInfo(SuiteInfoBase, SuiteFuncInfo):
def __init__(self, tree = None, name = "<string>"):
1998-09-14 13:44:15 -03:00
self._name = name
SuiteInfoBase.__init__(self, tree)
if tree:
found, vars = match(DOCSTRING_STMT_PATTERN, tree[1])
if found:
self._docstring = vars["docstring"]
1996-08-21 13:28:53 -03:00
def match(pattern, data, vars=None):
"""Match `data' to `pattern', with variable extraction.
pattern
1998-09-14 13:44:15 -03:00
Pattern to match against, possibly containing variables.
data
1998-09-14 13:44:15 -03:00
Data to be checked and against which variables are extracted.
vars
1998-09-14 13:44:15 -03:00
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.
1996-08-21 13:28:53 -03:00
"""
if vars is None:
1998-09-14 13:44:15 -03:00
vars = {}
if type(pattern) is ListType: # 'variables' are ['varname']
vars[pattern[0]] = data
return 1, vars
1996-08-21 13:28:53 -03:00
if type(pattern) is not TupleType:
1998-09-14 13:44:15 -03:00
return (pattern == data), vars
1996-08-21 13:28:53 -03:00
if len(data) != len(pattern):
1998-09-14 13:44:15 -03:00
return 0, vars
1996-08-21 13:28:53 -03:00
for pattern, data in map(None, pattern, data):
1998-09-14 13:44:15 -03:00
same, vars = match(pattern, data, vars)
if not same:
break
1996-08-21 13:28:53 -03:00
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'])
)
1996-08-21 13:28:53 -03:00
# 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.
#
DOCSTRING_STMT_PATTERN = (
symbol.stmt,
(symbol.simple_stmt,
(symbol.small_stmt,
(symbol.expr_stmt,
(symbol.testlist,
1998-09-14 13:44:15 -03:00
(symbol.test,
(symbol.and_test,
(symbol.not_test,
(symbol.comparison,
(symbol.expr,
(symbol.xor_expr,
(symbol.and_expr,
(symbol.shift_expr,
(symbol.arith_expr,
(symbol.term,
(symbol.factor,
(symbol.power,
(symbol.atom,
(token.STRING, ['docstring'])
)))))))))))))))),
1996-08-21 13:28:53 -03:00
(token.NEWLINE, '')
))