Changeset 3107:daf88d90fe8d in livinglogic.python.xist

Show
Ignore:
Timestamp:
01/05/08 23:30:18 (12 years ago)
Author:
Walter Doerwald <walter@…>
Branch:
default
Message:

Add support for ReST in docstrings.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • src/ll/xist/ns/doc.py

    r3080 r3107  
    1616 
    1717# import __builtin__ to use property, which is also defined here 
    18 import sys, types, inspect, warnings, operator, __builtin__ 
     18import sys, types, inspect, optparse, collections, warnings, operator, __builtin__ 
     19 
     20try: 
     21    from docutils import nodes, utils, parsers as restparsers, frontend 
     22    from docutils.parsers.rst import roles 
     23except ImportError: 
     24    pass 
     25else: 
     26    # FIXME: Do the right thing 
     27    roles.register_generic_role("mod", nodes.literal) 
     28    roles.register_generic_role("class", nodes.literal) 
     29    roles.register_generic_role("func", nodes.literal) 
     30    roles.register_generic_role("meth", nodes.literal) 
     31    roles.register_generic_role("var", nodes.literal) 
     32    roles.register_generic_role("exc", nodes.literal) 
     33    roles.register_generic_role("attr", nodes.literal) 
     34    roles.register_generic_role("prop", nodes.literal) 
     35    roles.register_generic_role("option", nodes.literal) 
    1936 
    2037import ll 
     
    3451 
    3552 
    36 def getdoc(thing): 
     53def getdoc(thing, format): 
    3754    if thing.__doc__ is None: 
    3855        return xsc.Null 
    39     lines = thing.__doc__.split("\n") 
    40  
    41     # find first nonempty line 
    42     for i in xrange(len(lines)): 
    43         if lines[i] and not lines[i].isspace(): 
    44             if i: 
    45                 del lines[:i] 
    46             break 
    47  
    48     if lines: 
    49         # find starting white space of this line 
    50         startwhite = "" 
    51         for c in lines[0]: 
    52             if c.isspace(): 
    53                 startwhite += c 
    54             else: 
     56    if format.lower() == "plaintext": 
     57        return xsc.Text(thing.__doc__) 
     58    elif format.lower() == "restructuredtext": 
     59        return rest2doc(thing.__doc__) 
     60    elif format.lower() == "xist": 
     61        lines = thing.__doc__.split("\n") 
     62 
     63        # find first nonempty line 
     64        for i in xrange(len(lines)): 
     65            if lines[i] and not lines[i].isspace(): 
     66                if i: 
     67                    del lines[:i] 
    5568                break 
    5669 
    57         # remove this whitespace from every line 
    58         for i in xrange(len(lines)): 
    59             if lines[i].startswith(startwhite): 
    60                 lines[i] = lines[i][len(startwhite):] 
    61  
    62         # remove empty lines 
    63         while lines and not lines[0]: 
    64             del lines[0] 
    65         while lines and not lines[-1]: 
    66             del lines[-1] 
    67  
    68     text = "\n".join(lines) 
    69  
    70     if inspect.ismethod(thing): 
    71         base = "METHOD-DOCSTRING(%s.%s.%s)" % (_getmodulename(thing), thing.im_class.__name__, thing.__name__) 
    72     elif isinstance(thing, __builtin__.property): 
    73         base = "PROPERTY-DOCSTRING(%s.%s)" % (_getmodulename(thing), "unknown") 
    74     elif inspect.isfunction(thing): 
    75         base = "FUNCTION-DOCSTRING(%s.%s)" % (_getmodulename(thing), thing.__name__) 
    76     elif inspect.isclass(thing): 
    77         base = "CLASS-DOCSTRING(%s.%s)" % (_getmodulename(thing), thing.__name__) 
    78     elif inspect.ismodule(thing): 
    79         base = "MODULE-DOCSTRING(%s)" % _getmodulename(thing) 
     70        if lines: 
     71            # find starting white space of this line 
     72            startwhite = "" 
     73            for c in lines[0]: 
     74                if c.isspace(): 
     75                    startwhite += c 
     76                else: 
     77                    break 
     78 
     79            # remove this whitespace from every line 
     80            for i in xrange(len(lines)): 
     81                if lines[i].startswith(startwhite): 
     82                    lines[i] = lines[i][len(startwhite):] 
     83     
     84            # remove empty lines 
     85            while lines and not lines[0]: 
     86                del lines[0] 
     87            while lines and not lines[-1]: 
     88                del lines[-1] 
     89 
     90        text = "\n".join(lines) 
     91 
     92        if inspect.ismethod(thing): 
     93            base = "METHOD-DOCSTRING(%s.%s.%s)" % (_getmodulename(thing), thing.im_class.__name__, thing.__name__) 
     94        elif isinstance(thing, __builtin__.property): 
     95            base = "PROPERTY-DOCSTRING(%s.%s)" % (_getmodulename(thing), "unknown") 
     96        elif inspect.isfunction(thing): 
     97            base = "FUNCTION-DOCSTRING(%s.%s)" % (_getmodulename(thing), thing.__name__) 
     98        elif inspect.isclass(thing): 
     99            base = "CLASS-DOCSTRING(%s.%s)" % (_getmodulename(thing), thing.__name__) 
     100        elif inspect.ismodule(thing): 
     101            base = "MODULE-DOCSTRING(%s)" % _getmodulename(thing) 
     102        else: 
     103            base = "DOCSTRING" 
     104        node = parsers.parsestring(text, base=base, prefixes=xsc.docprefixes()) 
     105        if not node[par]: # optimization: one paragraph docstrings don't need a <par> element. 
     106            node = par(node) 
     107 
     108        if inspect.ismethod(thing): 
     109            # Use the original method instead of the decorator 
     110            realthing = thing 
     111            while hasattr(realthing, "__wrapped__"): 
     112                realthing = realthing.__wrapped__ 
     113            for ref in node.walknode(pyref): 
     114                if u"module" not in ref.attrs: 
     115                    ref[u"module"] = _getmodulename(realthing) 
     116                    if u"class_" not in ref.attrs: 
     117                        ref[u"class_"] = thing.im_class.__name__ 
     118                        if u"method" not in ref.attrs: 
     119                            ref[u"method"] = thing.__name__ 
     120        elif inspect.isfunction(thing): 
     121            # Use the original method instead of the decorator 
     122            while hasattr(thing, "__wrapped__"): 
     123                thing = thing.__wrapped__ 
     124            for ref in node.walknode(pyref): 
     125                if u"module" not in ref.attrs: 
     126                    ref[u"module"] = _getmodulename(thing) 
     127        elif inspect.isclass(thing): 
     128            for ref in node.walknode(pyref): 
     129                if u"module" not in ref.attrs: 
     130                    ref[u"module"] = _getmodulename(thing) 
     131                    if u"class_" not in ref.attrs: 
     132                        ref[u"class_"] = thing.__name__ 
     133        elif inspect.ismodule(thing): 
     134            for ref in node.walknode(pyref): 
     135                if u"module" not in ref.attrs: 
     136                    ref[u"module"] = thing.__name__ 
     137        return node 
    80138    else: 
    81         base = "DOCSTRING" 
    82     node = parsers.parsestring(text, base=base, prefixes=xsc.docprefixes()) 
    83     if not node[par]: # optimization: one paragraph docstrings don't need a <par> element. 
    84         node = par(node) 
    85  
    86     if inspect.ismethod(thing): 
    87         # Use the original method instead of the decorator 
    88         realthing = thing 
    89         while hasattr(realthing, "__wrapped__"): 
    90             realthing = realthing.__wrapped__ 
    91         for ref in node.walknode(pyref): 
    92             if u"module" not in ref.attrs: 
    93                 ref[u"module"] = _getmodulename(realthing) 
    94                 if u"class_" not in ref.attrs: 
    95                     ref[u"class_"] = thing.im_class.__name__ 
    96                     if u"method" not in ref.attrs: 
    97                         ref[u"method"] = thing.__name__ 
    98     elif inspect.isfunction(thing): 
    99         # Use the original method instead of the decorator 
    100         while hasattr(thing, "__wrapped__"): 
    101             thing = thing.__wrapped__ 
    102         for ref in node.walknode(pyref): 
    103             if u"module" not in ref.attrs: 
    104                 ref[u"module"] = _getmodulename(thing) 
    105     elif inspect.isclass(thing): 
    106         for ref in node.walknode(pyref): 
    107             if u"module" not in ref.attrs: 
    108                 ref[u"module"] = _getmodulename(thing) 
    109                 if u"class_" not in ref.attrs: 
    110                     ref[u"class_"] = thing.__name__ 
    111     elif inspect.ismodule(thing): 
    112         for ref in node.walknode(pyref): 
    113             if u"module" not in ref.attrs: 
    114                 ref[u"module"] = thing.__name__ 
    115     return node 
     139        raise ValueError("unknown format %r" % format) 
    116140 
    117141 
     
    175199 
    176200 
    177 def explain(thing, name=None, context=[]): 
     201def explain(thing, name=None, format=None, context=[]): 
    178202    """ 
    179203    <par>Return a &xml; representation of the documentation of 
     
    203227 
    204228    # Determine whether thing has a docstring 
    205     doc = getdoc(thing) 
     229    if format is None and inspect.ismodule(thing): 
     230        format = getattr(thing, "__docformat__", "plaintext") 
     231    doc = getdoc(thing, format) 
    206232    if doc is xsc.Null: 
    207233        hasdoc = u"nodoc" 
     
    238264        node = section(title(sig), doc, role=(visibility, u" property ", hasdoc), id=id) 
    239265        if thing.fget is not None: 
    240             node.append(explain(thing.fget, u"__get__", context)) 
     266            node.append(explain(thing.fget, u"__get__", format, context)) 
    241267        if thing.fset is not None: 
    242             node.append(explain(thing.fset, u"__set__", context)) 
     268            node.append(explain(thing.fset, u"__set__", format, context)) 
    243269        if thing.fdel is not None: 
    244             node.append(explain(thing.fdel, u"__delete__", context)) 
     270            node.append(explain(thing.fdel, u"__delete__", format, context)) 
    245271        return node 
    246272    elif inspect.isclass(thing): 
     
    306332            all.sort() 
    307333            for (key, subobj, subname) in all: 
    308                 node.append(explain(subobj, subname, context)) 
     334                node.append(explain(subobj, subname, format, context)) 
    309335        return node 
    310336    elif inspect.ismodule(thing): 
     
    323349            for (key, obj, name) in all: 
    324350                node.append( 
    325                     explain(obj, name, context), 
     351                    explain(obj, name, format, context), 
    326352                ) 
    327353        return node 
     
    15321558        ) 
    15331559        return e 
     1560 
     1561 
     1562class ReSTConversionWarning(Warning): 
     1563    pass 
     1564 
     1565 
     1566class ReSTConverter(object): 
     1567    def __init__(self): 
     1568        self.namedrefs = collections.defaultdict() 
     1569        self.namedrefs.default_factory = list 
     1570        self.unnamedrefs = [] 
     1571 
     1572    def convert(self, node): 
     1573        if isinstance(node, nodes.document): 
     1574            return xsc.Frag(self.convert(child) for child in node.children) 
     1575        elif isinstance(node, nodes.Text): 
     1576            return xsc.Text(node.astext()) 
     1577        elif isinstance(node, nodes.section): 
     1578            return section(self.convert(child) for child in node.children) 
     1579        elif isinstance(node, nodes.title): 
     1580            return title(self.convert(child) for child in node.children) 
     1581        elif isinstance(node, nodes.paragraph): 
     1582            return par(self.convert(child) for child in node.children) 
     1583        elif isinstance(node, nodes.bullet_list): 
     1584            return ulist(self.convert(child) for child in node.children) 
     1585        elif isinstance(node, nodes.list_item): 
     1586            return item(self.convert(child) for child in node.children) 
     1587        elif isinstance(node, nodes.definition_list): 
     1588            return dlist(self.convert(child) for child in node.children) 
     1589        elif isinstance(node, nodes.definition_list_item): 
     1590            return xsc.Frag(self.convert(child) for child in node.children) 
     1591        elif isinstance(node, nodes.term): 
     1592            return term(self.convert(child) for child in node.children) 
     1593        elif isinstance(node, nodes.definition): 
     1594            return item(self.convert(child) for child in node.children) 
     1595        elif isinstance(node, nodes.literal_block): 
     1596            return prog(self.convert(child) for child in node.children) 
     1597        elif isinstance(node, nodes.literal): 
     1598            return lit(self.convert(child) for child in node.children) 
     1599        elif isinstance(node, nodes.emphasis): 
     1600            return em(self.convert(child) for child in node.children) 
     1601        elif isinstance(node, nodes.reference): 
     1602            link = link(self.convert(child) for child in node.children) 
     1603            if "anonymous" in node.attributes: 
     1604                self.unnamedrefs.append(link) 
     1605            else: 
     1606                self.namedrefs[node.attributes["refname"]].append(link) 
     1607            return link 
     1608        elif isinstance(node, nodes.target): 
     1609            uri = node.attributes["refuri"] 
     1610            if "anonymous" in node.attributes: 
     1611                # Set the link on the first unnamed reference 
     1612                self.unnamedrefs[0].attrs.href = uri 
     1613                del self.unnamedrefs[0] # done => remove it 
     1614            else: 
     1615                for name in node.attributes["names"]: 
     1616                    try: 
     1617                        links = self.namedrefs[name] 
     1618                    except KeyError: 
     1619                        pass 
     1620                    else: 
     1621                        for link in links: 
     1622                            link.attrs.href = uri 
     1623                        del self.namedrefs[name] 
     1624            return xsc.Null 
     1625        elif isinstance(node, nodes.system_message): 
     1626            warnings.warn(ReSTConversionWarning(str(node))) 
     1627            return xsc.Null # ignore system messages 
     1628        else: 
     1629            raise TypeError("can't handle %r" % node.__class__) 
     1630 
     1631 
     1632def rest2doc(string): 
     1633    parser = restparsers.get_parser_class("rst")() 
     1634    defaults = frontend.OptionParser().defaults.copy() 
     1635    defaults["tab_width"] = 3 
     1636    defaults["pep_references"] = 1 
     1637    defaults["rfc_references"] = 1 
     1638 
     1639    doc = utils.new_document("?", optparse.Values(defaults)) 
     1640    parser.parse(string, doc) 
     1641 
     1642    conv = ReSTConverter() 
     1643    return conv.convert(doc)