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 _embed_signature(self, signature, node_doc): 143 if node_doc: 144 docfmt = self._select_format("%s\n%s", "%s\n--\n\n%s") 145 return docfmt % (signature, node_doc) 146 else: 147 return signature 148 149 def __call__(self, node): 150 if not Options.docstrings: 151 return node 152 else: 153 return super(EmbedSignature, self).__call__(node) 154 155 def visit_ClassDefNode(self, node): 156 oldname = self.class_name 157 oldclass = self.class_node 158 self.class_node = node 159 try: 160 # PyClassDefNode 161 self.class_name = node.name 162 except AttributeError: 163 # CClassDefNode 164 self.class_name = node.class_name 165 self.visitchildren(node) 166 self.class_name = oldname 167 self.class_node = oldclass 168 return node 169 170 def visit_LambdaNode(self, node): 171 # lambda expressions so not have signature or inner functions 172 return node 173 174 def visit_DefNode(self, node): 175 if not self.current_directives['embedsignature']: 176 return node 177 178 is_constructor = False 179 hide_self = False 180 if node.entry.is_special: 181 is_constructor = self.class_node and node.name == '__init__' 182 if not is_constructor: 183 return node 184 class_name, func_name = None, self.class_name 185 hide_self = True 186 else: 187 class_name, func_name = self.class_name, node.name 188 189 npoargs = getattr(node, 'num_posonly_args', 0) 190 nkargs = getattr(node, 'num_kwonly_args', 0) 191 npargs = len(node.args) - nkargs - npoargs 192 signature = self._fmt_signature( 193 class_name, func_name, node.args, 194 npoargs, npargs, node.star_arg, 195 nkargs, node.starstar_arg, 196 return_expr=node.return_type_annotation, 197 return_type=None, hide_self=hide_self) 198 if signature: 199 if is_constructor: 200 doc_holder = self.class_node.entry.type.scope 201 else: 202 doc_holder = node.entry 203 204 if doc_holder.doc is not None: 205 old_doc = doc_holder.doc 206 elif not is_constructor and getattr(node, 'py_func', None) is not None: 207 old_doc = node.py_func.entry.doc 208 else: 209 old_doc = None 210 new_doc = self._embed_signature(signature, old_doc) 211 doc_holder.doc = EncodedString(new_doc) 212 if not is_constructor and getattr(node, 'py_func', None) is not None: 213 node.py_func.entry.doc = EncodedString(new_doc) 214 return node 215 216 def visit_CFuncDefNode(self, node): 217 if not self.current_directives['embedsignature']: 218 return node 219 if not node.overridable: # not cpdef FOO(...): 220 return node 221 222 signature = self._fmt_signature( 223 self.class_name, node.declarator.base.name, 224 node.declarator.args, 225 return_type=node.return_type) 226 if signature: 227 if node.entry.doc is not None: 228 old_doc = node.entry.doc 229 elif getattr(node, 'py_func', None) is not None: 230 old_doc = node.py_func.entry.doc 231 else: 232 old_doc = None 233 new_doc = self._embed_signature(signature, old_doc) 234 node.entry.doc = EncodedString(new_doc) 235 py_func = getattr(node, 'py_func', None) 236 if py_func is not None: 237 py_func.entry.doc = EncodedString(new_doc) 238 return node 239 240 def visit_PropertyNode(self, node): 241 if not self.current_directives['embedsignature']: 242 return node 243 244 entry = node.entry 245 body = node.body 246 prop_name = entry.name 247 type_name = None 248 if entry.visibility == 'public': 249 # property synthesised from a cdef public attribute 250 type_name = entry.type.declaration_code("", for_display=1) 251 if not entry.type.is_pyobject: 252 type_name = "'%s'" % type_name 253 elif entry.type.is_extension_type: 254 type_name = entry.type.module_name + '.' + type_name 255 if type_name is None: 256 for stat in body.stats: 257 if stat.name != '__get__': 258 continue 259 cls_name = self.class_name 260 if cls_name: 261 namespace = self._select_format('%s.' % cls_name, '') 262 prop_name = '%s%s' % (namespace, prop_name) 263 ret_annotation = stat.return_type_annotation 264 if ret_annotation: 265 type_name = self._fmt_annotation(ret_annotation) 266 if type_name is not None: 267 signature = '%s: %s' % (prop_name, type_name) 268 new_doc = self._embed_signature(signature, entry.doc) 269 entry.doc = EncodedString(new_doc) 270 return node 271 272 273# Monkeypatch AutoDocTransforms.EmbedSignature 274try: 275 from Cython.Compiler import AutoDocTransforms 276 AutoDocTransforms.EmbedSignature = EmbedSignature 277except Exception as exc: 278 import logging 279 logging.Logger(__name__).exception(exc) 280 281# Monkeypatch Nodes.raise_utility_code 282try: 283 from Cython.Compiler.Nodes import raise_utility_code 284 code = raise_utility_code.impl 285 try: 286 ipos = code.index("if (tb) {\n#if CYTHON_COMPILING_IN_PYPY\n") 287 except ValueError: 288 ipos = None 289 else: 290 raise_utility_code.impl = code[:ipos] + code[ipos:].replace( 291 'CYTHON_COMPILING_IN_PYPY', '!CYTHON_FAST_THREAD_STATE', 1) 292 del raise_utility_code, code, ipos 293except Exception as exc: 294 import logging 295 logging.Logger(__name__).exception(exc) 296