xref: /petsc/src/binding/petsc4py/docs/source/apidoc.py (revision 55a74a43bb44613d95e937906bec3b8c3581b432)
1*55a74a43SLisandro Dalcinimport os
2*55a74a43SLisandro Dalcinimport sys
3*55a74a43SLisandro Dalcinimport inspect
4*55a74a43SLisandro Dalcinimport textwrap
5*55a74a43SLisandro Dalcin
6*55a74a43SLisandro Dalcin
7*55a74a43SLisandro Dalcindef is_cyfunction(obj):
8*55a74a43SLisandro Dalcin    return type(obj).__name__ == 'cython_function_or_method'
9*55a74a43SLisandro Dalcin
10*55a74a43SLisandro Dalcin
11*55a74a43SLisandro Dalcindef is_function(obj):
12*55a74a43SLisandro Dalcin    return (
13*55a74a43SLisandro Dalcin        inspect.isbuiltin(obj)
14*55a74a43SLisandro Dalcin        or is_cyfunction(obj)
15*55a74a43SLisandro Dalcin        or type(obj) is type(ord)
16*55a74a43SLisandro Dalcin    )
17*55a74a43SLisandro Dalcin
18*55a74a43SLisandro Dalcin
19*55a74a43SLisandro Dalcindef is_method(obj):
20*55a74a43SLisandro Dalcin    return (
21*55a74a43SLisandro Dalcin        inspect.ismethoddescriptor(obj)
22*55a74a43SLisandro Dalcin        or inspect.ismethod(obj)
23*55a74a43SLisandro Dalcin        or is_cyfunction(obj)
24*55a74a43SLisandro Dalcin        or type(obj) in (
25*55a74a43SLisandro Dalcin            type(str.index),
26*55a74a43SLisandro Dalcin            type(str.__add__),
27*55a74a43SLisandro Dalcin            type(str.__new__),
28*55a74a43SLisandro Dalcin        )
29*55a74a43SLisandro Dalcin    )
30*55a74a43SLisandro Dalcin
31*55a74a43SLisandro Dalcin
32*55a74a43SLisandro Dalcindef is_classmethod(obj):
33*55a74a43SLisandro Dalcin    return (
34*55a74a43SLisandro Dalcin        inspect.isbuiltin(obj)
35*55a74a43SLisandro Dalcin        or type(obj).__name__ in (
36*55a74a43SLisandro Dalcin            'classmethod',
37*55a74a43SLisandro Dalcin            'classmethod_descriptor',
38*55a74a43SLisandro Dalcin        )
39*55a74a43SLisandro Dalcin    )
40*55a74a43SLisandro Dalcin
41*55a74a43SLisandro Dalcin
42*55a74a43SLisandro Dalcindef is_staticmethod(obj):
43*55a74a43SLisandro Dalcin    return (
44*55a74a43SLisandro Dalcin        type(obj).__name__ in (
45*55a74a43SLisandro Dalcin            'staticmethod',
46*55a74a43SLisandro Dalcin        )
47*55a74a43SLisandro Dalcin    )
48*55a74a43SLisandro Dalcin
49*55a74a43SLisandro Dalcindef is_constant(obj):
50*55a74a43SLisandro Dalcin    return isinstance(obj, (int, float, str, dict))
51*55a74a43SLisandro Dalcin
52*55a74a43SLisandro Dalcindef is_datadescr(obj):
53*55a74a43SLisandro Dalcin    return inspect.isdatadescriptor(obj) and not hasattr(obj, 'fget')
54*55a74a43SLisandro Dalcin
55*55a74a43SLisandro Dalcin
56*55a74a43SLisandro Dalcindef is_property(obj):
57*55a74a43SLisandro Dalcin    return inspect.isdatadescriptor(obj) and hasattr(obj, 'fget')
58*55a74a43SLisandro Dalcin
59*55a74a43SLisandro Dalcin
60*55a74a43SLisandro Dalcindef is_class(obj):
61*55a74a43SLisandro Dalcin    return inspect.isclass(obj) or type(obj) is type(int)
62*55a74a43SLisandro Dalcin
63*55a74a43SLisandro Dalcin
64*55a74a43SLisandro Dalcinclass Lines(list):
65*55a74a43SLisandro Dalcin
66*55a74a43SLisandro Dalcin    INDENT = " " * 4
67*55a74a43SLisandro Dalcin    level = 0
68*55a74a43SLisandro Dalcin
69*55a74a43SLisandro Dalcin    @property
70*55a74a43SLisandro Dalcin    def add(self):
71*55a74a43SLisandro Dalcin        return self
72*55a74a43SLisandro Dalcin
73*55a74a43SLisandro Dalcin    @add.setter
74*55a74a43SLisandro Dalcin    def add(self, lines):
75*55a74a43SLisandro Dalcin        if lines is None:
76*55a74a43SLisandro Dalcin            return
77*55a74a43SLisandro Dalcin        if isinstance(lines, str):
78*55a74a43SLisandro Dalcin            lines = textwrap.dedent(lines).strip().split('\n')
79*55a74a43SLisandro Dalcin        indent = self.INDENT * self.level
80*55a74a43SLisandro Dalcin        for line in lines:
81*55a74a43SLisandro Dalcin            self.append(indent + line)
82*55a74a43SLisandro Dalcin
83*55a74a43SLisandro Dalcin
84*55a74a43SLisandro Dalcindef signature(obj):
85*55a74a43SLisandro Dalcin    doc = obj.__doc__
86*55a74a43SLisandro Dalcin    doc = doc or f"{obj.__name__}: Any"  # FIXME remove line
87*55a74a43SLisandro Dalcin    sig = doc.partition('\n')[0].split('.', 1)[-1]
88*55a74a43SLisandro Dalcin    return sig or None
89*55a74a43SLisandro Dalcin
90*55a74a43SLisandro Dalcin
91*55a74a43SLisandro Dalcindef docstring(obj):
92*55a74a43SLisandro Dalcin    doc = obj.__doc__
93*55a74a43SLisandro Dalcin    doc = doc or '' # FIXME
94*55a74a43SLisandro Dalcin    if is_class(obj):
95*55a74a43SLisandro Dalcin        doc = doc.strip()
96*55a74a43SLisandro Dalcin    else:
97*55a74a43SLisandro Dalcin        doc = doc.partition('\n')[2]
98*55a74a43SLisandro Dalcin    summary, _, docbody = doc.partition('\n')
99*55a74a43SLisandro Dalcin    summary = summary.strip()
100*55a74a43SLisandro Dalcin    docbody = textwrap.dedent(docbody).strip()
101*55a74a43SLisandro Dalcin    if docbody:
102*55a74a43SLisandro Dalcin        doc = f'"""{summary}\n\n{docbody}\n\n"""'
103*55a74a43SLisandro Dalcin    else:
104*55a74a43SLisandro Dalcin        doc = f'"""{summary}"""'
105*55a74a43SLisandro Dalcin    doc = textwrap.indent(doc, Lines.INDENT)
106*55a74a43SLisandro Dalcin    return doc
107*55a74a43SLisandro Dalcin
108*55a74a43SLisandro Dalcin
109*55a74a43SLisandro Dalcindef visit_data(constant):
110*55a74a43SLisandro Dalcin    name, value = constant
111*55a74a43SLisandro Dalcin    typename = type(value).__name__
112*55a74a43SLisandro Dalcin    kind = "Constant" if isinstance(value, int) else "Object"
113*55a74a43SLisandro Dalcin    init = f"_def({typename}, '{name}')"
114*55a74a43SLisandro Dalcin    doc = f"#: {kind} ``{name}`` of type :class:`{typename}`"
115*55a74a43SLisandro Dalcin    return f"{name}: {typename} = {init}  {doc}\n"
116*55a74a43SLisandro Dalcin
117*55a74a43SLisandro Dalcin
118*55a74a43SLisandro Dalcindef visit_function(function):
119*55a74a43SLisandro Dalcin    sig = signature(function)
120*55a74a43SLisandro Dalcin    doc = docstring(function)
121*55a74a43SLisandro Dalcin    body = Lines.INDENT + "..."
122*55a74a43SLisandro Dalcin    return f"def {sig}:\n{doc}\n{body}\n"
123*55a74a43SLisandro Dalcin
124*55a74a43SLisandro Dalcin
125*55a74a43SLisandro Dalcindef visit_method(method):
126*55a74a43SLisandro Dalcin    sig = signature(method)
127*55a74a43SLisandro Dalcin    doc = docstring(method)
128*55a74a43SLisandro Dalcin    body = Lines.INDENT + "..."
129*55a74a43SLisandro Dalcin    return f"def {sig}:\n{doc}\n{body}\n"
130*55a74a43SLisandro Dalcin
131*55a74a43SLisandro Dalcin
132*55a74a43SLisandro Dalcindef visit_datadescr(datadescr, name=None):
133*55a74a43SLisandro Dalcin    sig = signature(datadescr)
134*55a74a43SLisandro Dalcin    doc = docstring(datadescr)
135*55a74a43SLisandro Dalcin    name = sig.partition(':')[0].strip() or datadescr.__name__
136*55a74a43SLisandro Dalcin    type = sig.partition(':')[2].strip() or 'Any'
137*55a74a43SLisandro Dalcin    sig = f"{name}(self) -> {type}"
138*55a74a43SLisandro Dalcin    body = Lines.INDENT + "..."
139*55a74a43SLisandro Dalcin    return f"@property\ndef {sig}:\n{doc}\n{body}\n"
140*55a74a43SLisandro Dalcin
141*55a74a43SLisandro Dalcin
142*55a74a43SLisandro Dalcindef visit_property(prop, name=None):
143*55a74a43SLisandro Dalcin    sig = signature(prop.fget)
144*55a74a43SLisandro Dalcin    name = name or prop.fget.__name__
145*55a74a43SLisandro Dalcin    type = sig.rsplit('->', 1)[-1].strip()
146*55a74a43SLisandro Dalcin    sig = f"{name}(self) -> {type}"
147*55a74a43SLisandro Dalcin    doc = f'"""{prop.__doc__}"""'
148*55a74a43SLisandro Dalcin    doc = textwrap.indent(doc, Lines.INDENT)
149*55a74a43SLisandro Dalcin    body = Lines.INDENT + "..."
150*55a74a43SLisandro Dalcin    return f"@property\ndef {sig}:\n{doc}\n{body}\n"
151*55a74a43SLisandro Dalcin
152*55a74a43SLisandro Dalcin
153*55a74a43SLisandro Dalcindef visit_constructor(cls, name='__init__', args=None):
154*55a74a43SLisandro Dalcin    init = (name == '__init__')
155*55a74a43SLisandro Dalcin    argname = cls.__mro__[-2].__name__.lower()
156*55a74a43SLisandro Dalcin    argtype = cls.__name__
157*55a74a43SLisandro Dalcin    initarg = args or f"{argname}: Optional[{argtype}] = None"
158*55a74a43SLisandro Dalcin    selfarg = 'self' if init else 'cls'
159*55a74a43SLisandro Dalcin    rettype = 'None' if init else argtype
160*55a74a43SLisandro Dalcin    arglist = f"{selfarg}, {initarg}"
161*55a74a43SLisandro Dalcin    sig = f"{name}({arglist}) -> {rettype}"
162*55a74a43SLisandro Dalcin    ret = '...' if init else 'return super().__new__(cls)'
163*55a74a43SLisandro Dalcin    body = Lines.INDENT + ret
164*55a74a43SLisandro Dalcin    return f"def {sig}:\n{body}"
165*55a74a43SLisandro Dalcin
166*55a74a43SLisandro Dalcin
167*55a74a43SLisandro Dalcindef visit_class(cls, outer=None, done=None):
168*55a74a43SLisandro Dalcin    skip = {
169*55a74a43SLisandro Dalcin        '__doc__',
170*55a74a43SLisandro Dalcin        '__dict__',
171*55a74a43SLisandro Dalcin        '__module__',
172*55a74a43SLisandro Dalcin        '__weakref__',
173*55a74a43SLisandro Dalcin        '__pyx_vtable__',
174*55a74a43SLisandro Dalcin        '__lt__',
175*55a74a43SLisandro Dalcin        '__le__',
176*55a74a43SLisandro Dalcin        '__ge__',
177*55a74a43SLisandro Dalcin        '__gt__',
178*55a74a43SLisandro Dalcin        '__enum2str',  # FIXME refactor implemetation
179*55a74a43SLisandro Dalcin        '_traceback_', # FIXME maybe refactor?
180*55a74a43SLisandro Dalcin    }
181*55a74a43SLisandro Dalcin    special = {
182*55a74a43SLisandro Dalcin        '__len__': "__len__(self) -> int",
183*55a74a43SLisandro Dalcin        '__bool__': "__bool__(self) -> bool",
184*55a74a43SLisandro Dalcin        '__hash__': "__hash__(self) -> int",
185*55a74a43SLisandro Dalcin        '__int__': "__int__(self) -> int",
186*55a74a43SLisandro Dalcin        '__index__': "__int__(self) -> int",
187*55a74a43SLisandro Dalcin        '__str__': "__str__(self) -> str",
188*55a74a43SLisandro Dalcin        '__repr__': "__repr__(self) -> str",
189*55a74a43SLisandro Dalcin        '__eq__': "__eq__(self, other: object) -> bool",
190*55a74a43SLisandro Dalcin        '__ne__': "__ne__(self, other: object) -> bool",
191*55a74a43SLisandro Dalcin    }
192*55a74a43SLisandro Dalcin    constructor = (
193*55a74a43SLisandro Dalcin        '__new__',
194*55a74a43SLisandro Dalcin        '__init__',
195*55a74a43SLisandro Dalcin    )
196*55a74a43SLisandro Dalcin
197*55a74a43SLisandro Dalcin    qualname = cls.__name__
198*55a74a43SLisandro Dalcin    cls_name = cls.__name__
199*55a74a43SLisandro Dalcin    if outer is not None and cls_name.startswith(outer):
200*55a74a43SLisandro Dalcin        cls_name = cls_name[len(outer):]
201*55a74a43SLisandro Dalcin        qualname = f"{outer}.{cls_name}"
202*55a74a43SLisandro Dalcin
203*55a74a43SLisandro Dalcin    override = OVERRIDE.get(qualname, {})
204*55a74a43SLisandro Dalcin    done = set() if done is None else done
205*55a74a43SLisandro Dalcin    lines = Lines()
206*55a74a43SLisandro Dalcin
207*55a74a43SLisandro Dalcin    base = cls.__base__
208*55a74a43SLisandro Dalcin    if base is object:
209*55a74a43SLisandro Dalcin        lines.add = f"class {cls_name}:"
210*55a74a43SLisandro Dalcin    else:
211*55a74a43SLisandro Dalcin        lines.add = f"class {cls_name}({base.__name__}):"
212*55a74a43SLisandro Dalcin    lines.level += 1
213*55a74a43SLisandro Dalcin
214*55a74a43SLisandro Dalcin    lines.add = docstring(cls)
215*55a74a43SLisandro Dalcin
216*55a74a43SLisandro Dalcin    for name in ('__new__', '__init__', '__hash__'):
217*55a74a43SLisandro Dalcin        if name in cls.__dict__:
218*55a74a43SLisandro Dalcin            done.add(name)
219*55a74a43SLisandro Dalcin
220*55a74a43SLisandro Dalcin    dct = cls.__dict__
221*55a74a43SLisandro Dalcin    keys = list(dct.keys())
222*55a74a43SLisandro Dalcin
223*55a74a43SLisandro Dalcin    def dunder(name):
224*55a74a43SLisandro Dalcin        return name.startswith('__') and name.endswith('__')
225*55a74a43SLisandro Dalcin
226*55a74a43SLisandro Dalcin    def members(seq):
227*55a74a43SLisandro Dalcin        for name in seq:
228*55a74a43SLisandro Dalcin            if name in skip:
229*55a74a43SLisandro Dalcin                continue
230*55a74a43SLisandro Dalcin            if name in done:
231*55a74a43SLisandro Dalcin                continue
232*55a74a43SLisandro Dalcin            if dunder(name):
233*55a74a43SLisandro Dalcin                if name not in special and name not in override:
234*55a74a43SLisandro Dalcin                    done.add(name)
235*55a74a43SLisandro Dalcin                    continue
236*55a74a43SLisandro Dalcin            yield name
237*55a74a43SLisandro Dalcin
238*55a74a43SLisandro Dalcin    for name in members(keys):
239*55a74a43SLisandro Dalcin        attr = getattr(cls, name)
240*55a74a43SLisandro Dalcin        if is_class(attr):
241*55a74a43SLisandro Dalcin            done.add(name)
242*55a74a43SLisandro Dalcin            lines.add = visit_class(attr, outer=cls_name)
243*55a74a43SLisandro Dalcin            continue
244*55a74a43SLisandro Dalcin
245*55a74a43SLisandro Dalcin    for name in members(keys):
246*55a74a43SLisandro Dalcin
247*55a74a43SLisandro Dalcin        if name in override:
248*55a74a43SLisandro Dalcin            done.add(name)
249*55a74a43SLisandro Dalcin            lines.add = override[name]
250*55a74a43SLisandro Dalcin            continue
251*55a74a43SLisandro Dalcin
252*55a74a43SLisandro Dalcin        if name in special:
253*55a74a43SLisandro Dalcin            done.add(name)
254*55a74a43SLisandro Dalcin            sig = special[name]
255*55a74a43SLisandro Dalcin            lines.add = f"def {sig}: ..."
256*55a74a43SLisandro Dalcin            continue
257*55a74a43SLisandro Dalcin
258*55a74a43SLisandro Dalcin        attr = getattr(cls, name)
259*55a74a43SLisandro Dalcin
260*55a74a43SLisandro Dalcin        if is_method(attr):
261*55a74a43SLisandro Dalcin            done.add(name)
262*55a74a43SLisandro Dalcin            if name == attr.__name__:
263*55a74a43SLisandro Dalcin                obj = dct[name]
264*55a74a43SLisandro Dalcin                if is_classmethod(obj):
265*55a74a43SLisandro Dalcin                    lines.add = "@classmethod"
266*55a74a43SLisandro Dalcin                elif is_staticmethod(obj):
267*55a74a43SLisandro Dalcin                    lines.add = "@staticmethod"
268*55a74a43SLisandro Dalcin                lines.add = visit_method(attr)
269*55a74a43SLisandro Dalcin            elif False:
270*55a74a43SLisandro Dalcin                lines.add = f"{name} = {attr.__name__}"
271*55a74a43SLisandro Dalcin            continue
272*55a74a43SLisandro Dalcin
273*55a74a43SLisandro Dalcin        if is_datadescr(attr):
274*55a74a43SLisandro Dalcin            done.add(name)
275*55a74a43SLisandro Dalcin            lines.add = visit_datadescr(attr)
276*55a74a43SLisandro Dalcin            continue
277*55a74a43SLisandro Dalcin
278*55a74a43SLisandro Dalcin        if is_property(attr):
279*55a74a43SLisandro Dalcin            done.add(name)
280*55a74a43SLisandro Dalcin            lines.add = visit_property(attr, name)
281*55a74a43SLisandro Dalcin            continue
282*55a74a43SLisandro Dalcin
283*55a74a43SLisandro Dalcin        if is_constant(attr):
284*55a74a43SLisandro Dalcin            done.add(name)
285*55a74a43SLisandro Dalcin            lines.add = visit_data((name, attr))
286*55a74a43SLisandro Dalcin            continue
287*55a74a43SLisandro Dalcin
288*55a74a43SLisandro Dalcin    leftovers = [name for name in keys if
289*55a74a43SLisandro Dalcin                 name not in done and name not in skip]
290*55a74a43SLisandro Dalcin    if leftovers:
291*55a74a43SLisandro Dalcin        raise RuntimeError(f"leftovers: {leftovers}")
292*55a74a43SLisandro Dalcin
293*55a74a43SLisandro Dalcin    lines.level -= 1
294*55a74a43SLisandro Dalcin    return lines
295*55a74a43SLisandro Dalcin
296*55a74a43SLisandro Dalcin
297*55a74a43SLisandro Dalcindef visit_module(module, done=None):
298*55a74a43SLisandro Dalcin    skip = {
299*55a74a43SLisandro Dalcin        '__doc__',
300*55a74a43SLisandro Dalcin        '__name__',
301*55a74a43SLisandro Dalcin        '__loader__',
302*55a74a43SLisandro Dalcin        '__spec__',
303*55a74a43SLisandro Dalcin        '__file__',
304*55a74a43SLisandro Dalcin        '__package__',
305*55a74a43SLisandro Dalcin        '__builtins__',
306*55a74a43SLisandro Dalcin        '__pyx_capi__',
307*55a74a43SLisandro Dalcin        '__pyx_unpickle_Enum',  # FIXME review
308*55a74a43SLisandro Dalcin    }
309*55a74a43SLisandro Dalcin
310*55a74a43SLisandro Dalcin    done = set() if done is None else done
311*55a74a43SLisandro Dalcin    lines = Lines()
312*55a74a43SLisandro Dalcin
313*55a74a43SLisandro Dalcin    keys = list(module.__dict__.keys())
314*55a74a43SLisandro Dalcin    keys.sort(key=lambda name: name.startswith("_"))
315*55a74a43SLisandro Dalcin
316*55a74a43SLisandro Dalcin    constants = [
317*55a74a43SLisandro Dalcin        (name, getattr(module, name)) for name in keys
318*55a74a43SLisandro Dalcin        if all((
319*55a74a43SLisandro Dalcin            name not in done and name not in skip,
320*55a74a43SLisandro Dalcin            is_constant(getattr(module, name)),
321*55a74a43SLisandro Dalcin        ))
322*55a74a43SLisandro Dalcin    ]
323*55a74a43SLisandro Dalcin    for _, value in constants:
324*55a74a43SLisandro Dalcin        cls = type(value)
325*55a74a43SLisandro Dalcin        name = cls.__name__
326*55a74a43SLisandro Dalcin        if name in done or name in skip:
327*55a74a43SLisandro Dalcin            continue
328*55a74a43SLisandro Dalcin        if cls.__module__ == module.__name__:
329*55a74a43SLisandro Dalcin            done.add(name)
330*55a74a43SLisandro Dalcin            lines.add = visit_class(cls)
331*55a74a43SLisandro Dalcin            lines.add = ""
332*55a74a43SLisandro Dalcin    for attr in constants:
333*55a74a43SLisandro Dalcin        name, value = attr
334*55a74a43SLisandro Dalcin        done.add(name)
335*55a74a43SLisandro Dalcin        if name in OVERRIDE:
336*55a74a43SLisandro Dalcin            lines.add = OVERRIDE[name]
337*55a74a43SLisandro Dalcin        else:
338*55a74a43SLisandro Dalcin            lines.add = visit_data((name, value))
339*55a74a43SLisandro Dalcin    if constants:
340*55a74a43SLisandro Dalcin        lines.add = ""
341*55a74a43SLisandro Dalcin
342*55a74a43SLisandro Dalcin    for name in keys:
343*55a74a43SLisandro Dalcin        if name in done or name in skip:
344*55a74a43SLisandro Dalcin            continue
345*55a74a43SLisandro Dalcin        value = getattr(module, name)
346*55a74a43SLisandro Dalcin
347*55a74a43SLisandro Dalcin        if is_class(value):
348*55a74a43SLisandro Dalcin            done.add(name)
349*55a74a43SLisandro Dalcin            if value.__name__ != name:
350*55a74a43SLisandro Dalcin                continue
351*55a74a43SLisandro Dalcin            if value.__module__ != module.__name__:
352*55a74a43SLisandro Dalcin                continue
353*55a74a43SLisandro Dalcin            lines.add = visit_class(value)
354*55a74a43SLisandro Dalcin            lines.add = ""
355*55a74a43SLisandro Dalcin            instances = [
356*55a74a43SLisandro Dalcin                (k, getattr(module, k)) for k in keys
357*55a74a43SLisandro Dalcin                if all((
358*55a74a43SLisandro Dalcin                    k not in done and k not in skip,
359*55a74a43SLisandro Dalcin                    type(getattr(module, k)) is value,
360*55a74a43SLisandro Dalcin                ))
361*55a74a43SLisandro Dalcin            ]
362*55a74a43SLisandro Dalcin            for attrname, attrvalue in instances:
363*55a74a43SLisandro Dalcin                done.add(attrname)
364*55a74a43SLisandro Dalcin                lines.add = visit_data((attrname, attrvalue))
365*55a74a43SLisandro Dalcin            if instances:
366*55a74a43SLisandro Dalcin                lines.add = ""
367*55a74a43SLisandro Dalcin            continue
368*55a74a43SLisandro Dalcin
369*55a74a43SLisandro Dalcin        if is_function(value):
370*55a74a43SLisandro Dalcin            done.add(name)
371*55a74a43SLisandro Dalcin            if name == value.__name__:
372*55a74a43SLisandro Dalcin                lines.add = visit_function(value)
373*55a74a43SLisandro Dalcin            else:
374*55a74a43SLisandro Dalcin                lines.add = f"{name} = {value.__name__}"
375*55a74a43SLisandro Dalcin            continue
376*55a74a43SLisandro Dalcin
377*55a74a43SLisandro Dalcin    lines.add = ""
378*55a74a43SLisandro Dalcin    for name in keys:
379*55a74a43SLisandro Dalcin        if name in done or name in skip:
380*55a74a43SLisandro Dalcin            continue
381*55a74a43SLisandro Dalcin        value = getattr(module, name)
382*55a74a43SLisandro Dalcin        done.add(name)
383*55a74a43SLisandro Dalcin        if name in OVERRIDE:
384*55a74a43SLisandro Dalcin            lines.add = OVERRIDE[name]
385*55a74a43SLisandro Dalcin        else:
386*55a74a43SLisandro Dalcin            lines.add = visit_data((name, value))
387*55a74a43SLisandro Dalcin
388*55a74a43SLisandro Dalcin    leftovers = [name for name in keys if
389*55a74a43SLisandro Dalcin                 name not in done and name not in skip]
390*55a74a43SLisandro Dalcin    if leftovers:
391*55a74a43SLisandro Dalcin        raise RuntimeError(f"leftovers: {leftovers}")
392*55a74a43SLisandro Dalcin    return lines
393*55a74a43SLisandro Dalcin
394*55a74a43SLisandro Dalcin
395*55a74a43SLisandro DalcinIMPORTS = """
396*55a74a43SLisandro Dalcinfrom __future__ import annotations
397*55a74a43SLisandro Dalcinimport sys
398*55a74a43SLisandro Dalcinfrom typing import (
399*55a74a43SLisandro Dalcin    Any,
400*55a74a43SLisandro Dalcin    Union,
401*55a74a43SLisandro Dalcin    Literal,
402*55a74a43SLisandro Dalcin    Optional,
403*55a74a43SLisandro Dalcin    NoReturn,
404*55a74a43SLisandro Dalcin    Final,
405*55a74a43SLisandro Dalcin)
406*55a74a43SLisandro Dalcinfrom typing import (
407*55a74a43SLisandro Dalcin    Callable,
408*55a74a43SLisandro Dalcin    Hashable,
409*55a74a43SLisandro Dalcin    Iterable,
410*55a74a43SLisandro Dalcin    Iterator,
411*55a74a43SLisandro Dalcin    Sequence,
412*55a74a43SLisandro Dalcin    Mapping,
413*55a74a43SLisandro Dalcin)
414*55a74a43SLisandro Dalcinif sys.version_info >= (3, 11):
415*55a74a43SLisandro Dalcin    from typing import Self
416*55a74a43SLisandro Dalcinelse:
417*55a74a43SLisandro Dalcin    from typing_extensions import Self
418*55a74a43SLisandro Dalcin
419*55a74a43SLisandro Dalcinimport numpy
420*55a74a43SLisandro Dalcinfrom numpy import dtype, ndarray
421*55a74a43SLisandro Dalcinfrom mpi4py.MPI import (
422*55a74a43SLisandro Dalcin    Intracomm,
423*55a74a43SLisandro Dalcin    Datatype,
424*55a74a43SLisandro Dalcin    Op,
425*55a74a43SLisandro Dalcin)
426*55a74a43SLisandro Dalcin
427*55a74a43SLisandro Dalcinclass _dtype:
428*55a74a43SLisandro Dalcin    def __init__(self, name):
429*55a74a43SLisandro Dalcin        self.name = name
430*55a74a43SLisandro Dalcin    def __repr__(self):
431*55a74a43SLisandro Dalcin        return self.name
432*55a74a43SLisandro Dalcin
433*55a74a43SLisandro DalcinIntType: dtype = _dtype('IntType')
434*55a74a43SLisandro DalcinRealType: dtype =  _dtype('RealType')
435*55a74a43SLisandro DalcinComplexType: dtype = _dtype('ComplexType')
436*55a74a43SLisandro DalcinScalarType: dtype = _dtype('ScalarType')
437*55a74a43SLisandro Dalcin"""
438*55a74a43SLisandro Dalcin
439*55a74a43SLisandro DalcinHELPERS = """
440*55a74a43SLisandro Dalcinclass _Int(int): pass
441*55a74a43SLisandro Dalcinclass _Str(str): pass
442*55a74a43SLisandro Dalcinclass _Float(float): pass
443*55a74a43SLisandro Dalcinclass _Dict(dict): pass
444*55a74a43SLisandro Dalcin
445*55a74a43SLisandro Dalcindef _repr(obj):
446*55a74a43SLisandro Dalcin    try:
447*55a74a43SLisandro Dalcin        return obj._name
448*55a74a43SLisandro Dalcin    except AttributeError:
449*55a74a43SLisandro Dalcin        return super(obj).__repr__()
450*55a74a43SLisandro Dalcin
451*55a74a43SLisandro Dalcindef _def(cls, name):
452*55a74a43SLisandro Dalcin    if cls is int:
453*55a74a43SLisandro Dalcin       cls = _Int
454*55a74a43SLisandro Dalcin    if cls is str:
455*55a74a43SLisandro Dalcin       cls = _Str
456*55a74a43SLisandro Dalcin    if cls is float:
457*55a74a43SLisandro Dalcin       cls = _Float
458*55a74a43SLisandro Dalcin    if cls is dict:
459*55a74a43SLisandro Dalcin       cls = _Dict
460*55a74a43SLisandro Dalcin
461*55a74a43SLisandro Dalcin    obj = cls()
462*55a74a43SLisandro Dalcin    obj._name = name
463*55a74a43SLisandro Dalcin    if '__repr__' not in cls.__dict__:
464*55a74a43SLisandro Dalcin        cls.__repr__ = _repr
465*55a74a43SLisandro Dalcin    return obj
466*55a74a43SLisandro Dalcin"""
467*55a74a43SLisandro Dalcin
468*55a74a43SLisandro DalcinOVERRIDE = {
469*55a74a43SLisandro Dalcin}
470*55a74a43SLisandro Dalcin
471*55a74a43SLisandro DalcinTYPING = """
472*55a74a43SLisandro Dalcinfrom .typing import *
473*55a74a43SLisandro Dalcin"""
474*55a74a43SLisandro Dalcin
475*55a74a43SLisandro Dalcin
476*55a74a43SLisandro Dalcindef visit_petsc4py_PETSc(done=None):
477*55a74a43SLisandro Dalcin    from petsc4py import PETSc
478*55a74a43SLisandro Dalcin    lines = Lines()
479*55a74a43SLisandro Dalcin    lines.add = f'"""{PETSc.__doc__}"""'
480*55a74a43SLisandro Dalcin    lines.add = IMPORTS
481*55a74a43SLisandro Dalcin    lines.add = ""
482*55a74a43SLisandro Dalcin    lines.add = HELPERS
483*55a74a43SLisandro Dalcin    lines.add = ""
484*55a74a43SLisandro Dalcin    lines.add = visit_module(PETSc)
485*55a74a43SLisandro Dalcin    lines.add = ""
486*55a74a43SLisandro Dalcin    lines.add = TYPING
487*55a74a43SLisandro Dalcin    return lines
488*55a74a43SLisandro Dalcin
489*55a74a43SLisandro Dalcin
490*55a74a43SLisandro Dalcindef generate(filename):
491*55a74a43SLisandro Dalcin    dirname = os.path.dirname(filename)
492*55a74a43SLisandro Dalcin    os.makedirs(dirname, exist_ok=True)
493*55a74a43SLisandro Dalcin    with open(filename, 'w') as f:
494*55a74a43SLisandro Dalcin        for line in visit_petsc4py_PETSc():
495*55a74a43SLisandro Dalcin            print(line, file=f)
496*55a74a43SLisandro Dalcin
497*55a74a43SLisandro Dalcin
498*55a74a43SLisandro Dalcindef load_module(filename, name=None):
499*55a74a43SLisandro Dalcin    if name is None:
500*55a74a43SLisandro Dalcin        name, _ = os.path.splitext(
501*55a74a43SLisandro Dalcin            os.path.basename(filename))
502*55a74a43SLisandro Dalcin    module = type(sys)(name)
503*55a74a43SLisandro Dalcin    module.__file__ = filename
504*55a74a43SLisandro Dalcin    module.__package__ = name.rsplit('.', 1)[0]
505*55a74a43SLisandro Dalcin    old = replace_module(module)
506*55a74a43SLisandro Dalcin    with open(filename) as f:
507*55a74a43SLisandro Dalcin        exec(f.read(), module.__dict__)  # noqa: S102
508*55a74a43SLisandro Dalcin    restore_module(old)
509*55a74a43SLisandro Dalcin    return module
510*55a74a43SLisandro Dalcin
511*55a74a43SLisandro Dalcin
512*55a74a43SLisandro Dalcin_sys_modules = {}
513*55a74a43SLisandro Dalcin
514*55a74a43SLisandro Dalcin
515*55a74a43SLisandro Dalcindef replace_module(module):
516*55a74a43SLisandro Dalcin    name = module.__name__
517*55a74a43SLisandro Dalcin    assert name not in _sys_modules
518*55a74a43SLisandro Dalcin    _sys_modules[name] = sys.modules[name]
519*55a74a43SLisandro Dalcin    sys.modules[name] = module
520*55a74a43SLisandro Dalcin    return _sys_modules[name]
521*55a74a43SLisandro Dalcin
522*55a74a43SLisandro Dalcin
523*55a74a43SLisandro Dalcindef restore_module(module):
524*55a74a43SLisandro Dalcin    name = module.__name__
525*55a74a43SLisandro Dalcin    assert name in _sys_modules
526*55a74a43SLisandro Dalcin    sys.modules[name] = _sys_modules[name]
527*55a74a43SLisandro Dalcin    del _sys_modules[name]
528*55a74a43SLisandro Dalcin
529*55a74a43SLisandro Dalcin
530*55a74a43SLisandro Dalcindef annotate(dest, source):
531*55a74a43SLisandro Dalcin    try:
532*55a74a43SLisandro Dalcin        dest.__annotations__ = source.__annotations__
533*55a74a43SLisandro Dalcin    except AttributeError:
534*55a74a43SLisandro Dalcin        pass
535*55a74a43SLisandro Dalcin    if isinstance(dest, type):
536*55a74a43SLisandro Dalcin        for name in dest.__dict__.keys():
537*55a74a43SLisandro Dalcin            if hasattr(source, name):
538*55a74a43SLisandro Dalcin                obj = getattr(dest, name)
539*55a74a43SLisandro Dalcin                annotate(obj, getattr(source, name))
540*55a74a43SLisandro Dalcin    if isinstance(dest, type(sys)):
541*55a74a43SLisandro Dalcin        for name in dir(dest):
542*55a74a43SLisandro Dalcin            if hasattr(source, name):
543*55a74a43SLisandro Dalcin                obj = getattr(dest, name)
544*55a74a43SLisandro Dalcin                mod = getattr(obj, '__module__', None)
545*55a74a43SLisandro Dalcin                if dest.__name__ == mod:
546*55a74a43SLisandro Dalcin                    annotate(obj, getattr(source, name))
547*55a74a43SLisandro Dalcin        for name in dir(source):
548*55a74a43SLisandro Dalcin            if not hasattr(dest, name):
549*55a74a43SLisandro Dalcin                setattr(dest, name, getattr(source, name))
550*55a74a43SLisandro Dalcin
551*55a74a43SLisandro Dalcin
552*55a74a43SLisandro DalcinOUTDIR = 'reference'
553*55a74a43SLisandro Dalcin
554*55a74a43SLisandro Dalcinif __name__ == '__main__':
555*55a74a43SLisandro Dalcin    generate(os.path.join(OUTDIR, 'petsc4py.PETSc.py'))
556