AutoDocTransforms.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. from __future__ import absolute_import, print_function
  2. from .Visitor import CythonTransform
  3. from .StringEncoding import EncodedString
  4. from . import Options
  5. from . import PyrexTypes, ExprNodes
  6. from ..CodeWriter import ExpressionWriter
  7. class AnnotationWriter(ExpressionWriter):
  8. def visit_Node(self, node):
  9. self.put(u"<???>")
  10. def visit_LambdaNode(self, node):
  11. # XXX Should we do better?
  12. self.put("<lambda>")
  13. class EmbedSignature(CythonTransform):
  14. def __init__(self, context):
  15. super(EmbedSignature, self).__init__(context)
  16. self.class_name = None
  17. self.class_node = None
  18. def _fmt_expr(self, node):
  19. writer = AnnotationWriter()
  20. result = writer.write(node)
  21. # print(type(node).__name__, '-->', result)
  22. return result
  23. def _fmt_arg(self, arg):
  24. if arg.type is PyrexTypes.py_object_type or arg.is_self_arg:
  25. doc = arg.name
  26. else:
  27. doc = arg.type.declaration_code(arg.name, for_display=1)
  28. if arg.annotation:
  29. annotation = self._fmt_expr(arg.annotation)
  30. doc = doc + (': %s' % annotation)
  31. if arg.default:
  32. default = self._fmt_expr(arg.default)
  33. doc = doc + (' = %s' % default)
  34. elif arg.default:
  35. default = self._fmt_expr(arg.default)
  36. doc = doc + ('=%s' % default)
  37. return doc
  38. def _fmt_star_arg(self, arg):
  39. arg_doc = arg.name
  40. if arg.annotation:
  41. annotation = self._fmt_expr(arg.annotation)
  42. arg_doc = arg_doc + (': %s' % annotation)
  43. return arg_doc
  44. def _fmt_arglist(self, args,
  45. npargs=0, pargs=None,
  46. nkargs=0, kargs=None,
  47. hide_self=False):
  48. arglist = []
  49. for arg in args:
  50. if not hide_self or not arg.entry.is_self_arg:
  51. arg_doc = self._fmt_arg(arg)
  52. arglist.append(arg_doc)
  53. if pargs:
  54. arg_doc = self._fmt_star_arg(pargs)
  55. arglist.insert(npargs, '*%s' % arg_doc)
  56. elif nkargs:
  57. arglist.insert(npargs, '*')
  58. if kargs:
  59. arg_doc = self._fmt_star_arg(kargs)
  60. arglist.append('**%s' % arg_doc)
  61. return arglist
  62. def _fmt_ret_type(self, ret):
  63. if ret is PyrexTypes.py_object_type:
  64. return None
  65. else:
  66. return ret.declaration_code("", for_display=1)
  67. def _fmt_signature(self, cls_name, func_name, args,
  68. npargs=0, pargs=None,
  69. nkargs=0, kargs=None,
  70. return_expr=None,
  71. return_type=None, hide_self=False):
  72. arglist = self._fmt_arglist(args,
  73. npargs, pargs,
  74. nkargs, kargs,
  75. hide_self=hide_self)
  76. arglist_doc = ', '.join(arglist)
  77. func_doc = '%s(%s)' % (func_name, arglist_doc)
  78. if cls_name:
  79. func_doc = '%s.%s' % (cls_name, func_doc)
  80. ret_doc = None
  81. if return_expr:
  82. ret_doc = self._fmt_expr(return_expr)
  83. elif return_type:
  84. ret_doc = self._fmt_ret_type(return_type)
  85. if ret_doc:
  86. func_doc = '%s -> %s' % (func_doc, ret_doc)
  87. return func_doc
  88. def _embed_signature(self, signature, node_doc):
  89. if node_doc:
  90. return "%s\n%s" % (signature, node_doc)
  91. else:
  92. return signature
  93. def __call__(self, node):
  94. if not Options.docstrings:
  95. return node
  96. else:
  97. return super(EmbedSignature, self).__call__(node)
  98. def visit_ClassDefNode(self, node):
  99. oldname = self.class_name
  100. oldclass = self.class_node
  101. self.class_node = node
  102. try:
  103. # PyClassDefNode
  104. self.class_name = node.name
  105. except AttributeError:
  106. # CClassDefNode
  107. self.class_name = node.class_name
  108. self.visitchildren(node)
  109. self.class_name = oldname
  110. self.class_node = oldclass
  111. return node
  112. def visit_LambdaNode(self, node):
  113. # lambda expressions so not have signature or inner functions
  114. return node
  115. def visit_DefNode(self, node):
  116. if not self.current_directives['embedsignature']:
  117. return node
  118. is_constructor = False
  119. hide_self = False
  120. if node.entry.is_special:
  121. is_constructor = self.class_node and node.name == '__init__'
  122. if not is_constructor:
  123. return node
  124. class_name, func_name = None, self.class_name
  125. hide_self = True
  126. else:
  127. class_name, func_name = self.class_name, node.name
  128. nkargs = getattr(node, 'num_kwonly_args', 0)
  129. npargs = len(node.args) - nkargs
  130. signature = self._fmt_signature(
  131. class_name, func_name, node.args,
  132. npargs, node.star_arg,
  133. nkargs, node.starstar_arg,
  134. return_expr=node.return_type_annotation,
  135. return_type=None, hide_self=hide_self)
  136. if signature:
  137. if is_constructor:
  138. doc_holder = self.class_node.entry.type.scope
  139. else:
  140. doc_holder = node.entry
  141. if doc_holder.doc is not None:
  142. old_doc = doc_holder.doc
  143. elif not is_constructor and getattr(node, 'py_func', None) is not None:
  144. old_doc = node.py_func.entry.doc
  145. else:
  146. old_doc = None
  147. new_doc = self._embed_signature(signature, old_doc)
  148. doc_holder.doc = EncodedString(new_doc)
  149. if not is_constructor and getattr(node, 'py_func', None) is not None:
  150. node.py_func.entry.doc = EncodedString(new_doc)
  151. return node
  152. def visit_CFuncDefNode(self, node):
  153. if not self.current_directives['embedsignature']:
  154. return node
  155. if not node.overridable: # not cpdef FOO(...):
  156. return node
  157. signature = self._fmt_signature(
  158. self.class_name, node.declarator.base.name,
  159. node.declarator.args,
  160. return_type=node.return_type)
  161. if signature:
  162. if node.entry.doc is not None:
  163. old_doc = node.entry.doc
  164. elif getattr(node, 'py_func', None) is not None:
  165. old_doc = node.py_func.entry.doc
  166. else:
  167. old_doc = None
  168. new_doc = self._embed_signature(signature, old_doc)
  169. node.entry.doc = EncodedString(new_doc)
  170. if hasattr(node, 'py_func') and node.py_func is not None:
  171. node.py_func.entry.doc = EncodedString(new_doc)
  172. return node
  173. def visit_PropertyNode(self, node):
  174. if not self.current_directives['embedsignature']:
  175. return node
  176. entry = node.entry
  177. if entry.visibility == 'public':
  178. # property synthesised from a cdef public attribute
  179. type_name = entry.type.declaration_code("", for_display=1)
  180. if not entry.type.is_pyobject:
  181. type_name = "'%s'" % type_name
  182. elif entry.type.is_extension_type:
  183. type_name = entry.type.module_name + '.' + type_name
  184. signature = '%s: %s' % (entry.name, type_name)
  185. new_doc = self._embed_signature(signature, entry.doc)
  186. entry.doc = EncodedString(new_doc)
  187. return node