xref: /petsc/src/binding/petsc4py/conf/cyautodoc.py (revision b3f8c7a1f1dae9cf75fc76de14d347d78933c187)
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)
1155a74a43SLisandro Dalcin
1255a74a43SLisandro Dalcin
1355a74a43SLisandro Dalcinclass ExpressionWriter(BaseExpressionWriter):
1455a74a43SLisandro Dalcin
1555a74a43SLisandro Dalcin    def visit_IndexNode(self, node):
1655a74a43SLisandro Dalcin        self.visit(node.base)
1755a74a43SLisandro Dalcin        self.put(u"[")
1855a74a43SLisandro Dalcin        if isinstance(node.index, TupleNode):
1955a74a43SLisandro Dalcin            if node.index.subexpr_nodes():
2055a74a43SLisandro Dalcin                self.emit_sequence(node.index)
2155a74a43SLisandro Dalcin            else:
2255a74a43SLisandro Dalcin                self.put(u"()")
2355a74a43SLisandro Dalcin        else:
2455a74a43SLisandro Dalcin            self.visit(node.index)
2555a74a43SLisandro Dalcin        self.put(u"]")
2655a74a43SLisandro Dalcin
2755a74a43SLisandro Dalcin    def visit_UnicodeNode(self, node):
2855a74a43SLisandro Dalcin        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
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()
4755a74a43SLisandro Dalcin        result = writer.write(node)
4855a74a43SLisandro Dalcin        # print(type(node).__name__, '-->', result)
4955a74a43SLisandro Dalcin        return result
5055a74a43SLisandro Dalcin
5155a74a43SLisandro Dalcin    def _fmt_annotation(self, node):
5255a74a43SLisandro Dalcin        writer = AnnotationWriter()
5355a74a43SLisandro Dalcin        result = writer.write(node)
5455a74a43SLisandro Dalcin        # print(type(node).__name__, '-->', result)
5555a74a43SLisandro Dalcin        return result
5655a74a43SLisandro Dalcin
5755a74a43SLisandro Dalcin    def _fmt_arg(self, arg):
5855a74a43SLisandro Dalcin        annotation = None
5955a74a43SLisandro Dalcin        if arg.is_self_arg:
6055a74a43SLisandro Dalcin            doc = self._select_format(arg.name, '$self')
6155a74a43SLisandro Dalcin        elif arg.is_type_arg:
6255a74a43SLisandro Dalcin            doc = self._select_format(arg.name, '$type')
6355a74a43SLisandro Dalcin        else:
6455a74a43SLisandro Dalcin            doc = arg.name
6555a74a43SLisandro Dalcin            if arg.type is PyrexTypes.py_object_type:
6655a74a43SLisandro Dalcin                annotation = None
6755a74a43SLisandro Dalcin            else:
6855a74a43SLisandro Dalcin                annotation = arg.type.declaration_code('', for_display=1)
6955a74a43SLisandro Dalcin                if arg.default and arg.default.is_none:
7055a74a43SLisandro Dalcin                    annotation += ' | None'
7155a74a43SLisandro Dalcin        if arg.annotation:
7255a74a43SLisandro Dalcin            annotation = self._fmt_annotation(arg.annotation)
7355a74a43SLisandro Dalcin        annotation = self._select_format(annotation, None)
7455a74a43SLisandro Dalcin        if annotation:
7555a74a43SLisandro Dalcin            doc = doc + (': %s' % annotation)
7655a74a43SLisandro Dalcin            if arg.default:
7755a74a43SLisandro Dalcin                default = self._fmt_expr(arg.default)
7855a74a43SLisandro Dalcin                doc = doc + (' = %s' % default)
7955a74a43SLisandro Dalcin        elif arg.default:
8055a74a43SLisandro Dalcin            default = self._fmt_expr(arg.default)
8155a74a43SLisandro Dalcin            doc = doc + ('=%s' % default)
8255a74a43SLisandro Dalcin        return doc
8355a74a43SLisandro Dalcin
8455a74a43SLisandro Dalcin    def _fmt_star_arg(self, arg):
8555a74a43SLisandro Dalcin        arg_doc = arg.name
8655a74a43SLisandro Dalcin        if arg.annotation:
8755a74a43SLisandro Dalcin            annotation = self._fmt_annotation(arg.annotation)
8855a74a43SLisandro Dalcin            arg_doc = arg_doc + (': %s' % annotation)
8955a74a43SLisandro Dalcin        return arg_doc
9055a74a43SLisandro Dalcin
9155a74a43SLisandro Dalcin    def _fmt_arglist(self, args,
9255a74a43SLisandro Dalcin                     npoargs=0, npargs=0, pargs=None,
9355a74a43SLisandro Dalcin                     nkargs=0, kargs=None,
9455a74a43SLisandro Dalcin                     hide_self=False):
9555a74a43SLisandro Dalcin        arglist = []
9655a74a43SLisandro Dalcin        for arg in args:
9755a74a43SLisandro Dalcin            if not hide_self or not arg.entry.is_self_arg:
9855a74a43SLisandro Dalcin                arg_doc = self._fmt_arg(arg)
9955a74a43SLisandro Dalcin                arglist.append(arg_doc)
10055a74a43SLisandro Dalcin        if pargs:
10155a74a43SLisandro Dalcin            arg_doc = self._fmt_star_arg(pargs)
10255a74a43SLisandro Dalcin            arglist.insert(npargs + npoargs, '*%s' % arg_doc)
10355a74a43SLisandro Dalcin        elif nkargs:
10455a74a43SLisandro Dalcin            arglist.insert(npargs + npoargs, '*')
10555a74a43SLisandro Dalcin        if npoargs:
10655a74a43SLisandro Dalcin            arglist.insert(npoargs, '/')
10755a74a43SLisandro Dalcin        if kargs:
10855a74a43SLisandro Dalcin            arg_doc = self._fmt_star_arg(kargs)
10955a74a43SLisandro Dalcin            arglist.append('**%s' % arg_doc)
11055a74a43SLisandro Dalcin        return arglist
11155a74a43SLisandro Dalcin
11255a74a43SLisandro Dalcin    def _fmt_ret_type(self, ret):
11355a74a43SLisandro Dalcin        if ret is PyrexTypes.py_object_type:
11455a74a43SLisandro Dalcin            return None
11555a74a43SLisandro Dalcin        else:
11655a74a43SLisandro Dalcin            return ret.declaration_code("", for_display=1)
11755a74a43SLisandro Dalcin
11855a74a43SLisandro Dalcin    def _fmt_signature(self, cls_name, func_name, args,
11955a74a43SLisandro Dalcin                       npoargs=0, npargs=0, pargs=None,
12055a74a43SLisandro Dalcin                       nkargs=0, kargs=None,
12155a74a43SLisandro Dalcin                       return_expr=None,
12255a74a43SLisandro Dalcin                       return_type=None, hide_self=False):
12355a74a43SLisandro Dalcin        arglist = self._fmt_arglist(args,
12455a74a43SLisandro Dalcin                                    npoargs, npargs, pargs,
12555a74a43SLisandro Dalcin                                    nkargs, kargs,
12655a74a43SLisandro Dalcin                                    hide_self=hide_self)
12755a74a43SLisandro Dalcin        arglist_doc = ', '.join(arglist)
12855a74a43SLisandro Dalcin        func_doc = '%s(%s)' % (func_name, arglist_doc)
12955a74a43SLisandro Dalcin        if cls_name:
13055a74a43SLisandro Dalcin            namespace = self._select_format('%s.' % cls_name, '')
13155a74a43SLisandro Dalcin            func_doc = '%s%s' % (namespace, func_doc)
13255a74a43SLisandro Dalcin        ret_doc = None
13355a74a43SLisandro Dalcin        if return_expr:
13455a74a43SLisandro Dalcin            ret_doc = self._fmt_annotation(return_expr)
13555a74a43SLisandro Dalcin        elif return_type:
13655a74a43SLisandro Dalcin            ret_doc = self._fmt_ret_type(return_type)
13755a74a43SLisandro Dalcin        if ret_doc:
13855a74a43SLisandro Dalcin            docfmt = self._select_format('%s -> %s', '%s -> (%s)')
13955a74a43SLisandro Dalcin            func_doc = docfmt % (func_doc, ret_doc)
14055a74a43SLisandro Dalcin        return func_doc
14155a74a43SLisandro Dalcin
142*b3f8c7a1SStefano Zampini    def _fmt_relative_position(self, pos):
143*b3f8c7a1SStefano Zampini        return ':'.join((str(pos[0].get_filenametable_entry()), str(pos[1])))
144*b3f8c7a1SStefano Zampini
145*b3f8c7a1SStefano Zampini    def _embed_signature(self, signature, pos, node_doc):
146*b3f8c7a1SStefano Zampini        pos = self._fmt_relative_position(pos)
14755a74a43SLisandro Dalcin        if node_doc:
148*b3f8c7a1SStefano Zampini            docfmt = self._select_format("%s\n%s\n%s", "%s\n--\n\n%s")
149*b3f8c7a1SStefano Zampini            return docfmt % (signature, node_doc, pos)
15055a74a43SLisandro Dalcin        else:
151*b3f8c7a1SStefano Zampini            docfmt = self._select_format("%s\n%s", "%s\n--\n\n%s")
152*b3f8c7a1SStefano Zampini            return docfmt % (signature, pos)
15355a74a43SLisandro Dalcin
15455a74a43SLisandro Dalcin    def __call__(self, node):
15555a74a43SLisandro Dalcin        if not Options.docstrings:
15655a74a43SLisandro Dalcin            return node
15755a74a43SLisandro Dalcin        else:
15855a74a43SLisandro Dalcin            return super(EmbedSignature, self).__call__(node)
15955a74a43SLisandro Dalcin
16055a74a43SLisandro Dalcin    def visit_ClassDefNode(self, node):
16155a74a43SLisandro Dalcin        oldname = self.class_name
16255a74a43SLisandro Dalcin        oldclass = self.class_node
16355a74a43SLisandro Dalcin        self.class_node = node
16455a74a43SLisandro Dalcin        try:
16555a74a43SLisandro Dalcin            # PyClassDefNode
16655a74a43SLisandro Dalcin            self.class_name = node.name
16755a74a43SLisandro Dalcin        except AttributeError:
16855a74a43SLisandro Dalcin            # CClassDefNode
16955a74a43SLisandro Dalcin            self.class_name = node.class_name
17055a74a43SLisandro Dalcin        self.visitchildren(node)
17155a74a43SLisandro Dalcin        self.class_name = oldname
17255a74a43SLisandro Dalcin        self.class_node = oldclass
17355a74a43SLisandro Dalcin        return node
17455a74a43SLisandro Dalcin
17555a74a43SLisandro Dalcin    def visit_LambdaNode(self, node):
17655a74a43SLisandro Dalcin        # lambda expressions so not have signature or inner functions
17755a74a43SLisandro Dalcin        return node
17855a74a43SLisandro Dalcin
17955a74a43SLisandro Dalcin    def visit_DefNode(self, node):
18055a74a43SLisandro Dalcin        if not self.current_directives['embedsignature']:
18155a74a43SLisandro Dalcin            return node
18255a74a43SLisandro Dalcin
18355a74a43SLisandro Dalcin        is_constructor = False
18455a74a43SLisandro Dalcin        hide_self = False
18555a74a43SLisandro Dalcin        if node.entry.is_special:
18655a74a43SLisandro Dalcin            is_constructor = self.class_node and node.name == '__init__'
18755a74a43SLisandro Dalcin            if not is_constructor:
18855a74a43SLisandro Dalcin                return node
18955a74a43SLisandro Dalcin            class_name, func_name = None, self.class_name
19055a74a43SLisandro Dalcin            hide_self = True
19155a74a43SLisandro Dalcin        else:
19255a74a43SLisandro Dalcin            class_name, func_name = self.class_name, node.name
19355a74a43SLisandro Dalcin
19455a74a43SLisandro Dalcin        npoargs = getattr(node, 'num_posonly_args', 0)
19555a74a43SLisandro Dalcin        nkargs = getattr(node, 'num_kwonly_args', 0)
19655a74a43SLisandro Dalcin        npargs = len(node.args) - nkargs - npoargs
19755a74a43SLisandro Dalcin        signature = self._fmt_signature(
19855a74a43SLisandro Dalcin            class_name, func_name, node.args,
19955a74a43SLisandro Dalcin            npoargs, npargs, node.star_arg,
20055a74a43SLisandro Dalcin            nkargs, node.starstar_arg,
20155a74a43SLisandro Dalcin            return_expr=node.return_type_annotation,
20255a74a43SLisandro Dalcin            return_type=None, hide_self=hide_self)
20355a74a43SLisandro Dalcin        if signature:
20455a74a43SLisandro Dalcin            if is_constructor:
20555a74a43SLisandro Dalcin                doc_holder = self.class_node.entry.type.scope
20655a74a43SLisandro Dalcin            else:
20755a74a43SLisandro Dalcin                doc_holder = node.entry
20855a74a43SLisandro Dalcin
20955a74a43SLisandro Dalcin            if doc_holder.doc is not None:
21055a74a43SLisandro Dalcin                old_doc = doc_holder.doc
21155a74a43SLisandro Dalcin            elif not is_constructor and getattr(node, 'py_func', None) is not None:
21255a74a43SLisandro Dalcin                old_doc = node.py_func.entry.doc
21355a74a43SLisandro Dalcin            else:
21455a74a43SLisandro Dalcin                old_doc = None
215*b3f8c7a1SStefano Zampini            new_doc = self._embed_signature(signature, node.pos, old_doc)
21655a74a43SLisandro Dalcin            doc_holder.doc = EncodedString(new_doc)
21755a74a43SLisandro Dalcin            if not is_constructor and getattr(node, 'py_func', None) is not None:
21855a74a43SLisandro Dalcin                node.py_func.entry.doc = EncodedString(new_doc)
21955a74a43SLisandro Dalcin        return node
22055a74a43SLisandro Dalcin
22155a74a43SLisandro Dalcin    def visit_CFuncDefNode(self, node):
22255a74a43SLisandro Dalcin        if not self.current_directives['embedsignature']:
22355a74a43SLisandro Dalcin            return node
22455a74a43SLisandro Dalcin        if not node.overridable:  # not cpdef FOO(...):
22555a74a43SLisandro Dalcin            return node
22655a74a43SLisandro Dalcin
22755a74a43SLisandro Dalcin        signature = self._fmt_signature(
22855a74a43SLisandro Dalcin            self.class_name, node.declarator.base.name,
22955a74a43SLisandro Dalcin            node.declarator.args,
23055a74a43SLisandro Dalcin            return_type=node.return_type)
23155a74a43SLisandro Dalcin        if signature:
23255a74a43SLisandro Dalcin            if node.entry.doc is not None:
23355a74a43SLisandro Dalcin                old_doc = node.entry.doc
23455a74a43SLisandro Dalcin            elif getattr(node, 'py_func', None) is not None:
23555a74a43SLisandro Dalcin                old_doc = node.py_func.entry.doc
23655a74a43SLisandro Dalcin            else:
23755a74a43SLisandro Dalcin                old_doc = None
238*b3f8c7a1SStefano Zampini            new_doc = self._embed_signature(signature, node.pos, old_doc)
23955a74a43SLisandro Dalcin            node.entry.doc = EncodedString(new_doc)
24055a74a43SLisandro Dalcin            py_func = getattr(node, 'py_func', None)
24155a74a43SLisandro Dalcin            if py_func is not None:
24255a74a43SLisandro Dalcin                py_func.entry.doc = EncodedString(new_doc)
24355a74a43SLisandro Dalcin        return node
24455a74a43SLisandro Dalcin
24555a74a43SLisandro Dalcin    def visit_PropertyNode(self, node):
24655a74a43SLisandro Dalcin        if not self.current_directives['embedsignature']:
24755a74a43SLisandro Dalcin            return node
24855a74a43SLisandro Dalcin
24955a74a43SLisandro Dalcin        entry = node.entry
25055a74a43SLisandro Dalcin        body = node.body
25155a74a43SLisandro Dalcin        prop_name = entry.name
25255a74a43SLisandro Dalcin        type_name = None
25355a74a43SLisandro Dalcin        if entry.visibility == 'public':
25455a74a43SLisandro Dalcin            # property synthesised from a cdef public attribute
25555a74a43SLisandro Dalcin            type_name = entry.type.declaration_code("", for_display=1)
25655a74a43SLisandro Dalcin            if not entry.type.is_pyobject:
25755a74a43SLisandro Dalcin                type_name = "'%s'" % type_name
25855a74a43SLisandro Dalcin            elif entry.type.is_extension_type:
25955a74a43SLisandro Dalcin                type_name = entry.type.module_name + '.' + type_name
26055a74a43SLisandro Dalcin        if type_name is None:
26155a74a43SLisandro Dalcin            for stat in body.stats:
26255a74a43SLisandro Dalcin                if stat.name != '__get__':
26355a74a43SLisandro Dalcin                    continue
26455a74a43SLisandro Dalcin                cls_name = self.class_name
26555a74a43SLisandro Dalcin                if cls_name:
26655a74a43SLisandro Dalcin                    namespace = self._select_format('%s.' % cls_name, '')
26755a74a43SLisandro Dalcin                    prop_name = '%s%s' % (namespace, prop_name)
26855a74a43SLisandro Dalcin                ret_annotation = stat.return_type_annotation
26955a74a43SLisandro Dalcin                if ret_annotation:
27055a74a43SLisandro Dalcin                    type_name = self._fmt_annotation(ret_annotation)
27155a74a43SLisandro Dalcin        if type_name is not None:
27255a74a43SLisandro Dalcin            signature = '%s: %s' % (prop_name, type_name)
273*b3f8c7a1SStefano Zampini            new_doc = self._embed_signature(signature, node.pos, entry.doc)
27455a74a43SLisandro Dalcin            entry.doc = EncodedString(new_doc)
27555a74a43SLisandro Dalcin        return node
27655a74a43SLisandro Dalcin
27755a74a43SLisandro Dalcin
27855a74a43SLisandro Dalcin# Monkeypatch AutoDocTransforms.EmbedSignature
27955a74a43SLisandro Dalcintry:
28055a74a43SLisandro Dalcin    from Cython.Compiler import AutoDocTransforms
28155a74a43SLisandro Dalcin    AutoDocTransforms.EmbedSignature = EmbedSignature
28255a74a43SLisandro Dalcinexcept Exception as exc:
28355a74a43SLisandro Dalcin    import logging
28455a74a43SLisandro Dalcin    logging.Logger(__name__).exception(exc)
28555a74a43SLisandro Dalcin
28655a74a43SLisandro Dalcin# Monkeypatch Nodes.raise_utility_code
28755a74a43SLisandro Dalcintry:
28855a74a43SLisandro Dalcin    from Cython.Compiler.Nodes import raise_utility_code
28955a74a43SLisandro Dalcin    code = raise_utility_code.impl
29055a74a43SLisandro Dalcin    try:
29155a74a43SLisandro Dalcin        ipos = code.index("if (tb) {\n#if CYTHON_COMPILING_IN_PYPY\n")
29255a74a43SLisandro Dalcin    except ValueError:
29355a74a43SLisandro Dalcin        ipos = None
29455a74a43SLisandro Dalcin    else:
29555a74a43SLisandro Dalcin        raise_utility_code.impl = code[:ipos] + code[ipos:].replace(
29655a74a43SLisandro Dalcin            'CYTHON_COMPILING_IN_PYPY', '!CYTHON_FAST_THREAD_STATE', 1)
29755a74a43SLisandro Dalcin    del raise_utility_code, code, ipos
29855a74a43SLisandro Dalcinexcept Exception as exc:
29955a74a43SLisandro Dalcin    import logging
30055a74a43SLisandro Dalcin    logging.Logger(__name__).exception(exc)
301