Function/method signature embedding finally implemented. Currently,
this seems to work just fine for mpi4py and petsc4py. I would love
Stefan give a try in lxml, and Robert in sage. Fernando and Brian CC'd
(sorry if you get this twice!), as they could provide further comments
on this, especially regarding the way signatures are formatted. After
some rounds, perhaps IPython could be made even smarter at the time of
displaying info on the console.
Finally, some notes about the attached patch.
* All this needs to be properly documented (I hardly find spare time
for documenting my own projects!)
* A testcase was added, perhaps it needs to be extended.
* This do not play well with '--embed-positions' global option
* I did not handle any indentation issues.
* Pehaps the determination of the value for default arguments could be
made smarter.
* The epydoc monkeypatching in Tools/cython-epydoc.py only tested with
epydoc-3.0.1.
Enjoy!
--
Lisandro Dalcín
---------------
Centro Internacional de Métodos Computacionales en Ingeniería (CIMEC)
Instituto de Desarrollo Tecnológico para la Industria Química (INTEC)
Consejo Nacional de Investigaciones Científicas y Técnicas (CONICET)
PTLC - Güemes 3450, (3000) Santa Fe, Argentina
Tel/Fax: +54-(0)342-451.1594
diff -r 02eb1bab8afa Cython/Compiler/AutoDocTransforms.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Cython/Compiler/AutoDocTransforms.py Thu Sep 18 13:16:22 2008 -0300
@@ -0,0 +1,129 @@
+import re
+
+from Cython.Compiler.Visitor import CythonTransform
+from Cython.Compiler.Nodes import DefNode, CFuncDefNode
+from Cython.Compiler.Errors import CompileError
+from Cython.Compiler.StringEncoding import EncodedString
+from Cython.Compiler import Options
+
+
+class EmbedSignature(CythonTransform):
+
+ SPECIAL_METHOD_RE = re.compile(r'__\w+__')
+
+ def __init__(self, context):
+ super(EmbedSignature, self).__init__(context)
+ self.denv = None # XXX
+ self.is_in_class = False
+ self.class_name = None
+
+ def _fmt_arg_type(self, arg):
+ return arg.base_type.name
+
+ def _fmt_arg_name(self, arg):
+ try:
+ return arg.declarator.name
+ except AttributeError:
+ return arg.declarator.base.name
+
+ def _fmt_arg_defv(self, arg):
+ if not arg.default:
+ return None
+ try:
+ denv = self.denv # XXX
+ ctval = arg.default.compile_time_value(self.denv)
+ return '%s' % ctval
+ except Exception:
+ try:
+ return arg.default.name # XXX
+ except AttributeError:
+ return '<???>'
+
+ def _fmt_arg(self, arg):
+ arg_type = self._fmt_arg_type(arg)
+ arg_name = self._fmt_arg_name(arg)
+ arg_defv = self._fmt_arg_defv(arg)
+ doc = arg_name
+ if arg_type:
+ doc = ('%s ' % arg_type) + doc
+ if arg_defv:
+ doc = doc + ('=%s' % arg_defv)
+ return doc
+
+ def _fmt_arglist(self, args,
+ npargs=0, pargs=None,
+ nkargs=0, kargs=None):
+ arglist = []
+ for arg in args:
+ arg_doc = self._fmt_arg(arg)
+ arglist.append(arg_doc)
+ if pargs:
+ arglist.insert(npargs, '*%s' % pargs.name)
+ elif nkargs:
+ arglist.insert(npargs, '*')
+ if kargs:
+ arglist.append('**%s' % kargs.name)
+ return arglist
+
+ def _fmt_signature(self, cls_name, func_name, args,
+ npargs=0, pargs=None,
+ nkargs=0, kargs=None,
+ return_type=None):
+ arglist = self._fmt_arglist(args,
+ npargs, pargs,
+ nkargs, kargs)
+ arglist = ', '.join(arglist)
+ func_doc = '%s(%s)' % (func_name, arglist)
+ if cls_name:
+ func_doc = ('%s.' % cls_name) + func_doc
+ if return_type:
+ func_doc = func_doc + ' -> %s' % return_type
+ return func_doc
+
+ def _embed_signature(self, signature, node_doc):
+ if node_doc:
+ return signature + '\n' + node_doc
+ else:
+ return signature
+
+ def visit_ClassDefNode(self, node):
+ oldincls = self.is_in_class
+ oldname = self.class_name
+ self.is_in_class = True
+ try:
+ # PyClassDefNode
+ self.class_name = node.name
+ except AttributeError:
+ # CClassDefNode
+ self.class_name = node.class_name
+ self.visitchildren(node)
+ self.is_in_class = oldincls
+ self.class_name = oldname
+ return node
+
+ def visit_FuncDefNode(self, node):
+ signature = None
+ if type(node) is DefNode: # def FOO(...):
+ special_method = (self.is_in_class and \
+ self.SPECIAL_METHOD_RE.match(node.name))
+ if not special_method:
+ nkargs = getattr(node, 'num_kwonly_args', 0)
+ npargs = len(node.args) - nkargs
+ signature = self._fmt_signature(
+ self.class_name, node.name, node.args,
+ npargs, node.star_arg,
+ nkargs, node.starstar_arg,
+ return_type=None)
+ elif type(node) is CFuncDefNode:
+ if node.overridable: # cpdef FOO(...):
+ signature = self._fmt_signature(
+ self.class_name, node.declarator.base.name,
+ node.declarator.args,
+ return_type=node.base_type.name)
+ else: # should not fall here ...
+ assert False
+ if signature:
+ if Options.docstrings and node.options['embedsignature']:
+ new_doc = self._embed_signature(signature, node.doc)
+ node.doc = EncodedString(new_doc) # XXX
+ return node
diff -r 02eb1bab8afa Cython/Compiler/Main.py
--- a/Cython/Compiler/Main.py Thu Sep 18 11:45:16 2008 +0200
+++ b/Cython/Compiler/Main.py Thu Sep 18 12:45:31 2008 -0300
@@ -80,6 +80,7 @@ class Context:
from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform
from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
from ParseTreeTransforms import ResolveOptions
+ from AutoDocTransforms import EmbedSignature
from Optimize import FlattenInListTransform, SwitchTransform, OptimizeRefcounting
from Buffer import IntroduceBufferAuxiliaryVars
from ModuleNode import check_c_classes
@@ -96,6 +97,7 @@ class Context:
PostParse(self),
_specific_post_parse,
ResolveOptions(self, self.pragma_overrides),
+ EmbedSignature(self),
FlattenInListTransform(),
WithTransform(self),
DecoratorTransform(self),
diff -r 02eb1bab8afa Cython/Compiler/Options.py
--- a/Cython/Compiler/Options.py Thu Sep 18 11:45:16 2008 +0200
+++ b/Cython/Compiler/Options.py Thu Sep 18 12:49:05 2008 -0300
@@ -56,11 +56,13 @@ c_line_in_traceback = 1
# Declare pragmas
option_types = {
- 'boundscheck' : bool
+ 'boundscheck' : bool,
+ 'embedsignature' : bool,
}
option_defaults = {
- 'boundscheck' : True
+ 'boundscheck' : True,
+ 'embedsignature' : False,
}
def parse_option_value(name, value):
diff -r 02eb1bab8afa Tools/cython-epydoc.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Tools/cython-epydoc.py Thu Sep 18 18:07:37 2008 -0300
@@ -0,0 +1,44 @@
+#! /usr/bin/env python
+
+# --------------------------------------------------------------------
+
+import re
+from epydoc import docstringparser as dsp
+
+CYTHON_SIGNATURE_RE = re.compile(
+ # Class name (for builtin methods)
+ r'^\s*((?P<class>\w+)\.)?' +
+ # The function name
+ r'(?P<func>\w+)' +
+ # The parameters
+ r'\(((?P<self>(?:self|cls|mcs)),?)?(?P<params>.*)\)' +
+ # The return value (optional)
+ r'(\s*(->)\s*(?P<return>\w+(?:\s*\w+)))?' +
+ # The end marker
+ r'\s*(?:\n|$)')
+
+parse_signature = dsp.parse_function_signature
+
+def parse_function_signature(func_doc, doc_source,
+ docformat, parse_errors):
+ found = parse_signature(func_doc, doc_source,
+ docformat, parse_errors)
+ if not found:
+ PYTHON_SIGNATURE_RE = dsp._SIGNATURE_RE
+ assert PYTHON_SIGNATURE_RE is not CYTHON_SIGNATURE_RE
+ try:
+ dsp._SIGNATURE_RE = CYTHON_SIGNATURE_RE
+ found = parse_signature(func_doc, doc_source,
+ docformat, parse_errors)
+ finally:
+ dsp._SIGNATURE_RE = PYTHON_SIGNATURE_RE
+ return found
+
+dsp.parse_function_signature = parse_function_signature
+
+# --------------------------------------------------------------------
+
+from epydoc.cli import cli
+cli()
+
+# --------------------------------------------------------------------
diff -r 02eb1bab8afa tests/run/embedsignatures.pyx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/run/embedsignatures.pyx Thu Sep 18 16:50:23 2008 -0300
@@ -0,0 +1,84 @@
+#cython: embedsignature=True
+
+__doc__ = u"""
+ >>> print (Ext.a.__doc__)
+ Ext.a(self)
+
+ >>> print (Ext.b.__doc__)
+ Ext.b(self, a, b, c)
+
+ >>> print (Ext.c.__doc__)
+ Ext.c(self, a, b, c=1)
+
+ >>> print (Ext.d.__doc__)
+ Ext.d(self, a, b, *, c=88)
+
+ >>> print (Ext.e.__doc__)
+ Ext.e(self, a, b, c=88, **kwds)
+
+ >>> print (Ext.f.__doc__)
+ Ext.f(self, a, b, *, c, d=42)
+
+ >>> print (Ext.g.__doc__)
+ Ext.g(self, a, b, *, c, d=42, e=17, f, **kwds)
+
+ >>> print (Ext.h.__doc__)
+ Ext.h(self, a, b, *args, c, d=42, e=17, f, **kwds)
+
+ >>> print (Ext.k.__doc__)
+ Ext.k(self, a, b, c=1, *args, d=42, e=17, f, **kwds)
+
+ >>> print (Ext.get_int.__doc__)
+ Ext.get_int(self) -> int
+
+ >>> print (Ext.get_float.__doc__)
+ Ext.get_float(self) -> float
+
+ >>> print (Ext.clone.__doc__)
+ Ext.clone(self) -> Ext
+
+ >>> print (foo.__doc__)
+ foo()
+
+"""
+
+cdef class Ext:
+
+ def a(self):
+ pass
+
+ def b(self, a, b, c):
+ pass
+
+ def c(self, a, b, c=1):
+ pass
+
+ def d(self, a, b, *, c = 88):
+ pass
+
+ def e(self, a, b, c = 88, **kwds):
+ pass
+
+ def f(self, a, b, *, c, d = 42):
+ pass
+
+ def g(self, a, b, *, c, d = 42, e = 17, f, **kwds):
+ pass
+
+ def h(self, a, b, *args, c, d = 42, e = 17, f, **kwds):
+ pass
+
+ def k(self, a, b, c=1, *args, d = 42, e = 17, f, **kwds):
+ pass
+
+ cpdef int get_int(self):
+ return 0
+
+ cpdef float get_float(self):
+ return 0.0
+
+ cpdef Ext clone(self):
+ return Ext()
+
+def foo():
+ pass
_______________________________________________
Cython-dev mailing list
[email protected]
http://codespeak.net/mailman/listinfo/cython-dev