xref: /petsc/src/binding/petsc4py/conf/cyautodoc.py (revision 55a74a43bb44613d95e937906bec3b8c3581b432)
1*55a74a43SLisandro Dalcin# ruff: noqa: UP008,UP031
2*55a74a43SLisandro Dalcinfrom Cython.Compiler import Options
3*55a74a43SLisandro Dalcinfrom Cython.Compiler import PyrexTypes
4*55a74a43SLisandro Dalcinfrom Cython.Compiler.ExprNodes import TupleNode
5*55a74a43SLisandro Dalcinfrom Cython.Compiler.Visitor import CythonTransform
6*55a74a43SLisandro Dalcinfrom Cython.Compiler.StringEncoding import EncodedString
7*55a74a43SLisandro Dalcinfrom Cython.Compiler.AutoDocTransforms import (
8*55a74a43SLisandro Dalcin    ExpressionWriter as BaseExpressionWriter,
9*55a74a43SLisandro Dalcin    AnnotationWriter as BaseAnnotationWriter,
10*55a74a43SLisandro Dalcin)
11*55a74a43SLisandro Dalcin
12*55a74a43SLisandro Dalcin
13*55a74a43SLisandro Dalcinclass ExpressionWriter(BaseExpressionWriter):
14*55a74a43SLisandro Dalcin
15*55a74a43SLisandro Dalcin    def visit_IndexNode(self, node):
16*55a74a43SLisandro Dalcin        self.visit(node.base)
17*55a74a43SLisandro Dalcin        self.put(u"[")
18*55a74a43SLisandro Dalcin        if isinstance(node.index, TupleNode):
19*55a74a43SLisandro Dalcin            if node.index.subexpr_nodes():
20*55a74a43SLisandro Dalcin                self.emit_sequence(node.index)
21*55a74a43SLisandro Dalcin            else:
22*55a74a43SLisandro Dalcin                self.put(u"()")
23*55a74a43SLisandro Dalcin        else:
24*55a74a43SLisandro Dalcin            self.visit(node.index)
25*55a74a43SLisandro Dalcin        self.put(u"]")
26*55a74a43SLisandro Dalcin
27*55a74a43SLisandro Dalcin    def visit_UnicodeNode(self, node):
28*55a74a43SLisandro Dalcin        self.emit_string(node, "")
29*55a74a43SLisandro Dalcin
30*55a74a43SLisandro Dalcin
31*55a74a43SLisandro Dalcinclass AnnotationWriter(ExpressionWriter, BaseAnnotationWriter):
32*55a74a43SLisandro Dalcin    pass
33*55a74a43SLisandro Dalcin
34*55a74a43SLisandro Dalcin
35*55a74a43SLisandro Dalcinclass EmbedSignature(CythonTransform):
36*55a74a43SLisandro Dalcin
37*55a74a43SLisandro Dalcin    def __init__(self, context):
38*55a74a43SLisandro Dalcin        super(EmbedSignature, self).__init__(context)
39*55a74a43SLisandro Dalcin        self.class_name = None
40*55a74a43SLisandro Dalcin        self.class_node = None
41*55a74a43SLisandro Dalcin
42*55a74a43SLisandro Dalcin    def _select_format(self, embed, clinic):
43*55a74a43SLisandro Dalcin        return embed
44*55a74a43SLisandro Dalcin
45*55a74a43SLisandro Dalcin    def _fmt_expr(self, node):
46*55a74a43SLisandro Dalcin        writer = ExpressionWriter()
47*55a74a43SLisandro Dalcin        result = writer.write(node)
48*55a74a43SLisandro Dalcin        # print(type(node).__name__, '-->', result)
49*55a74a43SLisandro Dalcin        return result
50*55a74a43SLisandro Dalcin
51*55a74a43SLisandro Dalcin    def _fmt_annotation(self, node):
52*55a74a43SLisandro Dalcin        writer = AnnotationWriter()
53*55a74a43SLisandro Dalcin        result = writer.write(node)
54*55a74a43SLisandro Dalcin        # print(type(node).__name__, '-->', result)
55*55a74a43SLisandro Dalcin        return result
56*55a74a43SLisandro Dalcin
57*55a74a43SLisandro Dalcin    def _fmt_arg(self, arg):
58*55a74a43SLisandro Dalcin        annotation = None
59*55a74a43SLisandro Dalcin        if arg.is_self_arg:
60*55a74a43SLisandro Dalcin            doc = self._select_format(arg.name, '$self')
61*55a74a43SLisandro Dalcin        elif arg.is_type_arg:
62*55a74a43SLisandro Dalcin            doc = self._select_format(arg.name, '$type')
63*55a74a43SLisandro Dalcin        else:
64*55a74a43SLisandro Dalcin            doc = arg.name
65*55a74a43SLisandro Dalcin            if arg.type is PyrexTypes.py_object_type:
66*55a74a43SLisandro Dalcin                annotation = None
67*55a74a43SLisandro Dalcin            else:
68*55a74a43SLisandro Dalcin                annotation = arg.type.declaration_code('', for_display=1)
69*55a74a43SLisandro Dalcin                if arg.default and arg.default.is_none:
70*55a74a43SLisandro Dalcin                    annotation += ' | None'
71*55a74a43SLisandro Dalcin        if arg.annotation:
72*55a74a43SLisandro Dalcin            annotation = self._fmt_annotation(arg.annotation)
73*55a74a43SLisandro Dalcin        annotation = self._select_format(annotation, None)
74*55a74a43SLisandro Dalcin        if annotation:
75*55a74a43SLisandro Dalcin            doc = doc + (': %s' % annotation)
76*55a74a43SLisandro Dalcin            if arg.default:
77*55a74a43SLisandro Dalcin                default = self._fmt_expr(arg.default)
78*55a74a43SLisandro Dalcin                doc = doc + (' = %s' % default)
79*55a74a43SLisandro Dalcin        elif arg.default:
80*55a74a43SLisandro Dalcin            default = self._fmt_expr(arg.default)
81*55a74a43SLisandro Dalcin            doc = doc + ('=%s' % default)
82*55a74a43SLisandro Dalcin        return doc
83*55a74a43SLisandro Dalcin
84*55a74a43SLisandro Dalcin    def _fmt_star_arg(self, arg):
85*55a74a43SLisandro Dalcin        arg_doc = arg.name
86*55a74a43SLisandro Dalcin        if arg.annotation:
87*55a74a43SLisandro Dalcin            annotation = self._fmt_annotation(arg.annotation)
88*55a74a43SLisandro Dalcin            arg_doc = arg_doc + (': %s' % annotation)
89*55a74a43SLisandro Dalcin        return arg_doc
90*55a74a43SLisandro Dalcin
91*55a74a43SLisandro Dalcin    def _fmt_arglist(self, args,
92*55a74a43SLisandro Dalcin                     npoargs=0, npargs=0, pargs=None,
93*55a74a43SLisandro Dalcin                     nkargs=0, kargs=None,
94*55a74a43SLisandro Dalcin                     hide_self=False):
95*55a74a43SLisandro Dalcin        arglist = []
96*55a74a43SLisandro Dalcin        for arg in args:
97*55a74a43SLisandro Dalcin            if not hide_self or not arg.entry.is_self_arg:
98*55a74a43SLisandro Dalcin                arg_doc = self._fmt_arg(arg)
99*55a74a43SLisandro Dalcin                arglist.append(arg_doc)
100*55a74a43SLisandro Dalcin        if pargs:
101*55a74a43SLisandro Dalcin            arg_doc = self._fmt_star_arg(pargs)
102*55a74a43SLisandro Dalcin            arglist.insert(npargs + npoargs, '*%s' % arg_doc)
103*55a74a43SLisandro Dalcin        elif nkargs:
104*55a74a43SLisandro Dalcin            arglist.insert(npargs + npoargs, '*')
105*55a74a43SLisandro Dalcin        if npoargs:
106*55a74a43SLisandro Dalcin            arglist.insert(npoargs, '/')
107*55a74a43SLisandro Dalcin        if kargs:
108*55a74a43SLisandro Dalcin            arg_doc = self._fmt_star_arg(kargs)
109*55a74a43SLisandro Dalcin            arglist.append('**%s' % arg_doc)
110*55a74a43SLisandro Dalcin        return arglist
111*55a74a43SLisandro Dalcin
112*55a74a43SLisandro Dalcin    def _fmt_ret_type(self, ret):
113*55a74a43SLisandro Dalcin        if ret is PyrexTypes.py_object_type:
114*55a74a43SLisandro Dalcin            return None
115*55a74a43SLisandro Dalcin        else:
116*55a74a43SLisandro Dalcin            return ret.declaration_code("", for_display=1)
117*55a74a43SLisandro Dalcin
118*55a74a43SLisandro Dalcin    def _fmt_signature(self, cls_name, func_name, args,
119*55a74a43SLisandro Dalcin                       npoargs=0, npargs=0, pargs=None,
120*55a74a43SLisandro Dalcin                       nkargs=0, kargs=None,
121*55a74a43SLisandro Dalcin                       return_expr=None,
122*55a74a43SLisandro Dalcin                       return_type=None, hide_self=False):
123*55a74a43SLisandro Dalcin        arglist = self._fmt_arglist(args,
124*55a74a43SLisandro Dalcin                                    npoargs, npargs, pargs,
125*55a74a43SLisandro Dalcin                                    nkargs, kargs,
126*55a74a43SLisandro Dalcin                                    hide_self=hide_self)
127*55a74a43SLisandro Dalcin        arglist_doc = ', '.join(arglist)
128*55a74a43SLisandro Dalcin        func_doc = '%s(%s)' % (func_name, arglist_doc)
129*55a74a43SLisandro Dalcin        if cls_name:
130*55a74a43SLisandro Dalcin            namespace = self._select_format('%s.' % cls_name, '')
131*55a74a43SLisandro Dalcin            func_doc = '%s%s' % (namespace, func_doc)
132*55a74a43SLisandro Dalcin        ret_doc = None
133*55a74a43SLisandro Dalcin        if return_expr:
134*55a74a43SLisandro Dalcin            ret_doc = self._fmt_annotation(return_expr)
135*55a74a43SLisandro Dalcin        elif return_type:
136*55a74a43SLisandro Dalcin            ret_doc = self._fmt_ret_type(return_type)
137*55a74a43SLisandro Dalcin        if ret_doc:
138*55a74a43SLisandro Dalcin            docfmt = self._select_format('%s -> %s', '%s -> (%s)')
139*55a74a43SLisandro Dalcin            func_doc = docfmt % (func_doc, ret_doc)
140*55a74a43SLisandro Dalcin        return func_doc
141*55a74a43SLisandro Dalcin
142*55a74a43SLisandro Dalcin    def _embed_signature(self, signature, node_doc):
143*55a74a43SLisandro Dalcin        if node_doc:
144*55a74a43SLisandro Dalcin            docfmt = self._select_format("%s\n%s", "%s\n--\n\n%s")
145*55a74a43SLisandro Dalcin            return docfmt % (signature, node_doc)
146*55a74a43SLisandro Dalcin        else:
147*55a74a43SLisandro Dalcin            return signature
148*55a74a43SLisandro Dalcin
149*55a74a43SLisandro Dalcin    def __call__(self, node):
150*55a74a43SLisandro Dalcin        if not Options.docstrings:
151*55a74a43SLisandro Dalcin            return node
152*55a74a43SLisandro Dalcin        else:
153*55a74a43SLisandro Dalcin            return super(EmbedSignature, self).__call__(node)
154*55a74a43SLisandro Dalcin
155*55a74a43SLisandro Dalcin    def visit_ClassDefNode(self, node):
156*55a74a43SLisandro Dalcin        oldname = self.class_name
157*55a74a43SLisandro Dalcin        oldclass = self.class_node
158*55a74a43SLisandro Dalcin        self.class_node = node
159*55a74a43SLisandro Dalcin        try:
160*55a74a43SLisandro Dalcin            # PyClassDefNode
161*55a74a43SLisandro Dalcin            self.class_name = node.name
162*55a74a43SLisandro Dalcin        except AttributeError:
163*55a74a43SLisandro Dalcin            # CClassDefNode
164*55a74a43SLisandro Dalcin            self.class_name = node.class_name
165*55a74a43SLisandro Dalcin        self.visitchildren(node)
166*55a74a43SLisandro Dalcin        self.class_name = oldname
167*55a74a43SLisandro Dalcin        self.class_node = oldclass
168*55a74a43SLisandro Dalcin        return node
169*55a74a43SLisandro Dalcin
170*55a74a43SLisandro Dalcin    def visit_LambdaNode(self, node):
171*55a74a43SLisandro Dalcin        # lambda expressions so not have signature or inner functions
172*55a74a43SLisandro Dalcin        return node
173*55a74a43SLisandro Dalcin
174*55a74a43SLisandro Dalcin    def visit_DefNode(self, node):
175*55a74a43SLisandro Dalcin        if not self.current_directives['embedsignature']:
176*55a74a43SLisandro Dalcin            return node
177*55a74a43SLisandro Dalcin
178*55a74a43SLisandro Dalcin        is_constructor = False
179*55a74a43SLisandro Dalcin        hide_self = False
180*55a74a43SLisandro Dalcin        if node.entry.is_special:
181*55a74a43SLisandro Dalcin            is_constructor = self.class_node and node.name == '__init__'
182*55a74a43SLisandro Dalcin            if not is_constructor:
183*55a74a43SLisandro Dalcin                return node
184*55a74a43SLisandro Dalcin            class_name, func_name = None, self.class_name
185*55a74a43SLisandro Dalcin            hide_self = True
186*55a74a43SLisandro Dalcin        else:
187*55a74a43SLisandro Dalcin            class_name, func_name = self.class_name, node.name
188*55a74a43SLisandro Dalcin
189*55a74a43SLisandro Dalcin        npoargs = getattr(node, 'num_posonly_args', 0)
190*55a74a43SLisandro Dalcin        nkargs = getattr(node, 'num_kwonly_args', 0)
191*55a74a43SLisandro Dalcin        npargs = len(node.args) - nkargs - npoargs
192*55a74a43SLisandro Dalcin        signature = self._fmt_signature(
193*55a74a43SLisandro Dalcin            class_name, func_name, node.args,
194*55a74a43SLisandro Dalcin            npoargs, npargs, node.star_arg,
195*55a74a43SLisandro Dalcin            nkargs, node.starstar_arg,
196*55a74a43SLisandro Dalcin            return_expr=node.return_type_annotation,
197*55a74a43SLisandro Dalcin            return_type=None, hide_self=hide_self)
198*55a74a43SLisandro Dalcin        if signature:
199*55a74a43SLisandro Dalcin            if is_constructor:
200*55a74a43SLisandro Dalcin                doc_holder = self.class_node.entry.type.scope
201*55a74a43SLisandro Dalcin            else:
202*55a74a43SLisandro Dalcin                doc_holder = node.entry
203*55a74a43SLisandro Dalcin
204*55a74a43SLisandro Dalcin            if doc_holder.doc is not None:
205*55a74a43SLisandro Dalcin                old_doc = doc_holder.doc
206*55a74a43SLisandro Dalcin            elif not is_constructor and getattr(node, 'py_func', None) is not None:
207*55a74a43SLisandro Dalcin                old_doc = node.py_func.entry.doc
208*55a74a43SLisandro Dalcin            else:
209*55a74a43SLisandro Dalcin                old_doc = None
210*55a74a43SLisandro Dalcin            new_doc = self._embed_signature(signature, old_doc)
211*55a74a43SLisandro Dalcin            doc_holder.doc = EncodedString(new_doc)
212*55a74a43SLisandro Dalcin            if not is_constructor and getattr(node, 'py_func', None) is not None:
213*55a74a43SLisandro Dalcin                node.py_func.entry.doc = EncodedString(new_doc)
214*55a74a43SLisandro Dalcin        return node
215*55a74a43SLisandro Dalcin
216*55a74a43SLisandro Dalcin    def visit_CFuncDefNode(self, node):
217*55a74a43SLisandro Dalcin        if not self.current_directives['embedsignature']:
218*55a74a43SLisandro Dalcin            return node
219*55a74a43SLisandro Dalcin        if not node.overridable:  # not cpdef FOO(...):
220*55a74a43SLisandro Dalcin            return node
221*55a74a43SLisandro Dalcin
222*55a74a43SLisandro Dalcin        signature = self._fmt_signature(
223*55a74a43SLisandro Dalcin            self.class_name, node.declarator.base.name,
224*55a74a43SLisandro Dalcin            node.declarator.args,
225*55a74a43SLisandro Dalcin            return_type=node.return_type)
226*55a74a43SLisandro Dalcin        if signature:
227*55a74a43SLisandro Dalcin            if node.entry.doc is not None:
228*55a74a43SLisandro Dalcin                old_doc = node.entry.doc
229*55a74a43SLisandro Dalcin            elif getattr(node, 'py_func', None) is not None:
230*55a74a43SLisandro Dalcin                old_doc = node.py_func.entry.doc
231*55a74a43SLisandro Dalcin            else:
232*55a74a43SLisandro Dalcin                old_doc = None
233*55a74a43SLisandro Dalcin            new_doc = self._embed_signature(signature, old_doc)
234*55a74a43SLisandro Dalcin            node.entry.doc = EncodedString(new_doc)
235*55a74a43SLisandro Dalcin            py_func = getattr(node, 'py_func', None)
236*55a74a43SLisandro Dalcin            if py_func is not None:
237*55a74a43SLisandro Dalcin                py_func.entry.doc = EncodedString(new_doc)
238*55a74a43SLisandro Dalcin        return node
239*55a74a43SLisandro Dalcin
240*55a74a43SLisandro Dalcin    def visit_PropertyNode(self, node):
241*55a74a43SLisandro Dalcin        if not self.current_directives['embedsignature']:
242*55a74a43SLisandro Dalcin            return node
243*55a74a43SLisandro Dalcin
244*55a74a43SLisandro Dalcin        entry = node.entry
245*55a74a43SLisandro Dalcin        body = node.body
246*55a74a43SLisandro Dalcin        prop_name = entry.name
247*55a74a43SLisandro Dalcin        type_name = None
248*55a74a43SLisandro Dalcin        if entry.visibility == 'public':
249*55a74a43SLisandro Dalcin            # property synthesised from a cdef public attribute
250*55a74a43SLisandro Dalcin            type_name = entry.type.declaration_code("", for_display=1)
251*55a74a43SLisandro Dalcin            if not entry.type.is_pyobject:
252*55a74a43SLisandro Dalcin                type_name = "'%s'" % type_name
253*55a74a43SLisandro Dalcin            elif entry.type.is_extension_type:
254*55a74a43SLisandro Dalcin                type_name = entry.type.module_name + '.' + type_name
255*55a74a43SLisandro Dalcin        if type_name is None:
256*55a74a43SLisandro Dalcin            for stat in body.stats:
257*55a74a43SLisandro Dalcin                if stat.name != '__get__':
258*55a74a43SLisandro Dalcin                    continue
259*55a74a43SLisandro Dalcin                cls_name = self.class_name
260*55a74a43SLisandro Dalcin                if cls_name:
261*55a74a43SLisandro Dalcin                    namespace = self._select_format('%s.' % cls_name, '')
262*55a74a43SLisandro Dalcin                    prop_name = '%s%s' % (namespace, prop_name)
263*55a74a43SLisandro Dalcin                ret_annotation = stat.return_type_annotation
264*55a74a43SLisandro Dalcin                if ret_annotation:
265*55a74a43SLisandro Dalcin                    type_name = self._fmt_annotation(ret_annotation)
266*55a74a43SLisandro Dalcin        if type_name is not None:
267*55a74a43SLisandro Dalcin            signature = '%s: %s' % (prop_name, type_name)
268*55a74a43SLisandro Dalcin            new_doc = self._embed_signature(signature, entry.doc)
269*55a74a43SLisandro Dalcin            entry.doc = EncodedString(new_doc)
270*55a74a43SLisandro Dalcin        return node
271*55a74a43SLisandro Dalcin
272*55a74a43SLisandro Dalcin
273*55a74a43SLisandro Dalcin# Monkeypatch AutoDocTransforms.EmbedSignature
274*55a74a43SLisandro Dalcintry:
275*55a74a43SLisandro Dalcin    from Cython.Compiler import AutoDocTransforms
276*55a74a43SLisandro Dalcin    AutoDocTransforms.EmbedSignature = EmbedSignature
277*55a74a43SLisandro Dalcinexcept Exception as exc:
278*55a74a43SLisandro Dalcin    import logging
279*55a74a43SLisandro Dalcin    logging.Logger(__name__).exception(exc)
280*55a74a43SLisandro Dalcin
281*55a74a43SLisandro Dalcin# Monkeypatch Nodes.raise_utility_code
282*55a74a43SLisandro Dalcintry:
283*55a74a43SLisandro Dalcin    from Cython.Compiler.Nodes import raise_utility_code
284*55a74a43SLisandro Dalcin    code = raise_utility_code.impl
285*55a74a43SLisandro Dalcin    try:
286*55a74a43SLisandro Dalcin        ipos = code.index("if (tb) {\n#if CYTHON_COMPILING_IN_PYPY\n")
287*55a74a43SLisandro Dalcin    except ValueError:
288*55a74a43SLisandro Dalcin        ipos = None
289*55a74a43SLisandro Dalcin    else:
290*55a74a43SLisandro Dalcin        raise_utility_code.impl = code[:ipos] + code[ipos:].replace(
291*55a74a43SLisandro Dalcin            'CYTHON_COMPILING_IN_PYPY', '!CYTHON_FAST_THREAD_STATE', 1)
292*55a74a43SLisandro Dalcin    del raise_utility_code, code, ipos
293*55a74a43SLisandro Dalcinexcept Exception as exc:
294*55a74a43SLisandro Dalcin    import logging
295*55a74a43SLisandro Dalcin    logging.Logger(__name__).exception(exc)
296