cython-devel

changeset 1157:075511424c24

Automatic embedding of signatures in docstring (#2)
author LisandroDalcin
date Fri Sep 19 16:28:36 2008 +0200 (3 years ago)
parents 02eb1bab8afa
children 137cca05a493
files Cython/Compiler/AutoDocTransforms.py Cython/Compiler/Main.py Cython/Compiler/Options.py Tools/cython-epydoc.py tests/run/embedsignatures.pyx
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Cython/Compiler/AutoDocTransforms.py Fri Sep 19 16:28:36 2008 +0200 1.3 @@ -0,0 +1,132 @@ 1.4 +import re 1.5 + 1.6 +from Cython.Compiler.Visitor import CythonTransform 1.7 +from Cython.Compiler.Nodes import DefNode, CFuncDefNode 1.8 +from Cython.Compiler.Errors import CompileError 1.9 +from Cython.Compiler.StringEncoding import EncodedString 1.10 +from Cython.Compiler import Options 1.11 + 1.12 + 1.13 +class EmbedSignature(CythonTransform): 1.14 + 1.15 + SPECIAL_METHOD_RE = re.compile(r'__\w+__') 1.16 + 1.17 + def __init__(self, context): 1.18 + super(EmbedSignature, self).__init__(context) 1.19 + self.denv = None # XXX 1.20 + self.is_in_class = False 1.21 + self.class_name = None 1.22 + 1.23 + def _fmt_arg_type(self, arg): 1.24 + try: 1.25 + return arg.base_type.name 1.26 + except AttributeError: 1.27 + return "" 1.28 + 1.29 + def _fmt_arg_name(self, arg): 1.30 + try: 1.31 + return arg.declarator.name 1.32 + except AttributeError: 1.33 + return arg.declarator.base.name 1.34 + 1.35 + def _fmt_arg_defv(self, arg): 1.36 + if not arg.default: 1.37 + return None 1.38 + try: 1.39 + denv = self.denv # XXX 1.40 + ctval = arg.default.compile_time_value(self.denv) 1.41 + return '%s' % ctval 1.42 + except Exception: 1.43 + try: 1.44 + return arg.default.name # XXX 1.45 + except AttributeError: 1.46 + return '<???>' 1.47 + 1.48 + def _fmt_arg(self, arg): 1.49 + arg_type = self._fmt_arg_type(arg) 1.50 + arg_name = self._fmt_arg_name(arg) 1.51 + arg_defv = self._fmt_arg_defv(arg) 1.52 + doc = arg_name 1.53 + if arg_type: 1.54 + doc = ('%s ' % arg_type) + doc 1.55 + if arg_defv: 1.56 + doc = doc + ('=%s' % arg_defv) 1.57 + return doc 1.58 + 1.59 + def _fmt_arglist(self, args, 1.60 + npargs=0, pargs=None, 1.61 + nkargs=0, kargs=None): 1.62 + arglist = [] 1.63 + for arg in args: 1.64 + arg_doc = self._fmt_arg(arg) 1.65 + arglist.append(arg_doc) 1.66 + if pargs: 1.67 + arglist.insert(npargs, '*%s' % pargs.name) 1.68 + elif nkargs: 1.69 + arglist.insert(npargs, '*') 1.70 + if kargs: 1.71 + arglist.append('**%s' % kargs.name) 1.72 + return arglist 1.73 + 1.74 + def _fmt_signature(self, cls_name, func_name, args, 1.75 + npargs=0, pargs=None, 1.76 + nkargs=0, kargs=None, 1.77 + return_type=None): 1.78 + arglist = self._fmt_arglist(args, 1.79 + npargs, pargs, 1.80 + nkargs, kargs) 1.81 + arglist = ', '.join(arglist) 1.82 + func_doc = '%s(%s)' % (func_name, arglist) 1.83 + if cls_name: 1.84 + func_doc = ('%s.' % cls_name) + func_doc 1.85 + if return_type: 1.86 + func_doc = func_doc + ' -> %s' % return_type 1.87 + return func_doc 1.88 + 1.89 + def _embed_signature(self, signature, node_doc): 1.90 + if node_doc: 1.91 + return signature + '\n' + node_doc 1.92 + else: 1.93 + return signature 1.94 + 1.95 + def visit_ClassDefNode(self, node): 1.96 + oldincls = self.is_in_class 1.97 + oldname = self.class_name 1.98 + self.is_in_class = True 1.99 + try: 1.100 + # PyClassDefNode 1.101 + self.class_name = node.name 1.102 + except AttributeError: 1.103 + # CClassDefNode 1.104 + self.class_name = node.class_name 1.105 + self.visitchildren(node) 1.106 + self.is_in_class = oldincls 1.107 + self.class_name = oldname 1.108 + return node 1.109 + 1.110 + def visit_FuncDefNode(self, node): 1.111 + signature = None 1.112 + if type(node) is DefNode: # def FOO(...): 1.113 + special_method = (self.is_in_class and \ 1.114 + self.SPECIAL_METHOD_RE.match(node.name)) 1.115 + if not special_method: 1.116 + nkargs = getattr(node, 'num_kwonly_args', 0) 1.117 + npargs = len(node.args) - nkargs 1.118 + signature = self._fmt_signature( 1.119 + self.class_name, node.name, node.args, 1.120 + npargs, node.star_arg, 1.121 + nkargs, node.starstar_arg, 1.122 + return_type=None) 1.123 + elif type(node) is CFuncDefNode: 1.124 + if node.overridable: # cpdef FOO(...): 1.125 + signature = self._fmt_signature( 1.126 + self.class_name, node.declarator.base.name, 1.127 + node.declarator.args, 1.128 + return_type=node.base_type.name) 1.129 + else: # should not fall here ... 1.130 + assert False 1.131 + if signature: 1.132 + if Options.docstrings and node.options['embedsignature']: 1.133 + new_doc = self._embed_signature(signature, node.doc) 1.134 + node.doc = EncodedString(new_doc) # XXX 1.135 + return node
2.1 --- a/Cython/Compiler/Main.py Thu Sep 18 11:45:16 2008 +0200 2.2 +++ b/Cython/Compiler/Main.py Fri Sep 19 16:28:36 2008 +0200 2.3 @@ -80,6 +80,7 @@ 2.4 from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform 2.5 from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform 2.6 from ParseTreeTransforms import ResolveOptions 2.7 + from AutoDocTransforms import EmbedSignature 2.8 from Optimize import FlattenInListTransform, SwitchTransform, OptimizeRefcounting 2.9 from Buffer import IntroduceBufferAuxiliaryVars 2.10 from ModuleNode import check_c_classes 2.11 @@ -96,6 +97,7 @@ 2.12 PostParse(self), 2.13 _specific_post_parse, 2.14 ResolveOptions(self, self.pragma_overrides), 2.15 + EmbedSignature(self), 2.16 FlattenInListTransform(), 2.17 WithTransform(self), 2.18 DecoratorTransform(self),
3.1 --- a/Cython/Compiler/Options.py Thu Sep 18 11:45:16 2008 +0200 3.2 +++ b/Cython/Compiler/Options.py Fri Sep 19 16:28:36 2008 +0200 3.3 @@ -56,11 +56,13 @@ 3.4 3.5 # Declare pragmas 3.6 option_types = { 3.7 - 'boundscheck' : bool 3.8 + 'boundscheck' : bool, 3.9 + 'embedsignature' : bool, 3.10 } 3.11 3.12 option_defaults = { 3.13 - 'boundscheck' : True 3.14 + 'boundscheck' : True, 3.15 + 'embedsignature' : False, 3.16 } 3.17 3.18 def parse_option_value(name, value):
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/Tools/cython-epydoc.py Fri Sep 19 16:28:36 2008 +0200 4.3 @@ -0,0 +1,45 @@ 4.4 +#! /usr/bin/env python 4.5 + 4.6 +# -------------------------------------------------------------------- 4.7 + 4.8 +import re 4.9 +from epydoc import docstringparser as dsp 4.10 + 4.11 +CYTHON_SIGNATURE_RE = re.compile( 4.12 + # Class name (for builtin methods) 4.13 + r'^\s*((?P<class>\w+)\.)?' + 4.14 + # The function name 4.15 + r'(?P<func>\w+)' + 4.16 + # The parameters 4.17 + r'\(((?P<self>(?:self|cls|mcs)),?)?(?P<params>.*)\)' + 4.18 + # The return value (optional) 4.19 + r'(\s*(->)\s*(?P<return>\w+(?:\s*\w+)))?' + 4.20 + # The end marker 4.21 + r'\s*(?:\n|$)') 4.22 + 4.23 +parse_signature = dsp.parse_function_signature 4.24 + 4.25 +def parse_function_signature(func_doc, doc_source, 4.26 + docformat, parse_errors): 4.27 + PYTHON_SIGNATURE_RE = dsp._SIGNATURE_RE 4.28 + assert PYTHON_SIGNATURE_RE is not CYTHON_SIGNATURE_RE 4.29 + try: 4.30 + dsp._SIGNATURE_RE = CYTHON_SIGNATURE_RE 4.31 + found = parse_signature(func_doc, doc_source, 4.32 + docformat, parse_errors) 4.33 + dsp._SIGNATURE_RE = PYTHON_SIGNATURE_RE 4.34 + if not found: 4.35 + found = parse_signature(func_doc, doc_source, 4.36 + docformat, parse_errors) 4.37 + return found 4.38 + finally: 4.39 + dsp._SIGNATURE_RE = PYTHON_SIGNATURE_RE 4.40 + 4.41 +dsp.parse_function_signature = parse_function_signature 4.42 + 4.43 +# -------------------------------------------------------------------- 4.44 + 4.45 +from epydoc.cli import cli 4.46 +cli() 4.47 + 4.48 +# --------------------------------------------------------------------
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/tests/run/embedsignatures.pyx Fri Sep 19 16:28:36 2008 +0200 5.3 @@ -0,0 +1,107 @@ 5.4 +#cython: embedsignature=True 5.5 + 5.6 +# note the r, we use \n below 5.7 +__doc__ = ur""" 5.8 + >>> print (Ext.a.__doc__) 5.9 + Ext.a(self) 5.10 + 5.11 + >>> print (Ext.b.__doc__) 5.12 + Ext.b(self, a, b, c) 5.13 + 5.14 + >>> print (Ext.c.__doc__) 5.15 + Ext.c(self, a, b, c=1) 5.16 + 5.17 + >>> print (Ext.d.__doc__) 5.18 + Ext.d(self, a, b, *, c=88) 5.19 + 5.20 + >>> print (Ext.e.__doc__) 5.21 + Ext.e(self, a, b, c=88, **kwds) 5.22 + 5.23 + >>> print (Ext.f.__doc__) 5.24 + Ext.f(self, a, b, *, c, d=42) 5.25 + 5.26 + >>> print (Ext.g.__doc__) 5.27 + Ext.g(self, a, b, *, c, d=42, e=17, f, **kwds) 5.28 + 5.29 + >>> print (Ext.h.__doc__) 5.30 + Ext.h(self, a, b, *args, c, d=42, e=17, f, **kwds) 5.31 + 5.32 + >>> print (Ext.k.__doc__) 5.33 + Ext.k(self, a, b, c=1, *args, d=42, e=17, f, **kwds) 5.34 + 5.35 + >>> print (Ext.get_int.__doc__) 5.36 + Ext.get_int(self) -> int 5.37 + 5.38 + >>> print (Ext.get_float.__doc__) 5.39 + Ext.get_float(self) -> float 5.40 + 5.41 + >>> print (Ext.clone.__doc__) 5.42 + Ext.clone(self) -> Ext 5.43 + 5.44 + >>> print (foo.__doc__) 5.45 + foo() 5.46 + 5.47 + >>> with_doc_1.__doc__ 5.48 + 'with_doc_1(a, b, c)\nExisting string' 5.49 + 5.50 + >>> with_doc_2.__doc__ 5.51 + 'with_doc_2(a, b, c)\n\n Existing string\n ' 5.52 + 5.53 + >>> types.__doc__ 5.54 + 'types(Ext a, int b, int c, float d, e)' 5.55 + 5.56 +""" 5.57 + 5.58 +cdef class Ext: 5.59 + 5.60 + def a(self): 5.61 + pass 5.62 + 5.63 + def b(self, a, b, c): 5.64 + pass 5.65 + 5.66 + def c(self, a, b, c=1): 5.67 + pass 5.68 + 5.69 + def d(self, a, b, *, c = 88): 5.70 + pass 5.71 + 5.72 + def e(self, a, b, c = 88, **kwds): 5.73 + pass 5.74 + 5.75 + def f(self, a, b, *, c, d = 42): 5.76 + pass 5.77 + 5.78 + def g(self, a, b, *, c, d = 42, e = 17, f, **kwds): 5.79 + pass 5.80 + 5.81 + def h(self, a, b, *args, c, d = 42, e = 17, f, **kwds): 5.82 + pass 5.83 + 5.84 + def k(self, a, b, c=1, *args, d = 42, e = 17, f, **kwds): 5.85 + pass 5.86 + 5.87 + cpdef int get_int(self): 5.88 + return 0 5.89 + 5.90 + cpdef float get_float(self): 5.91 + return 0.0 5.92 + 5.93 + cpdef Ext clone(self): 5.94 + return Ext() 5.95 + 5.96 +def foo(): 5.97 + pass 5.98 + 5.99 +def types(Ext a, int b, unsigned short c, float d, e): 5.100 + pass 5.101 + 5.102 +def with_doc_1(a, b, c): 5.103 + """Existing string""" 5.104 + pass 5.105 + 5.106 +def with_doc_2(a, b, c): 5.107 + """ 5.108 + Existing string 5.109 + """ 5.110 + pass