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