1# ruff: noqa: UP008,UP031 2from Cython.Compiler import Options 3from Cython.Compiler import PyrexTypes 4from Cython.Compiler.ExprNodes import TupleNode 5from Cython.Compiler.Visitor import CythonTransform 6from Cython.Compiler.StringEncoding import EncodedString 7from Cython.Compiler.AutoDocTransforms import ( 8 ExpressionWriter as BaseExpressionWriter, 9 AnnotationWriter as BaseAnnotationWriter, 10) 11 12 13class ExpressionWriter(BaseExpressionWriter): 14 15 def visit_IndexNode(self, node): 16 self.visit(node.base) 17 self.put(u"[") 18 if isinstance(node.index, TupleNode): 19 if node.index.subexpr_nodes(): 20 self.emit_sequence(node.index) 21 else: 22 self.put(u"()") 23 else: 24 self.visit(node.index) 25 self.put(u"]") 26 27 def visit_UnicodeNode(self, node): 28 self.emit_string(node, "") 29 30 31class AnnotationWriter(ExpressionWriter, BaseAnnotationWriter): 32 pass 33 34 35class EmbedSignature(CythonTransform): 36 37 def __init__(self, context): 38 super(EmbedSignature, self).__init__(context) 39 self.class_name = None 40 self.class_node = None 41 42 def _select_format(self, embed, clinic): 43 return embed 44 45 def _fmt_expr(self, node): 46 writer = ExpressionWriter() 47 result = writer.write(node) 48 # print(type(node).__name__, '-->', result) 49 return result 50 51 def _fmt_annotation(self, node): 52 writer = AnnotationWriter() 53 result = writer.write(node) 54 # print(type(node).__name__, '-->', result) 55 return result 56 57 def _fmt_arg(self, arg): 58 annotation = None 59 if arg.is_self_arg: 60 doc = self._select_format(arg.name, '$self') 61 elif arg.is_type_arg: 62 doc = self._select_format(arg.name, '$type') 63 else: 64 doc = arg.name 65 if arg.type is PyrexTypes.py_object_type: 66 annotation = None 67 else: 68 annotation = arg.type.declaration_code('', for_display=1) 69 if arg.default and arg.default.is_none: 70 annotation += ' | None' 71 if arg.annotation: 72 annotation = self._fmt_annotation(arg.annotation) 73 annotation = self._select_format(annotation, None) 74 if annotation: 75 doc = doc + (': %s' % annotation) 76 if arg.default: 77 default = self._fmt_expr(arg.default) 78 doc = doc + (' = %s' % default) 79 elif arg.default: 80 default = self._fmt_expr(arg.default) 81 doc = doc + ('=%s' % default) 82 return doc 83 84 def _fmt_star_arg(self, arg): 85 arg_doc = arg.name 86 if arg.annotation: 87 annotation = self._fmt_annotation(arg.annotation) 88 arg_doc = arg_doc + (': %s' % annotation) 89 return arg_doc 90 91 def _fmt_arglist(self, args, 92 npoargs=0, npargs=0, pargs=None, 93 nkargs=0, kargs=None, 94 hide_self=False): 95 arglist = [] 96 for arg in args: 97 if not hide_self or not arg.entry.is_self_arg: 98 arg_doc = self._fmt_arg(arg) 99 arglist.append(arg_doc) 100 if pargs: 101 arg_doc = self._fmt_star_arg(pargs) 102 arglist.insert(npargs + npoargs, '*%s' % arg_doc) 103 elif nkargs: 104 arglist.insert(npargs + npoargs, '*') 105 if npoargs: 106 arglist.insert(npoargs, '/') 107 if kargs: 108 arg_doc = self._fmt_star_arg(kargs) 109 arglist.append('**%s' % arg_doc) 110 return arglist 111 112 def _fmt_ret_type(self, ret): 113 if ret is PyrexTypes.py_object_type: 114 return None 115 else: 116 return ret.declaration_code("", for_display=1) 117 118 def _fmt_signature(self, cls_name, func_name, args, 119 npoargs=0, npargs=0, pargs=None, 120 nkargs=0, kargs=None, 121 return_expr=None, 122 return_type=None, hide_self=False): 123 arglist = self._fmt_arglist(args, 124 npoargs, npargs, pargs, 125 nkargs, kargs, 126 hide_self=hide_self) 127 arglist_doc = ', '.join(arglist) 128 func_doc = '%s(%s)' % (func_name, arglist_doc) 129 if cls_name: 130 namespace = self._select_format('%s.' % cls_name, '') 131 func_doc = '%s%s' % (namespace, func_doc) 132 ret_doc = None 133 if return_expr: 134 ret_doc = self._fmt_annotation(return_expr) 135 elif return_type: 136 ret_doc = self._fmt_ret_type(return_type) 137 if ret_doc: 138 docfmt = self._select_format('%s -> %s', '%s -> (%s)') 139 func_doc = docfmt % (func_doc, ret_doc) 140 return func_doc 141 142 def _fmt_relative_position(self, pos): 143 return 'Source code at ' + ':'.join((str(pos[0].get_filenametable_entry()), str(pos[1]))) 144 145 def _embed_signature(self, signature, pos, node_doc): 146 pos = self._fmt_relative_position(pos) 147 if node_doc: 148 docfmt = self._select_format("%s\n%s\n%s", "%s\n--\n\n%s") 149 return docfmt % (signature, node_doc, pos) 150 else: 151 docfmt = self._select_format("%s\n%s", "%s\n--\n\n%s") 152 return docfmt % (signature, pos) 153 154 def __call__(self, node): 155 if not Options.docstrings: 156 return node 157 else: 158 return super(EmbedSignature, self).__call__(node) 159 160 def visit_ClassDefNode(self, node): 161 oldname = self.class_name 162 oldclass = self.class_node 163 self.class_node = node 164 try: 165 # PyClassDefNode 166 self.class_name = node.name 167 except AttributeError: 168 # CClassDefNode 169 self.class_name = node.class_name 170 self.visitchildren(node) 171 self.class_name = oldname 172 self.class_node = oldclass 173 return node 174 175 def visit_LambdaNode(self, node): 176 # lambda expressions so not have signature or inner functions 177 return node 178 179 def visit_DefNode(self, node): 180 if not self.current_directives['embedsignature']: 181 return node 182 183 is_constructor = False 184 hide_self = False 185 if node.entry.is_special: 186 is_constructor = self.class_node and node.name == '__init__' 187 if not is_constructor: 188 return node 189 class_name, func_name = None, self.class_name 190 hide_self = True 191 else: 192 class_name, func_name = self.class_name, node.name 193 194 npoargs = getattr(node, 'num_posonly_args', 0) 195 nkargs = getattr(node, 'num_kwonly_args', 0) 196 npargs = len(node.args) - nkargs - npoargs 197 signature = self._fmt_signature( 198 class_name, func_name, node.args, 199 npoargs, npargs, node.star_arg, 200 nkargs, node.starstar_arg, 201 return_expr=node.return_type_annotation, 202 return_type=None, hide_self=hide_self) 203 if signature: 204 if is_constructor: 205 doc_holder = self.class_node.entry.type.scope 206 else: 207 doc_holder = node.entry 208 209 if doc_holder.doc is not None: 210 old_doc = doc_holder.doc 211 elif not is_constructor and getattr(node, 'py_func', None) is not None: 212 old_doc = node.py_func.entry.doc 213 else: 214 old_doc = None 215 new_doc = self._embed_signature(signature, node.pos, old_doc) 216 doc_holder.doc = EncodedString(new_doc) 217 if not is_constructor and getattr(node, 'py_func', None) is not None: 218 node.py_func.entry.doc = EncodedString(new_doc) 219 return node 220 221 def visit_CFuncDefNode(self, node): 222 if not self.current_directives['embedsignature']: 223 return node 224 if not node.overridable: # not cpdef FOO(...): 225 return node 226 227 signature = self._fmt_signature( 228 self.class_name, node.declarator.base.name, 229 node.declarator.args, 230 return_type=node.return_type) 231 if signature: 232 if node.entry.doc is not None: 233 old_doc = node.entry.doc 234 elif getattr(node, 'py_func', None) is not None: 235 old_doc = node.py_func.entry.doc 236 else: 237 old_doc = None 238 new_doc = self._embed_signature(signature, node.pos, old_doc) 239 node.entry.doc = EncodedString(new_doc) 240 py_func = getattr(node, 'py_func', None) 241 if py_func is not None: 242 py_func.entry.doc = EncodedString(new_doc) 243 return node 244 245 def visit_PropertyNode(self, node): 246 if not self.current_directives['embedsignature']: 247 return node 248 249 entry = node.entry 250 body = node.body 251 prop_name = entry.name 252 type_name = None 253 if entry.visibility == 'public': 254 # property synthesised from a cdef public attribute 255 type_name = entry.type.declaration_code("", for_display=1) 256 if not entry.type.is_pyobject: 257 type_name = "'%s'" % type_name 258 elif entry.type.is_extension_type: 259 type_name = entry.type.module_name + '.' + type_name 260 if type_name is None: 261 for stat in body.stats: 262 if stat.name != '__get__': 263 continue 264 cls_name = self.class_name 265 if cls_name: 266 namespace = self._select_format('%s.' % cls_name, '') 267 prop_name = '%s%s' % (namespace, prop_name) 268 ret_annotation = stat.return_type_annotation 269 if ret_annotation: 270 type_name = self._fmt_annotation(ret_annotation) 271 if type_name is not None: 272 signature = '%s: %s' % (prop_name, type_name) 273 new_doc = self._embed_signature(signature, node.pos, entry.doc) 274 entry.doc = EncodedString(new_doc) 275 return node 276 277 278# Monkeypatch AutoDocTransforms.EmbedSignature 279try: 280 from Cython.Compiler import AutoDocTransforms 281 AutoDocTransforms.EmbedSignature = EmbedSignature 282except Exception as exc: 283 import logging 284 logging.Logger(__name__).exception(exc) 285 286# Monkeypatch Nodes.raise_utility_code 287try: 288 from Cython.Compiler.Nodes import raise_utility_code 289 code = raise_utility_code.impl 290 try: 291 ipos = code.index("if (tb) {\n#if CYTHON_COMPILING_IN_PYPY\n") 292 except ValueError: 293 ipos = None 294 else: 295 raise_utility_code.impl = code[:ipos] + code[ipos:].replace( 296 'CYTHON_COMPILING_IN_PYPY', '!CYTHON_FAST_THREAD_STATE', 1) 297 del raise_utility_code, code, ipos 298except Exception as exc: 299 import logging 300 logging.Logger(__name__).exception(exc) 301