1 from Cython.Compiler.Visitor import CythonTransform 2 from Cython.Compiler.StringEncoding import EncodedString 3 from Cython.Compiler import Options 4 from Cython.Compiler import PyrexTypes, ExprNodes 5 6 class EmbedSignature(CythonTransform): 7 8 def __init__(self, context): 9 super(EmbedSignature, self).__init__(context) 10 self.denv = None # XXX 11 self.class_name = None 12 self.class_node = None 13 14 unop_precedence = 11 15 binop_precedence = { 16 'or': 1, 17 'and': 2, 18 'not': 3, 19 'in': 4, 'not in': 4, 'is': 4, 'is not': 4, '<': 4, '<=': 4, '>': 4, '>=': 4, '!=': 4, '==': 4, 20 '|': 5, 21 '^': 6, 22 '&': 7, 23 '<<': 8, '>>': 8, 24 '+': 9, '-': 9, 25 '*': 10, '/': 10, '//': 10, '%': 10, 26 # unary: '+': 11, '-': 11, '~': 11 27 '**': 12} 28 29 def _fmt_expr_node(self, node, precedence=0): 30 if isinstance(node, ExprNodes.BinopNode) and not node.inplace: 31 new_prec = self.binop_precedence.get(node.operator, 0) 32 result = '%s %s %s' % (self._fmt_expr_node(node.operand1, new_prec), 33 node.operator, 34 self._fmt_expr_node(node.operand2, new_prec)) 35 if precedence > new_prec: 36 result = '(%s)' % result 37 elif isinstance(node, ExprNodes.UnopNode): 38 result = '%s%s' % (node.operator, 39 self._fmt_expr_node(node.operand, self.unop_precedence)) 40 if precedence > self.unop_precedence: 41 result = '(%s)' % result 42 elif isinstance(node, ExprNodes.AttributeNode): 43 result = '%s.%s' % (self._fmt_expr_node(node.obj), node.attribute) 44 else: 45 result = node.name 46 return result 47 48 def _fmt_arg_defv(self, arg): 49 default_val = arg.default 50 if not default_val: 51 return None 52 try: 53 denv = self.denv # XXX 54 ctval = default_val.compile_time_value(self.denv) 55 repr_val = repr(ctval) 56 if isinstance(default_val, ExprNodes.UnicodeNode): 57 if repr_val[:1] != 'u': 58 return u'u%s' % repr_val 59 elif isinstance(default_val, ExprNodes.BytesNode): 60 if repr_val[:1] != 'b': 61 return u'b%s' % repr_val 62 elif isinstance(default_val, ExprNodes.StringNode): 63 if repr_val[:1] in 'ub': 64 return repr_val[1:] 65 return repr_val 66 except Exception: 67 try: 68 return self._fmt_expr_node(default_val) 69 except AttributeError, e: 70 return '<???>' 71 72 def _fmt_arg(self, arg): 73 if arg.type is PyrexTypes.py_object_type or arg.is_self_arg: 74 doc = arg.name 75 else: 76 doc = arg.type.declaration_code(arg.name, for_display=1) 77 if arg.default: 78 arg_defv = self._fmt_arg_defv(arg) 79 if arg_defv: 80 doc = doc + ('=%s' % arg_defv) 81 return doc 82 83 def _fmt_arglist(self, args, 84 npargs=0, pargs=None, 85 nkargs=0, kargs=None, 86 hide_self=False): 87 arglist = [] 88 for arg in args: 89 if not hide_self or not arg.entry.is_self_arg: 90 arg_doc = self._fmt_arg(arg) 91 arglist.append(arg_doc) 92 if pargs: 93 arglist.insert(npargs, '*%s' % pargs.name) 94 elif nkargs: 95 arglist.insert(npargs, '*') 96 if kargs: 97 arglist.append('**%s' % kargs.name) 98 return arglist 99 100 def _fmt_ret_type(self, ret): 101 if ret is PyrexTypes.py_object_type: 102 return None 103 else: 104 return ret.declaration_code("", for_display=1) 105 106 def _fmt_signature(self, cls_name, func_name, args, 107 npargs=0, pargs=None, 108 nkargs=0, kargs=None, 109 return_type=None, hide_self=False): 110 arglist = self._fmt_arglist(args, 111 npargs, pargs, 112 nkargs, kargs, 113 hide_self=hide_self) 114 arglist_doc = ', '.join(arglist) 115 func_doc = '%s(%s)' % (func_name, arglist_doc) 116 if cls_name: 117 func_doc = '%s.%s' % (cls_name, func_doc) 118 if return_type: 119 ret_doc = self._fmt_ret_type(return_type) 120 if ret_doc: 121 func_doc = '%s -> %s' % (func_doc, ret_doc) 122 return func_doc 123 124 def _embed_signature(self, signature, node_doc): 125 if node_doc: 126 return "%s\n%s" % (signature, node_doc) 127 else: 128 return signature 129 130 def __call__(self, node): 131 if not Options.docstrings: 132 return node 133 else: 134 return super(EmbedSignature, self).__call__(node) 135 136 def visit_ClassDefNode(self, node): 137 oldname = self.class_name 138 oldclass = self.class_node 139 self.class_node = node 140 try: 141 # PyClassDefNode 142 self.class_name = node.name 143 except AttributeError: 144 # CClassDefNode 145 self.class_name = node.class_name 146 self.visitchildren(node) 147 self.class_name = oldname 148 self.class_node = oldclass 149 return node 150 151 def visit_DefNode(self, node): 152 if not self.current_directives['embedsignature']: 153 return node 154 155 is_constructor = False 156 hide_self = False 157 if node.entry.is_special: 158 is_constructor = self.class_node and node.name == '__init__' 159 if not is_constructor: 160 return node 161 class_name, func_name = None, self.class_name 162 hide_self = True 163 else: 164 class_name, func_name = self.class_name, node.name 165 166 nkargs = getattr(node, 'num_kwonly_args', 0) 167 npargs = len(node.args) - nkargs 168 signature = self._fmt_signature( 169 class_name, func_name, node.args, 170 npargs, node.star_arg, 171 nkargs, node.starstar_arg, 172 return_type=None, hide_self=hide_self) 173 if signature: 174 if is_constructor: 175 doc_holder = self.class_node.entry.type.scope 176 else: 177 doc_holder = node.entry 178 179 if doc_holder.doc is not None: 180 old_doc = doc_holder.doc 181 elif not is_constructor and getattr(node, 'py_func', None) is not None: 182 old_doc = node.py_func.entry.doc 183 else: 184 old_doc = None 185 new_doc = self._embed_signature(signature, old_doc) 186 doc_holder.doc = EncodedString(new_doc) 187 if not is_constructor and getattr(node, 'py_func', None) is not None: 188 node.py_func.entry.doc = EncodedString(new_doc) 189 return node 190 191 def visit_CFuncDefNode(self, node): 192 if not self.current_directives['embedsignature']: 193 return node 194 if not node.overridable: # not cpdef FOO(...): 195 return node 196 197 signature = self._fmt_signature( 198 self.class_name, node.declarator.base.name, 199 node.declarator.args, 200 return_type=node.return_type) 201 if signature: 202 if node.entry.doc is not None: 203 old_doc = node.entry.doc 204 elif getattr(node, 'py_func', None) is not None: 205 old_doc = node.py_func.entry.doc 206 else: 207 old_doc = None 208 new_doc = self._embed_signature(signature, old_doc) 209 node.entry.doc = EncodedString(new_doc) 210 if hasattr(node, 'py_func') and node.py_func is not None: 211 node.py_func.entry.doc = EncodedString(new_doc) 212 return node 213 214 def visit_PropertyNode(self, node): 215 if not self.current_directives['embedsignature']: 216 return node 217 218 entry = node.entry 219 if entry.visibility == 'public': 220 # property synthesised from a cdef public attribute 221 type_name = entry.type.declaration_code("", for_display=1) 222 if not entry.type.is_pyobject: 223 type_name = "'%s'" % type_name 224 elif entry.type.is_extension_type: 225 type_name = entry.type.module_name + '.' + type_name 226 signature = '%s: %s' % (entry.name, type_name) 227 new_doc = self._embed_signature(signature, entry.doc) 228 entry.doc = EncodedString(new_doc) 229 return node 230