Changeset 33

Show
Ignore:
Timestamp:
05/31/06 17:32:14 (7 years ago)
Author:
mk
Message:

Check docstrings for use of reST (closes ticket #11).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/mk/cheesecake/codeparser.py

    r32 r33  
    11import os 
     2import re 
     3 
    24from model import System, Module, Class, Function, parseFile, processModuleAst 
    35 
     6def use_reST(text): 
     7    """Return True if text includes reST and False otherwise. 
     8 
     9    See http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html 
     10    for reference. 
     11 
     12    >>> use_reST("String with *emphasis*.") 
     13    True 
     14    >>> use_reST("*Multi-word emphasis.*") 
     15    True 
     16    >>> use_reST("How about testing **strong string**?") 
     17    True 
     18    >>> use_reST("Some *noisy!* punctuation") 
     19    True 
     20    >>> use_reST("**characters?**, in the way.") 
     21    True 
     22    >>> use_reST("Don\'t forget ``inline literals``.") 
     23    True 
     24    >>> use_reST("This is reST (hyperlink_).") 
     25    True 
     26    >>> use_reST("This is (`quite long hyperlink`_).") 
     27    True 
     28    >>> use_reST("* Bullet\\n* List\\n") 
     29    True 
     30    >>> use_reST(":Field: list\\n:indeed: it is\\n") 
     31    True 
     32 
     33    >>> use_reST("Plain string.") 
     34    False 
     35    >>> use_reST("Do some math: 2 * 2a* 2 = 8a") 
     36    False 
     37    >>> use_reST("Not*really*strong.") 
     38    False 
     39    >>> use_reST("Interpreted `text` is widely used as quotes, so exclude it.") 
     40    False 
     41    >>> use_reST("Not a :field:.") 
     42    False 
     43    """ 
     44    def compile(pattern, user_map=None): 
     45        """Compile a regex pattern using default or user mapping. 
     46        """ 
     47 
     48        # Word in reST can also contain hyphens and punctuation characters. 
     49        mapping = {'ALPHA': r'[-.,?!\w]', 'WORD': r'[-.,?!\s\w]', 
     50                           'START': r'(^|\s)', 'END': r'([.,?!\s]|$)'} 
     51 
     52        if user_map: 
     53            mapping = mapping.copy() 
     54            mapping.update(user_map) 
     55 
     56        def sub(text, mapping): 
     57            for From, To in mapping.iteritems(): 
     58                text = text.replace(From, To) 
     59            return text 
     60 
     61        return re.compile(sub(pattern, mapping), re.LOCALE | re.VERBOSE) 
     62 
     63    def inline_markup(start, end=None, mapping=None): 
     64        if not end: 
     65            end = start 
     66        return compile(r'''(START  %(start)s  ALPHA  %(end)s  END) | 
     67                           (START  %(start)s  ALPHA  WORD*  ALPHA  %(end)s  END)'''\ 
     68                       % {'start': start, 'end': end}, mapping) 
     69 
     70    def line_markup(start, end=None): 
     71        return inline_markup(start, end, mapping={'ALPHA': r'[-.,?!\s\w]', 
     72                                                  'START': r'(\n|^)[\ \t]*', 
     73                                                  'END': r''}) 
     74 
     75    emphasis_pattern = inline_markup(r'\*') 
     76    strong_pattern = inline_markup(r'\*\*') 
     77    inline_pattern = inline_markup(r'``') 
     78    hyperlink_pattern = inline_markup(r'\(', r'_\)', 
     79                                      {'ALPHA': r'\w', 'WORD': r'[-.\w]'}) 
     80    long_hyperlink_pattern = inline_markup(r'\(`', r'`_\)') 
     81    field_pattern = line_markup(r':') 
     82    bullet_pattern = line_markup(r'\*', r'') 
     83 
     84    rest_patterns = [strong_pattern, 
     85                     emphasis_pattern, 
     86                     inline_pattern, 
     87                     hyperlink_pattern, 
     88                     long_hyperlink_pattern, 
     89                     field_pattern, 
     90                     bullet_pattern] 
     91 
     92    for pattern in rest_patterns: 
     93        if re.search(pattern, text): 
     94            return True 
     95 
     96    return False 
     97 
     98 
    499class CodeParser(object): 
    5     """ 
    6     Information about the structure of a Python module 
     100    """Information about the structure of a Python module. 
    7101 
    8102    * Collects modules, classes, methods, functions and associated docstrings 
     
    20114        self.method_func = [] 
    21115        self.functions = [] 
    22         self.docstrings = {} 
     116        self.docstrings = [] # objects that have docstrings 
     117        self.rest_docstrings = [] # objects that have docstrings with reST 
    23118 
    24119        (path, filename) = os.path.split(pyfile) 
     
    41136                self.method_func.append(fullname) 
    42137            if isinstance(obj.docstring, str) and obj.docstring.strip(): 
    43                 self.docstrings[fullname] = 1 
     138                self.docstrings.append(fullname) 
     139                if use_reST(obj.docstring): 
     140                    self.rest_docstrings.append(fullname) 
    44141 
    45142        for method_or_func in self.method_func: 
     
    59156 
    60157    def object_count(self): 
    61         """ 
    62         Return number of objects found in this module 
     158        """Return number of objects found in this module. 
    63159 
     160        Objects include: 
    64161        * module 
    65162        * classes 
     
    74171 
    75172    def docstring_count(self): 
     173        """Return number of docstrings found in this module 
    76174        """ 
    77         Return number of docstrings found in this module 
     175        return len(self.docstrings) 
     176 
     177    def rest_docstring_count(self): 
     178        """Return number of reST docstrings found in this module 
    78179        """ 
    79         return len(self.docstrings.keys()
     180        return len(self.rest_docstrings
    80181 
    81182    def functions_called(self): 
  • branches/mk/tests/data/module1.py

    r32 r33  
    3636        pass 
    3737 
     38    def method5(self): 
     39        """Method with few definitions. 
     40 
     41        :Word: And its definition. 
     42        """ 
     43        pass 
     44 
    3845class Class2: 
    3946 
     
    7178    """ 
    7279    pass 
     80 
     81def func7(): 
     82    "Time to get *a bit* of reST." 
     83    pass 
  • branches/mk/tests/test_code_parser.py

    r32 r33  
    22 
    33import _path_cheesecake 
     4from _helper_cheesecake import set 
     5 
    46from cheesecake.codeparser import CodeParser 
    57 
     
    2224        "module1.Class1.method2",  
    2325        "module1.Class1.method3", 
    24         "module1.Class1.method4"] 
     26        "module1.Class1.method4", 
     27        "module1.Class1.method5",] 
    2528 
    2629    def test_functions(self): 
     
    3134        "module1.func4",  
    3235        "module1.__func5__", 
    33         "module1.func6"] 
     36        "module1.func6", 
     37        "module1.func7"] 
    3438 
    3539    def test_count(self): 
    36         assert self.code1.object_count() == 15 
    37         assert self.code1.docstring_count() == 12 
     40        assert self.code1.object_count() == 17 
     41        assert self.code1.docstring_count() == 14 
     42        assert self.code1.rest_docstring_count() == 2 
    3843 
    3944    def test_docstrings(self): 
     
    4752            "module1.Class1.method2", 
    4853            "module1.Class1.method3", 
     54            "module1.Class1.method5", 
    4955            "module1.func1", 
    5056            "module1.func2", 
    5157            "module1.func3", 
    5258            "module1.__func5__", 
     59            "module1.func7", 
    5360        ] 
    5461 
    55         objects_without_docstrings = [ 
    56             "module1.Class1.method4", 
    57             "module1.func4", 
    58             "module1.func6", 
     62        objects_with_rest_docstrings = [ 
     63            "module1.Class1.method5", 
     64            "module1.func7", 
    5965        ] 
    6066 
    6167        print self.code1.docstrings 
    6268 
    63         for obj in objects_with_docstrings: 
    64             assert self.code1.docstrings.get(obj) == 1 
    65  
    66         for obj in objects_without_docstrings: 
    67             assert not self.code1.docstrings.get(obj) 
     69        assert set(objects_with_docstrings) == set(self.code1.docstrings) 
     70        assert set(objects_with_rest_docstrings) == set(self.code1.rest_docstrings) 
  • branches/mk/tests/test_count_files.py

    r29 r33  
    11 
    22from _mockup_cheesecake import MockupCheesecakeTest 
    3  
    4 if 'set' not in dir(__builtins__): 
    5     from sets import Set as set 
     3from _helper_cheesecake import set 
    64 
    75 
  • branches/mk/tests/test_index_docstrings.py

    r2 r33  
     1import os 
     2from math import ceil 
     3 
    14import _path_cheesecake 
    25from cheesecake.cheesecake_index import Cheesecake, CodeParser 
    3 import os 
    4 from math import ceil 
     6 
    57datadir = os.path.abspath(os.path.join(os.path.dirname(__file__), "data")) 
    68 
    7 class TestIndexDocstrings
     9class TestIndexDocstrings(object)
    810    def setUp(self): 
    911        self.cheesecake = Cheesecake(path=os.path.join(datadir, "package1.tar.gz"))