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