mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			194 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""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
 | 
						|
import token
 | 
						|
import types
 | 
						|
 | 
						|
 | 
						|
def get_docs(fileName):
 | 
						|
    """Retrieve information from the parse tree of a source file.
 | 
						|
 | 
						|
    fileName
 | 
						|
	Name of the file to read Python source code from.
 | 
						|
    """
 | 
						|
    source = open(fileName).read()
 | 
						|
    import os
 | 
						|
    basename = os.path.basename(os.path.splitext(fileName)[0])
 | 
						|
    import parser
 | 
						|
    ast = parser.suite(source)
 | 
						|
    tup = parser.ast2tuple(ast)
 | 
						|
    return ModuleInfo(tup, basename)
 | 
						|
 | 
						|
 | 
						|
class SuiteInfoBase:
 | 
						|
    _docstring = ''
 | 
						|
    _name = ''
 | 
						|
 | 
						|
    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
 | 
						|
 | 
						|
    def get_name(self):
 | 
						|
	return self._name
 | 
						|
 | 
						|
    def get_class_names(self):
 | 
						|
	return self._class_info.keys()
 | 
						|
 | 
						|
    def get_class_info(self, name):
 | 
						|
	return self._class_info[name]
 | 
						|
 | 
						|
    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()
 | 
						|
 | 
						|
    def get_function_info(self, name):
 | 
						|
	return self._function_info[name]
 | 
						|
 | 
						|
 | 
						|
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 = 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()
 | 
						|
 | 
						|
    def get_method_info(self, name):
 | 
						|
	return self._function_info[name]
 | 
						|
 | 
						|
 | 
						|
class ModuleInfo(SuiteInfoBase, SuiteFuncInfo):
 | 
						|
    def __init__(self, tree = None, name = "<string>"):
 | 
						|
	self._name = name
 | 
						|
	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 = {}
 | 
						|
    if type(pattern) is ListType:	# 'variables' are ['varname']
 | 
						|
	vars[pattern[0]] = data
 | 
						|
	return 1, vars
 | 
						|
    if type(pattern) is not TupleType:
 | 
						|
	return (pattern == data), vars
 | 
						|
    if len(data) != len(pattern):
 | 
						|
	return 0, vars
 | 
						|
    for pattern, data in map(None, pattern, data):
 | 
						|
	same, vars = match(pattern, data, vars)
 | 
						|
	if not same:
 | 
						|
	    break
 | 
						|
    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.
 | 
						|
#
 | 
						|
DOCSTRING_STMT_PATTERN = (
 | 
						|
    symbol.stmt,
 | 
						|
    (symbol.simple_stmt,
 | 
						|
     (symbol.small_stmt,
 | 
						|
      (symbol.expr_stmt,
 | 
						|
       (symbol.testlist,
 | 
						|
	(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'])
 | 
						|
		     )))))))))))))))),
 | 
						|
     (token.NEWLINE, '')
 | 
						|
     ))
 | 
						|
 | 
						|
#
 | 
						|
#  end of file
 |