155a74a43SLisandro Dalcin# ruff: noqa: UP008,UP031 255a74a43SLisandro Dalcinfrom Cython.Compiler import Options 355a74a43SLisandro Dalcinfrom Cython.Compiler import PyrexTypes 455a74a43SLisandro Dalcinfrom Cython.Compiler.ExprNodes import TupleNode 555a74a43SLisandro Dalcinfrom Cython.Compiler.Visitor import CythonTransform 655a74a43SLisandro Dalcinfrom Cython.Compiler.StringEncoding import EncodedString 755a74a43SLisandro Dalcinfrom Cython.Compiler.AutoDocTransforms import ( 855a74a43SLisandro Dalcin ExpressionWriter as BaseExpressionWriter, 955a74a43SLisandro Dalcin AnnotationWriter as BaseAnnotationWriter, 1055a74a43SLisandro Dalcin) 11*6f336411SStefano Zampinifrom Cython.Compiler.Errors import error 1255a74a43SLisandro Dalcin 1355a74a43SLisandro Dalcin 1455a74a43SLisandro Dalcinclass ExpressionWriter(BaseExpressionWriter): 1555a74a43SLisandro Dalcin def visit_IndexNode(self, node): 1655a74a43SLisandro Dalcin self.visit(node.base) 17*6f336411SStefano Zampini self.put('[') 1855a74a43SLisandro Dalcin if isinstance(node.index, TupleNode): 1955a74a43SLisandro Dalcin if node.index.subexpr_nodes(): 2055a74a43SLisandro Dalcin self.emit_sequence(node.index) 2155a74a43SLisandro Dalcin else: 22*6f336411SStefano Zampini self.put('()') 2355a74a43SLisandro Dalcin else: 2455a74a43SLisandro Dalcin self.visit(node.index) 25*6f336411SStefano Zampini self.put(']') 2655a74a43SLisandro Dalcin 2755a74a43SLisandro Dalcin def visit_UnicodeNode(self, node): 28*6f336411SStefano Zampini self.emit_string(node, '') 2955a74a43SLisandro Dalcin 3055a74a43SLisandro Dalcin 3155a74a43SLisandro Dalcinclass AnnotationWriter(ExpressionWriter, BaseAnnotationWriter): 3255a74a43SLisandro Dalcin pass 3355a74a43SLisandro Dalcin 3455a74a43SLisandro Dalcin 3555a74a43SLisandro Dalcinclass EmbedSignature(CythonTransform): 3655a74a43SLisandro Dalcin def __init__(self, context): 3755a74a43SLisandro Dalcin super(EmbedSignature, self).__init__(context) 3855a74a43SLisandro Dalcin self.class_name = None 3955a74a43SLisandro Dalcin self.class_node = None 4055a74a43SLisandro Dalcin 4155a74a43SLisandro Dalcin def _select_format(self, embed, clinic): 4255a74a43SLisandro Dalcin return embed 4355a74a43SLisandro Dalcin 4455a74a43SLisandro Dalcin def _fmt_expr(self, node): 4555a74a43SLisandro Dalcin writer = ExpressionWriter() 46*6f336411SStefano Zampini return writer.write(node) 4755a74a43SLisandro Dalcin 4855a74a43SLisandro Dalcin def _fmt_annotation(self, node): 4955a74a43SLisandro Dalcin writer = AnnotationWriter() 50*6f336411SStefano Zampini return writer.write(node) 5155a74a43SLisandro Dalcin 5255a74a43SLisandro Dalcin def _fmt_arg(self, arg): 5355a74a43SLisandro Dalcin annotation = None 5455a74a43SLisandro Dalcin if arg.is_self_arg: 5555a74a43SLisandro Dalcin doc = self._select_format(arg.name, '$self') 5655a74a43SLisandro Dalcin elif arg.is_type_arg: 5755a74a43SLisandro Dalcin doc = self._select_format(arg.name, '$type') 5855a74a43SLisandro Dalcin else: 5955a74a43SLisandro Dalcin doc = arg.name 6055a74a43SLisandro Dalcin if arg.type is PyrexTypes.py_object_type: 6155a74a43SLisandro Dalcin annotation = None 6255a74a43SLisandro Dalcin else: 6355a74a43SLisandro Dalcin annotation = arg.type.declaration_code('', for_display=1) 6455a74a43SLisandro Dalcin if arg.default and arg.default.is_none: 6555a74a43SLisandro Dalcin annotation += ' | None' 6655a74a43SLisandro Dalcin if arg.annotation: 6755a74a43SLisandro Dalcin annotation = self._fmt_annotation(arg.annotation) 6855a74a43SLisandro Dalcin annotation = self._select_format(annotation, None) 6955a74a43SLisandro Dalcin if annotation: 7055a74a43SLisandro Dalcin doc = doc + (': %s' % annotation) 7155a74a43SLisandro Dalcin if arg.default: 7255a74a43SLisandro Dalcin default = self._fmt_expr(arg.default) 7355a74a43SLisandro Dalcin doc = doc + (' = %s' % default) 7455a74a43SLisandro Dalcin elif arg.default: 7555a74a43SLisandro Dalcin default = self._fmt_expr(arg.default) 7655a74a43SLisandro Dalcin doc = doc + ('=%s' % default) 7755a74a43SLisandro Dalcin return doc 7855a74a43SLisandro Dalcin 7955a74a43SLisandro Dalcin def _fmt_star_arg(self, arg): 8055a74a43SLisandro Dalcin arg_doc = arg.name 8155a74a43SLisandro Dalcin if arg.annotation: 8255a74a43SLisandro Dalcin annotation = self._fmt_annotation(arg.annotation) 8355a74a43SLisandro Dalcin arg_doc = arg_doc + (': %s' % annotation) 8455a74a43SLisandro Dalcin return arg_doc 8555a74a43SLisandro Dalcin 86*6f336411SStefano Zampini def _fmt_arglist( 87*6f336411SStefano Zampini self, 88*6f336411SStefano Zampini args, 89*6f336411SStefano Zampini npoargs=0, 90*6f336411SStefano Zampini npargs=0, 91*6f336411SStefano Zampini pargs=None, 92*6f336411SStefano Zampini nkargs=0, 93*6f336411SStefano Zampini kargs=None, 94*6f336411SStefano Zampini hide_self=False, 95*6f336411SStefano Zampini ): 9655a74a43SLisandro Dalcin arglist = [] 9755a74a43SLisandro Dalcin for arg in args: 9855a74a43SLisandro Dalcin if not hide_self or not arg.entry.is_self_arg: 9955a74a43SLisandro Dalcin arg_doc = self._fmt_arg(arg) 10055a74a43SLisandro Dalcin arglist.append(arg_doc) 10155a74a43SLisandro Dalcin if pargs: 10255a74a43SLisandro Dalcin arg_doc = self._fmt_star_arg(pargs) 10355a74a43SLisandro Dalcin arglist.insert(npargs + npoargs, '*%s' % arg_doc) 10455a74a43SLisandro Dalcin elif nkargs: 10555a74a43SLisandro Dalcin arglist.insert(npargs + npoargs, '*') 10655a74a43SLisandro Dalcin if npoargs: 10755a74a43SLisandro Dalcin arglist.insert(npoargs, '/') 10855a74a43SLisandro Dalcin if kargs: 10955a74a43SLisandro Dalcin arg_doc = self._fmt_star_arg(kargs) 11055a74a43SLisandro Dalcin arglist.append('**%s' % arg_doc) 11155a74a43SLisandro Dalcin return arglist 11255a74a43SLisandro Dalcin 11355a74a43SLisandro Dalcin def _fmt_ret_type(self, ret): 11455a74a43SLisandro Dalcin if ret is PyrexTypes.py_object_type: 11555a74a43SLisandro Dalcin return None 116*6f336411SStefano Zampini return ret.declaration_code('', for_display=1) 11755a74a43SLisandro Dalcin 118*6f336411SStefano Zampini def _fmt_signature( 119*6f336411SStefano Zampini self, 120*6f336411SStefano Zampini node, 121*6f336411SStefano Zampini cls_name, 122*6f336411SStefano Zampini func_name, 123*6f336411SStefano Zampini args, 124*6f336411SStefano Zampini npoargs=0, 125*6f336411SStefano Zampini npargs=0, 126*6f336411SStefano Zampini pargs=None, 127*6f336411SStefano Zampini nkargs=0, 128*6f336411SStefano Zampini kargs=None, 12955a74a43SLisandro Dalcin return_expr=None, 130*6f336411SStefano Zampini return_type=None, 131*6f336411SStefano Zampini hide_self=False, 132*6f336411SStefano Zampini ): 133*6f336411SStefano Zampini arglist = self._fmt_arglist( 134*6f336411SStefano Zampini args, npoargs, npargs, pargs, nkargs, kargs, hide_self=hide_self 135*6f336411SStefano Zampini ) 13655a74a43SLisandro Dalcin arglist_doc = ', '.join(arglist) 13755a74a43SLisandro Dalcin func_doc = '%s(%s)' % (func_name, arglist_doc) 13855a74a43SLisandro Dalcin if cls_name: 13955a74a43SLisandro Dalcin namespace = self._select_format('%s.' % cls_name, '') 14055a74a43SLisandro Dalcin func_doc = '%s%s' % (namespace, func_doc) 14155a74a43SLisandro Dalcin ret_doc = None 14255a74a43SLisandro Dalcin if return_expr: 14355a74a43SLisandro Dalcin ret_doc = self._fmt_annotation(return_expr) 14455a74a43SLisandro Dalcin elif return_type: 14555a74a43SLisandro Dalcin ret_doc = self._fmt_ret_type(return_type) 14655a74a43SLisandro Dalcin if ret_doc: 14755a74a43SLisandro Dalcin docfmt = self._select_format('%s -> %s', '%s -> (%s)') 14855a74a43SLisandro Dalcin func_doc = docfmt % (func_doc, ret_doc) 149*6f336411SStefano Zampini else: 150*6f336411SStefano Zampini if not func_doc.startswith('_') and not func_name.startswith('_'): 151*6f336411SStefano Zampini error( 152*6f336411SStefano Zampini node.pos, 153*6f336411SStefano Zampini f'cyautodoc._fmt_signature: missing return type for {func_doc}', 154*6f336411SStefano Zampini ) 15555a74a43SLisandro Dalcin return func_doc 15655a74a43SLisandro Dalcin 157b3f8c7a1SStefano Zampini def _fmt_relative_position(self, pos): 158*6f336411SStefano Zampini return 'Source code at ' + ':'.join( 159*6f336411SStefano Zampini (str(pos[0].get_filenametable_entry()), str(pos[1])) 160*6f336411SStefano Zampini ) 161b3f8c7a1SStefano Zampini 162b3f8c7a1SStefano Zampini def _embed_signature(self, signature, pos, node_doc): 163b3f8c7a1SStefano Zampini pos = self._fmt_relative_position(pos) 16455a74a43SLisandro Dalcin if node_doc: 165*6f336411SStefano Zampini docfmt = self._select_format('%s\n%s\n%s', '%s\n--\n\n%s') 166b3f8c7a1SStefano Zampini return docfmt % (signature, node_doc, pos) 167*6f336411SStefano Zampini docfmt = self._select_format('%s\n%s', '%s\n--\n\n%s') 168b3f8c7a1SStefano Zampini return docfmt % (signature, pos) 16955a74a43SLisandro Dalcin 17055a74a43SLisandro Dalcin def __call__(self, node): 17155a74a43SLisandro Dalcin if not Options.docstrings: 17255a74a43SLisandro Dalcin return node 17355a74a43SLisandro Dalcin return super(EmbedSignature, self).__call__(node) 17455a74a43SLisandro Dalcin 17555a74a43SLisandro Dalcin def visit_ClassDefNode(self, node): 17655a74a43SLisandro Dalcin oldname = self.class_name 17755a74a43SLisandro Dalcin oldclass = self.class_node 17855a74a43SLisandro Dalcin self.class_node = node 17955a74a43SLisandro Dalcin try: 18055a74a43SLisandro Dalcin # PyClassDefNode 18155a74a43SLisandro Dalcin self.class_name = node.name 18255a74a43SLisandro Dalcin except AttributeError: 18355a74a43SLisandro Dalcin # CClassDefNode 18455a74a43SLisandro Dalcin self.class_name = node.class_name 18555a74a43SLisandro Dalcin self.visitchildren(node) 18655a74a43SLisandro Dalcin self.class_name = oldname 18755a74a43SLisandro Dalcin self.class_node = oldclass 18855a74a43SLisandro Dalcin return node 18955a74a43SLisandro Dalcin 19055a74a43SLisandro Dalcin def visit_LambdaNode(self, node): 19155a74a43SLisandro Dalcin # lambda expressions so not have signature or inner functions 19255a74a43SLisandro Dalcin return node 19355a74a43SLisandro Dalcin 19455a74a43SLisandro Dalcin def visit_DefNode(self, node): 19555a74a43SLisandro Dalcin if not self.current_directives['embedsignature']: 19655a74a43SLisandro Dalcin return node 19755a74a43SLisandro Dalcin 19855a74a43SLisandro Dalcin hide_self = False 19955a74a43SLisandro Dalcin if node.entry.is_special: 20055a74a43SLisandro Dalcin return node 20155a74a43SLisandro Dalcin class_name, func_name = self.class_name, node.name 20255a74a43SLisandro Dalcin 20355a74a43SLisandro Dalcin npoargs = getattr(node, 'num_posonly_args', 0) 20455a74a43SLisandro Dalcin nkargs = getattr(node, 'num_kwonly_args', 0) 20555a74a43SLisandro Dalcin npargs = len(node.args) - nkargs - npoargs 20655a74a43SLisandro Dalcin signature = self._fmt_signature( 207*6f336411SStefano Zampini node, 208*6f336411SStefano Zampini class_name, 209*6f336411SStefano Zampini func_name, 210*6f336411SStefano Zampini node.args, 211*6f336411SStefano Zampini npoargs, 212*6f336411SStefano Zampini npargs, 213*6f336411SStefano Zampini node.star_arg, 214*6f336411SStefano Zampini nkargs, 215*6f336411SStefano Zampini node.starstar_arg, 21655a74a43SLisandro Dalcin return_expr=node.return_type_annotation, 217*6f336411SStefano Zampini return_type=None, 218*6f336411SStefano Zampini hide_self=hide_self, 219*6f336411SStefano Zampini ) 22055a74a43SLisandro Dalcin if signature: 22155a74a43SLisandro Dalcin doc_holder = node.entry 22255a74a43SLisandro Dalcin 22355a74a43SLisandro Dalcin if doc_holder.doc is not None: 22455a74a43SLisandro Dalcin old_doc = doc_holder.doc 225*6f336411SStefano Zampini elif getattr(node, 'py_func', None) is not None: 22655a74a43SLisandro Dalcin old_doc = node.py_func.entry.doc 22755a74a43SLisandro Dalcin else: 22855a74a43SLisandro Dalcin old_doc = None 229b3f8c7a1SStefano Zampini new_doc = self._embed_signature(signature, node.pos, old_doc) 23055a74a43SLisandro Dalcin doc_holder.doc = EncodedString(new_doc) 231*6f336411SStefano Zampini if getattr(node, 'py_func', None) is not None: 23255a74a43SLisandro Dalcin node.py_func.entry.doc = EncodedString(new_doc) 23355a74a43SLisandro Dalcin return node 23455a74a43SLisandro Dalcin 23555a74a43SLisandro Dalcin def visit_CFuncDefNode(self, node): 23655a74a43SLisandro Dalcin if not self.current_directives['embedsignature']: 23755a74a43SLisandro Dalcin return node 23855a74a43SLisandro Dalcin if not node.overridable: # not cpdef FOO(...): 23955a74a43SLisandro Dalcin return node 24055a74a43SLisandro Dalcin 24155a74a43SLisandro Dalcin signature = self._fmt_signature( 242*6f336411SStefano Zampini node, 243*6f336411SStefano Zampini self.class_name, 244*6f336411SStefano Zampini node.declarator.base.name, 24555a74a43SLisandro Dalcin node.declarator.args, 246*6f336411SStefano Zampini return_type=node.return_type, 247*6f336411SStefano Zampini ) 24855a74a43SLisandro Dalcin if signature: 24955a74a43SLisandro Dalcin if node.entry.doc is not None: 25055a74a43SLisandro Dalcin old_doc = node.entry.doc 25155a74a43SLisandro Dalcin elif getattr(node, 'py_func', None) is not None: 25255a74a43SLisandro Dalcin old_doc = node.py_func.entry.doc 25355a74a43SLisandro Dalcin else: 25455a74a43SLisandro Dalcin old_doc = None 255b3f8c7a1SStefano Zampini new_doc = self._embed_signature(signature, node.pos, old_doc) 25655a74a43SLisandro Dalcin node.entry.doc = EncodedString(new_doc) 25755a74a43SLisandro Dalcin py_func = getattr(node, 'py_func', None) 25855a74a43SLisandro Dalcin if py_func is not None: 25955a74a43SLisandro Dalcin py_func.entry.doc = EncodedString(new_doc) 26055a74a43SLisandro Dalcin return node 26155a74a43SLisandro Dalcin 26255a74a43SLisandro Dalcin def visit_PropertyNode(self, node): 26355a74a43SLisandro Dalcin if not self.current_directives['embedsignature']: 26455a74a43SLisandro Dalcin return node 26555a74a43SLisandro Dalcin 26655a74a43SLisandro Dalcin entry = node.entry 26755a74a43SLisandro Dalcin body = node.body 26855a74a43SLisandro Dalcin prop_name = entry.name 26955a74a43SLisandro Dalcin type_name = None 27055a74a43SLisandro Dalcin if entry.visibility == 'public': 27155a74a43SLisandro Dalcin # property synthesised from a cdef public attribute 272*6f336411SStefano Zampini type_name = entry.type.declaration_code('', for_display=1) 27355a74a43SLisandro Dalcin if not entry.type.is_pyobject: 27455a74a43SLisandro Dalcin type_name = "'%s'" % type_name 27555a74a43SLisandro Dalcin elif entry.type.is_extension_type: 27655a74a43SLisandro Dalcin type_name = entry.type.module_name + '.' + type_name 27755a74a43SLisandro Dalcin if type_name is None: 27855a74a43SLisandro Dalcin for stat in body.stats: 27955a74a43SLisandro Dalcin if stat.name != '__get__': 28055a74a43SLisandro Dalcin continue 28155a74a43SLisandro Dalcin cls_name = self.class_name 28255a74a43SLisandro Dalcin if cls_name: 28355a74a43SLisandro Dalcin namespace = self._select_format('%s.' % cls_name, '') 28455a74a43SLisandro Dalcin prop_name = '%s%s' % (namespace, prop_name) 28555a74a43SLisandro Dalcin ret_annotation = stat.return_type_annotation 28655a74a43SLisandro Dalcin if ret_annotation: 28755a74a43SLisandro Dalcin type_name = self._fmt_annotation(ret_annotation) 28855a74a43SLisandro Dalcin if type_name is not None: 28955a74a43SLisandro Dalcin signature = '%s: %s' % (prop_name, type_name) 290b3f8c7a1SStefano Zampini new_doc = self._embed_signature(signature, node.pos, entry.doc) 29155a74a43SLisandro Dalcin entry.doc = EncodedString(new_doc) 29255a74a43SLisandro Dalcin return node 29355a74a43SLisandro Dalcin 29455a74a43SLisandro Dalcin 29555a74a43SLisandro Dalcin# Monkeypatch AutoDocTransforms.EmbedSignature 29655a74a43SLisandro Dalcintry: 29755a74a43SLisandro Dalcin from Cython.Compiler import AutoDocTransforms 298*6f336411SStefano Zampini 29955a74a43SLisandro Dalcin AutoDocTransforms.EmbedSignature = EmbedSignature 30055a74a43SLisandro Dalcinexcept Exception as exc: 30155a74a43SLisandro Dalcin import logging 302*6f336411SStefano Zampini 30355a74a43SLisandro Dalcin logging.Logger(__name__).exception(exc) 30455a74a43SLisandro Dalcin 30555a74a43SLisandro Dalcin# Monkeypatch Nodes.raise_utility_code 30655a74a43SLisandro Dalcintry: 30755a74a43SLisandro Dalcin from Cython.Compiler.Nodes import raise_utility_code 308*6f336411SStefano Zampini 30955a74a43SLisandro Dalcin code = raise_utility_code.impl 31055a74a43SLisandro Dalcin try: 311*6f336411SStefano Zampini ipos = code.index('if (tb) {\n#if CYTHON_COMPILING_IN_PYPY\n') 31255a74a43SLisandro Dalcin except ValueError: 31355a74a43SLisandro Dalcin ipos = None 31455a74a43SLisandro Dalcin else: 31555a74a43SLisandro Dalcin raise_utility_code.impl = code[:ipos] + code[ipos:].replace( 316*6f336411SStefano Zampini 'CYTHON_COMPILING_IN_PYPY', '!CYTHON_FAST_THREAD_STATE', 1 317*6f336411SStefano Zampini ) 31855a74a43SLisandro Dalcin del raise_utility_code, code, ipos 31955a74a43SLisandro Dalcinexcept Exception as exc: 32055a74a43SLisandro Dalcin import logging 321*6f336411SStefano Zampini 32255a74a43SLisandro Dalcin logging.Logger(__name__).exception(exc) 323