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) 116f336411SStefano 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) 176f336411SStefano 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: 226f336411SStefano Zampini self.put('()') 2355a74a43SLisandro Dalcin else: 2455a74a43SLisandro Dalcin self.visit(node.index) 256f336411SStefano Zampini self.put(']') 2655a74a43SLisandro Dalcin 27*87b0c9e1SLisandro Dalcin if hasattr(BaseExpressionWriter, 'emit_string'): 2855a74a43SLisandro Dalcin def visit_UnicodeNode(self, node): 296f336411SStefano Zampini self.emit_string(node, '') 3055a74a43SLisandro Dalcin 3155a74a43SLisandro Dalcin 3255a74a43SLisandro Dalcinclass AnnotationWriter(ExpressionWriter, BaseAnnotationWriter): 3355a74a43SLisandro Dalcin pass 3455a74a43SLisandro Dalcin 3555a74a43SLisandro Dalcin 3655a74a43SLisandro Dalcinclass EmbedSignature(CythonTransform): 3755a74a43SLisandro Dalcin def __init__(self, context): 3855a74a43SLisandro Dalcin super(EmbedSignature, self).__init__(context) 3955a74a43SLisandro Dalcin self.class_name = None 4055a74a43SLisandro Dalcin self.class_node = None 4155a74a43SLisandro Dalcin 4255a74a43SLisandro Dalcin def _select_format(self, embed, clinic): 4355a74a43SLisandro Dalcin return embed 4455a74a43SLisandro Dalcin 4555a74a43SLisandro Dalcin def _fmt_expr(self, node): 4655a74a43SLisandro Dalcin writer = ExpressionWriter() 476f336411SStefano Zampini return writer.write(node) 4855a74a43SLisandro Dalcin 4955a74a43SLisandro Dalcin def _fmt_annotation(self, node): 5055a74a43SLisandro Dalcin writer = AnnotationWriter() 516f336411SStefano Zampini return writer.write(node) 5255a74a43SLisandro Dalcin 5355a74a43SLisandro Dalcin def _fmt_arg(self, arg): 5455a74a43SLisandro Dalcin annotation = None 5555a74a43SLisandro Dalcin if arg.is_self_arg: 5655a74a43SLisandro Dalcin doc = self._select_format(arg.name, '$self') 5755a74a43SLisandro Dalcin elif arg.is_type_arg: 5855a74a43SLisandro Dalcin doc = self._select_format(arg.name, '$type') 5955a74a43SLisandro Dalcin else: 6055a74a43SLisandro Dalcin doc = arg.name 6155a74a43SLisandro Dalcin if arg.type is PyrexTypes.py_object_type: 6255a74a43SLisandro Dalcin annotation = None 6355a74a43SLisandro Dalcin else: 6455a74a43SLisandro Dalcin annotation = arg.type.declaration_code('', for_display=1) 6555a74a43SLisandro Dalcin if arg.default and arg.default.is_none: 6655a74a43SLisandro Dalcin annotation += ' | None' 6755a74a43SLisandro Dalcin if arg.annotation: 6855a74a43SLisandro Dalcin annotation = self._fmt_annotation(arg.annotation) 6955a74a43SLisandro Dalcin annotation = self._select_format(annotation, None) 7055a74a43SLisandro Dalcin if annotation: 7155a74a43SLisandro Dalcin doc = doc + (': %s' % annotation) 7255a74a43SLisandro Dalcin if arg.default: 7355a74a43SLisandro Dalcin default = self._fmt_expr(arg.default) 7455a74a43SLisandro Dalcin doc = doc + (' = %s' % default) 7555a74a43SLisandro Dalcin elif arg.default: 7655a74a43SLisandro Dalcin default = self._fmt_expr(arg.default) 7755a74a43SLisandro Dalcin doc = doc + ('=%s' % default) 7855a74a43SLisandro Dalcin return doc 7955a74a43SLisandro Dalcin 8055a74a43SLisandro Dalcin def _fmt_star_arg(self, arg): 8155a74a43SLisandro Dalcin arg_doc = arg.name 8255a74a43SLisandro Dalcin if arg.annotation: 8355a74a43SLisandro Dalcin annotation = self._fmt_annotation(arg.annotation) 8455a74a43SLisandro Dalcin arg_doc = arg_doc + (': %s' % annotation) 8555a74a43SLisandro Dalcin return arg_doc 8655a74a43SLisandro Dalcin 876f336411SStefano Zampini def _fmt_arglist( 886f336411SStefano Zampini self, 896f336411SStefano Zampini args, 906f336411SStefano Zampini npoargs=0, 916f336411SStefano Zampini npargs=0, 926f336411SStefano Zampini pargs=None, 936f336411SStefano Zampini nkargs=0, 946f336411SStefano Zampini kargs=None, 956f336411SStefano Zampini hide_self=False, 966f336411SStefano Zampini ): 9755a74a43SLisandro Dalcin arglist = [] 9855a74a43SLisandro Dalcin for arg in args: 9955a74a43SLisandro Dalcin if not hide_self or not arg.entry.is_self_arg: 10055a74a43SLisandro Dalcin arg_doc = self._fmt_arg(arg) 10155a74a43SLisandro Dalcin arglist.append(arg_doc) 10255a74a43SLisandro Dalcin if pargs: 10355a74a43SLisandro Dalcin arg_doc = self._fmt_star_arg(pargs) 10455a74a43SLisandro Dalcin arglist.insert(npargs + npoargs, '*%s' % arg_doc) 10555a74a43SLisandro Dalcin elif nkargs: 10655a74a43SLisandro Dalcin arglist.insert(npargs + npoargs, '*') 10755a74a43SLisandro Dalcin if npoargs: 10855a74a43SLisandro Dalcin arglist.insert(npoargs, '/') 10955a74a43SLisandro Dalcin if kargs: 11055a74a43SLisandro Dalcin arg_doc = self._fmt_star_arg(kargs) 11155a74a43SLisandro Dalcin arglist.append('**%s' % arg_doc) 11255a74a43SLisandro Dalcin return arglist 11355a74a43SLisandro Dalcin 11455a74a43SLisandro Dalcin def _fmt_ret_type(self, ret): 11555a74a43SLisandro Dalcin if ret is PyrexTypes.py_object_type: 11655a74a43SLisandro Dalcin return None 1176f336411SStefano Zampini return ret.declaration_code('', for_display=1) 11855a74a43SLisandro Dalcin 1196f336411SStefano Zampini def _fmt_signature( 1206f336411SStefano Zampini self, 1216f336411SStefano Zampini node, 1226f336411SStefano Zampini cls_name, 1236f336411SStefano Zampini func_name, 1246f336411SStefano Zampini args, 1256f336411SStefano Zampini npoargs=0, 1266f336411SStefano Zampini npargs=0, 1276f336411SStefano Zampini pargs=None, 1286f336411SStefano Zampini nkargs=0, 1296f336411SStefano Zampini kargs=None, 13055a74a43SLisandro Dalcin return_expr=None, 1316f336411SStefano Zampini return_type=None, 1326f336411SStefano Zampini hide_self=False, 1336f336411SStefano Zampini ): 1346f336411SStefano Zampini arglist = self._fmt_arglist( 1356f336411SStefano Zampini args, npoargs, npargs, pargs, nkargs, kargs, hide_self=hide_self 1366f336411SStefano Zampini ) 13755a74a43SLisandro Dalcin arglist_doc = ', '.join(arglist) 13855a74a43SLisandro Dalcin func_doc = '%s(%s)' % (func_name, arglist_doc) 13955a74a43SLisandro Dalcin if cls_name: 14055a74a43SLisandro Dalcin namespace = self._select_format('%s.' % cls_name, '') 14155a74a43SLisandro Dalcin func_doc = '%s%s' % (namespace, func_doc) 14255a74a43SLisandro Dalcin ret_doc = None 14355a74a43SLisandro Dalcin if return_expr: 14455a74a43SLisandro Dalcin ret_doc = self._fmt_annotation(return_expr) 14555a74a43SLisandro Dalcin elif return_type: 14655a74a43SLisandro Dalcin ret_doc = self._fmt_ret_type(return_type) 14755a74a43SLisandro Dalcin if ret_doc: 14855a74a43SLisandro Dalcin docfmt = self._select_format('%s -> %s', '%s -> (%s)') 14955a74a43SLisandro Dalcin func_doc = docfmt % (func_doc, ret_doc) 1506f336411SStefano Zampini else: 1516f336411SStefano Zampini if not func_doc.startswith('_') and not func_name.startswith('_'): 1526f336411SStefano Zampini error( 1536f336411SStefano Zampini node.pos, 1546f336411SStefano Zampini f'cyautodoc._fmt_signature: missing return type for {func_doc}', 1556f336411SStefano Zampini ) 15655a74a43SLisandro Dalcin return func_doc 15755a74a43SLisandro Dalcin 158b3f8c7a1SStefano Zampini def _fmt_relative_position(self, pos): 1596f336411SStefano Zampini return 'Source code at ' + ':'.join( 1606f336411SStefano Zampini (str(pos[0].get_filenametable_entry()), str(pos[1])) 1616f336411SStefano Zampini ) 162b3f8c7a1SStefano Zampini 163b3f8c7a1SStefano Zampini def _embed_signature(self, signature, pos, node_doc): 164b3f8c7a1SStefano Zampini pos = self._fmt_relative_position(pos) 16555a74a43SLisandro Dalcin if node_doc: 1666f336411SStefano Zampini docfmt = self._select_format('%s\n%s\n%s', '%s\n--\n\n%s') 167b3f8c7a1SStefano Zampini return docfmt % (signature, node_doc, pos) 1686f336411SStefano Zampini docfmt = self._select_format('%s\n%s', '%s\n--\n\n%s') 169b3f8c7a1SStefano Zampini return docfmt % (signature, pos) 17055a74a43SLisandro Dalcin 17155a74a43SLisandro Dalcin def __call__(self, node): 17255a74a43SLisandro Dalcin if not Options.docstrings: 17355a74a43SLisandro Dalcin return node 17455a74a43SLisandro Dalcin return super(EmbedSignature, self).__call__(node) 17555a74a43SLisandro Dalcin 17655a74a43SLisandro Dalcin def visit_ClassDefNode(self, node): 17755a74a43SLisandro Dalcin oldname = self.class_name 17855a74a43SLisandro Dalcin oldclass = self.class_node 17955a74a43SLisandro Dalcin self.class_node = node 18055a74a43SLisandro Dalcin try: 18155a74a43SLisandro Dalcin # PyClassDefNode 18255a74a43SLisandro Dalcin self.class_name = node.name 18355a74a43SLisandro Dalcin except AttributeError: 18455a74a43SLisandro Dalcin # CClassDefNode 18555a74a43SLisandro Dalcin self.class_name = node.class_name 18655a74a43SLisandro Dalcin self.visitchildren(node) 18755a74a43SLisandro Dalcin self.class_name = oldname 18855a74a43SLisandro Dalcin self.class_node = oldclass 18955a74a43SLisandro Dalcin return node 19055a74a43SLisandro Dalcin 19155a74a43SLisandro Dalcin def visit_LambdaNode(self, node): 19255a74a43SLisandro Dalcin # lambda expressions so not have signature or inner functions 19355a74a43SLisandro Dalcin return node 19455a74a43SLisandro Dalcin 19555a74a43SLisandro Dalcin def visit_DefNode(self, node): 19655a74a43SLisandro Dalcin if not self.current_directives['embedsignature']: 19755a74a43SLisandro Dalcin return node 19855a74a43SLisandro Dalcin 19955a74a43SLisandro Dalcin hide_self = False 20055a74a43SLisandro Dalcin if node.entry.is_special: 20155a74a43SLisandro Dalcin return node 20255a74a43SLisandro Dalcin class_name, func_name = self.class_name, node.name 20355a74a43SLisandro Dalcin 20455a74a43SLisandro Dalcin npoargs = getattr(node, 'num_posonly_args', 0) 20555a74a43SLisandro Dalcin nkargs = getattr(node, 'num_kwonly_args', 0) 20655a74a43SLisandro Dalcin npargs = len(node.args) - nkargs - npoargs 20755a74a43SLisandro Dalcin signature = self._fmt_signature( 2086f336411SStefano Zampini node, 2096f336411SStefano Zampini class_name, 2106f336411SStefano Zampini func_name, 2116f336411SStefano Zampini node.args, 2126f336411SStefano Zampini npoargs, 2136f336411SStefano Zampini npargs, 2146f336411SStefano Zampini node.star_arg, 2156f336411SStefano Zampini nkargs, 2166f336411SStefano Zampini node.starstar_arg, 21755a74a43SLisandro Dalcin return_expr=node.return_type_annotation, 2186f336411SStefano Zampini return_type=None, 2196f336411SStefano Zampini hide_self=hide_self, 2206f336411SStefano Zampini ) 22155a74a43SLisandro Dalcin if signature: 22255a74a43SLisandro Dalcin doc_holder = node.entry 22355a74a43SLisandro Dalcin 22455a74a43SLisandro Dalcin if doc_holder.doc is not None: 22555a74a43SLisandro Dalcin old_doc = doc_holder.doc 2266f336411SStefano Zampini elif getattr(node, 'py_func', None) is not None: 22755a74a43SLisandro Dalcin old_doc = node.py_func.entry.doc 22855a74a43SLisandro Dalcin else: 22955a74a43SLisandro Dalcin old_doc = None 230b3f8c7a1SStefano Zampini new_doc = self._embed_signature(signature, node.pos, old_doc) 23155a74a43SLisandro Dalcin doc_holder.doc = EncodedString(new_doc) 2326f336411SStefano Zampini if getattr(node, 'py_func', None) is not None: 23355a74a43SLisandro Dalcin node.py_func.entry.doc = EncodedString(new_doc) 23455a74a43SLisandro Dalcin return node 23555a74a43SLisandro Dalcin 23655a74a43SLisandro Dalcin def visit_CFuncDefNode(self, node): 23755a74a43SLisandro Dalcin if not self.current_directives['embedsignature']: 23855a74a43SLisandro Dalcin return node 23955a74a43SLisandro Dalcin if not node.overridable: # not cpdef FOO(...): 24055a74a43SLisandro Dalcin return node 24155a74a43SLisandro Dalcin 24255a74a43SLisandro Dalcin signature = self._fmt_signature( 2436f336411SStefano Zampini node, 2446f336411SStefano Zampini self.class_name, 2456f336411SStefano Zampini node.declarator.base.name, 24655a74a43SLisandro Dalcin node.declarator.args, 2476f336411SStefano Zampini return_type=node.return_type, 2486f336411SStefano Zampini ) 24955a74a43SLisandro Dalcin if signature: 25055a74a43SLisandro Dalcin if node.entry.doc is not None: 25155a74a43SLisandro Dalcin old_doc = node.entry.doc 25255a74a43SLisandro Dalcin elif getattr(node, 'py_func', None) is not None: 25355a74a43SLisandro Dalcin old_doc = node.py_func.entry.doc 25455a74a43SLisandro Dalcin else: 25555a74a43SLisandro Dalcin old_doc = None 256b3f8c7a1SStefano Zampini new_doc = self._embed_signature(signature, node.pos, old_doc) 25755a74a43SLisandro Dalcin node.entry.doc = EncodedString(new_doc) 25855a74a43SLisandro Dalcin py_func = getattr(node, 'py_func', None) 25955a74a43SLisandro Dalcin if py_func is not None: 26055a74a43SLisandro Dalcin py_func.entry.doc = EncodedString(new_doc) 26155a74a43SLisandro Dalcin return node 26255a74a43SLisandro Dalcin 26355a74a43SLisandro Dalcin def visit_PropertyNode(self, node): 26455a74a43SLisandro Dalcin if not self.current_directives['embedsignature']: 26555a74a43SLisandro Dalcin return node 26655a74a43SLisandro Dalcin 26755a74a43SLisandro Dalcin entry = node.entry 26855a74a43SLisandro Dalcin body = node.body 26955a74a43SLisandro Dalcin prop_name = entry.name 27055a74a43SLisandro Dalcin type_name = None 27155a74a43SLisandro Dalcin if entry.visibility == 'public': 27255a74a43SLisandro Dalcin # property synthesised from a cdef public attribute 2736f336411SStefano Zampini type_name = entry.type.declaration_code('', for_display=1) 27455a74a43SLisandro Dalcin if not entry.type.is_pyobject: 27555a74a43SLisandro Dalcin type_name = "'%s'" % type_name 27655a74a43SLisandro Dalcin elif entry.type.is_extension_type: 27755a74a43SLisandro Dalcin type_name = entry.type.module_name + '.' + type_name 27855a74a43SLisandro Dalcin if type_name is None: 27955a74a43SLisandro Dalcin for stat in body.stats: 28055a74a43SLisandro Dalcin if stat.name != '__get__': 28155a74a43SLisandro Dalcin continue 28255a74a43SLisandro Dalcin cls_name = self.class_name 28355a74a43SLisandro Dalcin if cls_name: 28455a74a43SLisandro Dalcin namespace = self._select_format('%s.' % cls_name, '') 28555a74a43SLisandro Dalcin prop_name = '%s%s' % (namespace, prop_name) 28655a74a43SLisandro Dalcin ret_annotation = stat.return_type_annotation 28755a74a43SLisandro Dalcin if ret_annotation: 28855a74a43SLisandro Dalcin type_name = self._fmt_annotation(ret_annotation) 28955a74a43SLisandro Dalcin if type_name is not None: 29055a74a43SLisandro Dalcin signature = '%s: %s' % (prop_name, type_name) 291b3f8c7a1SStefano Zampini new_doc = self._embed_signature(signature, node.pos, entry.doc) 29255a74a43SLisandro Dalcin entry.doc = EncodedString(new_doc) 29355a74a43SLisandro Dalcin return node 29455a74a43SLisandro Dalcin 29555a74a43SLisandro Dalcin 29655a74a43SLisandro Dalcin# Monkeypatch AutoDocTransforms.EmbedSignature 29755a74a43SLisandro Dalcintry: 29855a74a43SLisandro Dalcin from Cython.Compiler import AutoDocTransforms 2996f336411SStefano Zampini 30055a74a43SLisandro Dalcin AutoDocTransforms.EmbedSignature = EmbedSignature 30155a74a43SLisandro Dalcinexcept Exception as exc: 30255a74a43SLisandro Dalcin import logging 3036f336411SStefano Zampini 30455a74a43SLisandro Dalcin logging.Logger(__name__).exception(exc) 30555a74a43SLisandro Dalcin 30655a74a43SLisandro Dalcin# Monkeypatch Nodes.raise_utility_code 30755a74a43SLisandro Dalcintry: 30855a74a43SLisandro Dalcin from Cython.Compiler.Nodes import raise_utility_code 3096f336411SStefano Zampini 31055a74a43SLisandro Dalcin code = raise_utility_code.impl 31155a74a43SLisandro Dalcin try: 3126f336411SStefano Zampini ipos = code.index('if (tb) {\n#if CYTHON_COMPILING_IN_PYPY\n') 31355a74a43SLisandro Dalcin except ValueError: 31455a74a43SLisandro Dalcin ipos = None 31555a74a43SLisandro Dalcin else: 31655a74a43SLisandro Dalcin raise_utility_code.impl = code[:ipos] + code[ipos:].replace( 3176f336411SStefano Zampini 'CYTHON_COMPILING_IN_PYPY', '!CYTHON_FAST_THREAD_STATE', 1 3186f336411SStefano Zampini ) 31955a74a43SLisandro Dalcin del raise_utility_code, code, ipos 32055a74a43SLisandro Dalcinexcept Exception as exc: 32155a74a43SLisandro Dalcin import logging 3226f336411SStefano Zampini 32355a74a43SLisandro Dalcin logging.Logger(__name__).exception(exc) 324