xref: /petsc/src/binding/petsc4py/conf/cyautodoc.py (revision f5523d9a7837f0014d956390352c6c6f7678be39)
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