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