Package cheesecake :: Module codeparser
[hide private]
[frames] | no frames]

Source Code for Module cheesecake.codeparser

  1  import doctest 
  2  import os 
  3  import re 
  4   
  5  import logger 
  6  from model import System, Module, Class, Function, parseFile, processModuleAst 
  7   
  8   
  9  # Python 2.3/2.4 compatibilty hacks. 
 10  if getattr(doctest, 'DocTestParser', False): 
 11      # Python 2.4 have DocTestParser class. 
 12      get_doctests = doctest.DocTestParser().get_examples 
 13  else: 
 14      # Python 2.3 have _extract_examples function. 
 15      get_doctests = doctest._extract_examples 
 16   
 17   
18 -def compile_regex(pattern, user_map=None):
19 """Compile a regex pattern using default or user mapping. 20 """ 21 22 # Handy regular expressions. 23 mapping = {'ALPHA': r'[-.,?!\w]', 'WORD': r'[-.,?!\s\w]', 24 'START': r'(^|\s)', 'END': r'([.,?!\s]|$)'} 25 26 if user_map: 27 mapping = mapping.copy() 28 mapping.update(user_map) 29
30 - def sub(text, mapping):
31 for From, To in mapping.iteritems(): 32 text = text.replace(From, To) 33 return text
34 35 pattern = sub(pattern, mapping) 36 37 return re.compile(pattern, re.LOCALE | re.VERBOSE)
38
39 -def inline_markup(start, end=None, mapping=None):
40 if end is None: 41 end = start 42 return compile_regex(r'''(START %(start)s ALPHA %(end)s END) | 43 (START %(start)s ALPHA WORD* ALPHA %(end)s END)'''\ 44 % {'start': start, 'end': end}, mapping)
45
46 -def line_markup(start, end=None):
47 return inline_markup(start, end, mapping={'ALPHA': r'[-.,?!\s\w]', 48 'START': r'(\n|^)[\ \t]*', 49 'END': r''})
50 51 supported_formats = { 52 # reST refrence: http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html 53 'reST': [ 54 inline_markup(r'\*'), # emphasis 55 inline_markup(r'\*\*'), # strong 56 inline_markup(r'``'), # inline 57 inline_markup(r'\(', r'_\)', # hyperlink 58 {'ALPHA': r'\w', 'WORD': r'[-.\w]'}), 59 inline_markup(r'\(`', r'`_\)'), # long hyperlink 60 line_markup(r':'), # field 61 line_markup(r'[*+-]', r''), # unordered list 62 line_markup(r'((\d+) | ([a-zA-Z]+) [.\)])', r''), # ordered list 63 line_markup(r'\( ((\d+) | ([a-zA-Z]+)) \)', r''), # ordered list 64 ], 65 66 # epytext reference: http://epydoc.sourceforge.net/epytext.html 67 'epytext': [ 68 re.compile(r'[BCEGILMSUX]\{.*\}'), # inline elements 69 line_markup(r'@[a-z]+([\ \t][a-zA-Z]+)?:', r''), # fields 70 line_markup(r'-', r''), # unordered list 71 line_markup(r'\d+(\.\d+)*', r''), # ordered list 72 ], 73 74 # javadoc reference: http://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/javadoc.html 75 'javadoc': [ 76 re.compile(r'<[a-zA-z]+[^>]*>'), # HTML elements 77 line_markup(r'@[a-z][a-zA-Z]*\s', r''), # normal tags 78 re.compile(r'{@ ((docRoot) | (inheritDoc) | (link) | (linkplain) |'\ 79 ' (value)) [^}]* }', re.VERBOSE), # special tags 80 ], 81 } 82 83
84 -def use_format(text, format):
85 """Return True if text includes given documentation format 86 and False otherwise. 87 88 See supported_formats for list of known formats. 89 """ 90 for pattern in supported_formats[format]: 91 if re.search(pattern, text): 92 return True 93 94 return False
95 96
97 -class CodeParser(object):
98 """Information about the structure of a Python module. 99 100 * Collects modules, classes, methods, functions and associated docstrings 101 * Based on mwh's docextractor.model module 102 """
103 - def __init__(self, pyfile, log=None):
104 """Initialize Code Parser object. 105 106 :Parameters: 107 `pyfile` : str 108 Path to a Python module to parse. 109 `log` : logger.Producer instance 110 Logger to use during code parsing. 111 """ 112 if log: 113 self.log = log.codeparser 114 else: 115 self.log = logger.default.codeparser 116 self.modules = [] 117 self.classes = [] 118 self.methods = [] 119 self.method_func = [] 120 self.functions = [] 121 self.docstrings = [] # objects that have docstrings 122 self.docstrings_by_format = {} 123 self.formatted_docstrings_count = 0 124 self.doctests_count = 0 125 self.unittests_count = 0 126 127 # Initialize lists of format docstrings. 128 for format in supported_formats: 129 self.docstrings_by_format[format] = [] 130 131 (path, filename) = os.path.split(pyfile) 132 (module, ext) = os.path.splitext(filename) 133 self.log("Inspecting file: " + pyfile) 134 135 self.system = System() 136 try: 137 processModuleAst(parseFile(pyfile), module, self.system) 138 except Exception, e: 139 self.log("Code parsing error occured:\n***\n%s\n***" % str(e)) 140 return 141 142 for obj in self.system.orderedallobjects: 143 fullname = obj.fullName() 144 if isinstance(obj, Module): 145 self.modules.append(fullname) 146 if isinstance(obj, Class): 147 if 'unittest.TestCase' in obj.bases or 'TestCase' in obj.bases: 148 self.unittests_count += 1 149 self.classes.append(fullname) 150 if isinstance(obj, Function): 151 self.method_func.append(fullname) 152 if isinstance(obj.docstring, str) and obj.docstring.strip(): 153 self.docstrings.append(fullname) 154 # Check docstring for known documenation formats. 155 formatted = False 156 for format in supported_formats: 157 if use_format(obj.docstring, format): 158 self.docstrings_by_format[format].append(fullname) 159 formatted = True 160 if formatted: 161 self.formatted_docstrings_count += 1 162 163 # Check if docstring include any doctests. 164 if get_doctests(obj.docstring): 165 self.doctests_count += 1 166 167 for method_or_func in self.method_func: 168 method_found = 0 169 for cls in self.classes: 170 if method_or_func.startswith(cls): 171 self.methods.append(method_or_func) 172 method_found = 1 173 break 174 if not method_found: 175 self.functions.append(method_or_func) 176 177 self.log("modules: " + ",".join(self.modules)) 178 self.log("classes: " + ",".join(self.classes)) 179 self.log("methods: " + ",".join(self.methods)) 180 self.log("functions: " + ",".join(self.functions)) 181 self.log("docstrings: %s" % self.docstrings_by_format) 182 self.log("number of doctests: %d" % self.doctests_count)
183
184 - def object_count(self):
185 """Return number of objects found in this module. 186 187 Objects include: 188 * module 189 * classes 190 * methods 191 * functions 192 """ 193 module_count = len(self.modules) 194 cls_count = len(self.classes) 195 method_count = len(self.methods) 196 func_count = len(self.functions) 197 return module_count + cls_count + method_count + func_count
198
199 - def docstring_count(self):
200 """Return number of docstrings found in this module. 201 """ 202 return len(self.docstrings)
203
204 - def docstring_count_by_type(self, type):
205 """Return number of docstrings of given type found in this module. 206 """ 207 return len(self.docstrings_by_format[type])
208
209 - def _functions_called(self):
210 """Return list of functions called by functions/methods 211 defined in this module. 212 """ 213 return self.system.func_called.keys()
214 215 functions_called = property(_functions_called)
216