155a74a43SLisandro Dalcinimport os 255a74a43SLisandro Dalcinimport sys 355a74a43SLisandro Dalcinimport inspect 455a74a43SLisandro Dalcinimport textwrap 5e4efd8cbSStefano Zampinifrom sphinx.util import logging 6e4efd8cbSStefano Zampinilogger = logging.getLogger(__name__) 755a74a43SLisandro Dalcin 855a74a43SLisandro Dalcindef is_cyfunction(obj): 955a74a43SLisandro Dalcin return type(obj).__name__ == 'cython_function_or_method' 1055a74a43SLisandro Dalcin 1155a74a43SLisandro Dalcin 1255a74a43SLisandro Dalcindef is_function(obj): 1355a74a43SLisandro Dalcin return ( 1455a74a43SLisandro Dalcin inspect.isbuiltin(obj) 1555a74a43SLisandro Dalcin or is_cyfunction(obj) 1655a74a43SLisandro Dalcin or type(obj) is type(ord) 1755a74a43SLisandro Dalcin ) 1855a74a43SLisandro Dalcin 1955a74a43SLisandro Dalcin 2055a74a43SLisandro Dalcindef is_method(obj): 2155a74a43SLisandro Dalcin return ( 2255a74a43SLisandro Dalcin inspect.ismethoddescriptor(obj) 2355a74a43SLisandro Dalcin or inspect.ismethod(obj) 2455a74a43SLisandro Dalcin or is_cyfunction(obj) 2555a74a43SLisandro Dalcin or type(obj) in ( 2655a74a43SLisandro Dalcin type(str.index), 2755a74a43SLisandro Dalcin type(str.__add__), 2855a74a43SLisandro Dalcin type(str.__new__), 2955a74a43SLisandro Dalcin ) 3055a74a43SLisandro Dalcin ) 3155a74a43SLisandro Dalcin 3255a74a43SLisandro Dalcin 3355a74a43SLisandro Dalcindef is_classmethod(obj): 3455a74a43SLisandro Dalcin return ( 3555a74a43SLisandro Dalcin inspect.isbuiltin(obj) 3655a74a43SLisandro Dalcin or type(obj).__name__ in ( 3755a74a43SLisandro Dalcin 'classmethod', 3855a74a43SLisandro Dalcin 'classmethod_descriptor', 3955a74a43SLisandro Dalcin ) 4055a74a43SLisandro Dalcin ) 4155a74a43SLisandro Dalcin 4255a74a43SLisandro Dalcin 4355a74a43SLisandro Dalcindef is_staticmethod(obj): 4455a74a43SLisandro Dalcin return ( 4555a74a43SLisandro Dalcin type(obj).__name__ in ( 4655a74a43SLisandro Dalcin 'staticmethod', 4755a74a43SLisandro Dalcin ) 4855a74a43SLisandro Dalcin ) 4955a74a43SLisandro Dalcin 5055a74a43SLisandro Dalcindef is_constant(obj): 5155a74a43SLisandro Dalcin return isinstance(obj, (int, float, str, dict)) 5255a74a43SLisandro Dalcin 5355a74a43SLisandro Dalcindef is_datadescr(obj): 5455a74a43SLisandro Dalcin return inspect.isdatadescriptor(obj) and not hasattr(obj, 'fget') 5555a74a43SLisandro Dalcin 5655a74a43SLisandro Dalcin 5755a74a43SLisandro Dalcindef is_property(obj): 5855a74a43SLisandro Dalcin return inspect.isdatadescriptor(obj) and hasattr(obj, 'fget') 5955a74a43SLisandro Dalcin 6055a74a43SLisandro Dalcin 6155a74a43SLisandro Dalcindef is_class(obj): 6255a74a43SLisandro Dalcin return inspect.isclass(obj) or type(obj) is type(int) 6355a74a43SLisandro Dalcin 6455a74a43SLisandro Dalcin 6555a74a43SLisandro Dalcinclass Lines(list): 6655a74a43SLisandro Dalcin 6755a74a43SLisandro Dalcin INDENT = " " * 4 6855a74a43SLisandro Dalcin level = 0 6955a74a43SLisandro Dalcin 7055a74a43SLisandro Dalcin @property 7155a74a43SLisandro Dalcin def add(self): 7255a74a43SLisandro Dalcin return self 7355a74a43SLisandro Dalcin 7455a74a43SLisandro Dalcin @add.setter 7555a74a43SLisandro Dalcin def add(self, lines): 7655a74a43SLisandro Dalcin if lines is None: 7755a74a43SLisandro Dalcin return 7855a74a43SLisandro Dalcin if isinstance(lines, str): 7955a74a43SLisandro Dalcin lines = textwrap.dedent(lines).strip().split('\n') 8055a74a43SLisandro Dalcin indent = self.INDENT * self.level 8155a74a43SLisandro Dalcin for line in lines: 8255a74a43SLisandro Dalcin self.append(indent + line) 8355a74a43SLisandro Dalcin 8455a74a43SLisandro Dalcin 8555a74a43SLisandro Dalcindef signature(obj): 8655a74a43SLisandro Dalcin doc = obj.__doc__ 8755a74a43SLisandro Dalcin doc = doc or f"{obj.__name__}: Any" # FIXME remove line 8855a74a43SLisandro Dalcin sig = doc.partition('\n')[0].split('.', 1)[-1] 8955a74a43SLisandro Dalcin return sig or None 9055a74a43SLisandro Dalcin 9155a74a43SLisandro Dalcin 9255a74a43SLisandro Dalcindef docstring(obj): 9355a74a43SLisandro Dalcin doc = obj.__doc__ 9455a74a43SLisandro Dalcin doc = doc or '' # FIXME 95b3f8c7a1SStefano Zampini link = None 96e4efd8cbSStefano Zampini sig = None 97e4efd8cbSStefano Zampini cl = is_class(obj) 98e4efd8cbSStefano Zampini if cl: 9955a74a43SLisandro Dalcin doc = doc.strip() 10055a74a43SLisandro Dalcin else: 101e4efd8cbSStefano Zampini sig, _, doc = doc.partition('\n') 102b3f8c7a1SStefano Zampini doc, _, link = doc.rpartition('\n') 103b3f8c7a1SStefano Zampini 10455a74a43SLisandro Dalcin summary, _, docbody = doc.partition('\n') 10555a74a43SLisandro Dalcin summary = summary.strip() 10655a74a43SLisandro Dalcin docbody = textwrap.dedent(docbody).strip() 107e4efd8cbSStefano Zampini if docbody and sig: 108e4efd8cbSStefano Zampini if not summary.endswith('.'): 109e4efd8cbSStefano Zampini logger.warning(f'Summary for {sig} does not end with period.') 110e4efd8cbSStefano Zampini if len(summary) > 79: 111e4efd8cbSStefano Zampini logger.warning(f'Summary for {sig} too long.') 112e4efd8cbSStefano Zampini # FIXME 113*0898713fSStefano Zampini lines = docbody.split('\n') 114*0898713fSStefano Zampini for i,l in enumerate(lines): 115*0898713fSStefano Zampini if len(l) > 79: 116*0898713fSStefano Zampini logger.warning(f'Line {i} for {sig} too long.') 117e4efd8cbSStefano Zampini #init = ("Collective.", "Not collective.", "Logically collective.", "Neighborwise collective.") 118e4efd8cbSStefano Zampini #if lines[0] not in init: 119e4efd8cbSStefano Zampini # logger.warning(f'Unexpected collectiveness specification for {sig}\nFound {lines[0]}') 120e4efd8cbSStefano Zampini 121b3f8c7a1SStefano Zampini if link: 122e4efd8cbSStefano Zampini linktxt, _, link = link.rpartition(' ') 123b3f8c7a1SStefano Zampini linkloc = link.replace(':','#L') 124e4efd8cbSStefano Zampini # FIXME do we want to use a special section? 125b3f8c7a1SStefano Zampini # section = f'References\n----------`' 126e4efd8cbSStefano Zampini section = '\n' 127e4efd8cbSStefano Zampini linkbody = f':sources:`{linktxt} {link} <{linkloc}>`' 128e4efd8cbSStefano Zampini linkbody = f'{section}\n{linkbody}' 129b3f8c7a1SStefano Zampini if docbody: 130b3f8c7a1SStefano Zampini docbody = f'{docbody}\n\n{linkbody}' 131b3f8c7a1SStefano Zampini else: 132b3f8c7a1SStefano Zampini docbody = linkbody 133b3f8c7a1SStefano Zampini 13455a74a43SLisandro Dalcin if docbody: 13555a74a43SLisandro Dalcin doc = f'"""{summary}\n\n{docbody}\n\n"""' 13655a74a43SLisandro Dalcin else: 13755a74a43SLisandro Dalcin doc = f'"""{summary}"""' 13855a74a43SLisandro Dalcin doc = textwrap.indent(doc, Lines.INDENT) 13955a74a43SLisandro Dalcin return doc 14055a74a43SLisandro Dalcin 14155a74a43SLisandro Dalcin 14255a74a43SLisandro Dalcindef visit_data(constant): 14355a74a43SLisandro Dalcin name, value = constant 14455a74a43SLisandro Dalcin typename = type(value).__name__ 14555a74a43SLisandro Dalcin kind = "Constant" if isinstance(value, int) else "Object" 14655a74a43SLisandro Dalcin init = f"_def({typename}, '{name}')" 14755a74a43SLisandro Dalcin doc = f"#: {kind} ``{name}`` of type :class:`{typename}`" 14855a74a43SLisandro Dalcin return f"{name}: {typename} = {init} {doc}\n" 14955a74a43SLisandro Dalcin 15055a74a43SLisandro Dalcin 15155a74a43SLisandro Dalcindef visit_function(function): 15255a74a43SLisandro Dalcin sig = signature(function) 15355a74a43SLisandro Dalcin doc = docstring(function) 15455a74a43SLisandro Dalcin body = Lines.INDENT + "..." 15555a74a43SLisandro Dalcin return f"def {sig}:\n{doc}\n{body}\n" 15655a74a43SLisandro Dalcin 15755a74a43SLisandro Dalcin 15855a74a43SLisandro Dalcindef visit_method(method): 15955a74a43SLisandro Dalcin sig = signature(method) 16055a74a43SLisandro Dalcin doc = docstring(method) 16155a74a43SLisandro Dalcin body = Lines.INDENT + "..." 16255a74a43SLisandro Dalcin return f"def {sig}:\n{doc}\n{body}\n" 16355a74a43SLisandro Dalcin 16455a74a43SLisandro Dalcin 16555a74a43SLisandro Dalcindef visit_datadescr(datadescr, name=None): 16655a74a43SLisandro Dalcin sig = signature(datadescr) 16755a74a43SLisandro Dalcin doc = docstring(datadescr) 16855a74a43SLisandro Dalcin name = sig.partition(':')[0].strip() or datadescr.__name__ 16955a74a43SLisandro Dalcin type = sig.partition(':')[2].strip() or 'Any' 17055a74a43SLisandro Dalcin sig = f"{name}(self) -> {type}" 17155a74a43SLisandro Dalcin body = Lines.INDENT + "..." 17255a74a43SLisandro Dalcin return f"@property\ndef {sig}:\n{doc}\n{body}\n" 17355a74a43SLisandro Dalcin 17455a74a43SLisandro Dalcin 17555a74a43SLisandro Dalcindef visit_property(prop, name=None): 17655a74a43SLisandro Dalcin sig = signature(prop.fget) 17755a74a43SLisandro Dalcin name = name or prop.fget.__name__ 17855a74a43SLisandro Dalcin type = sig.rsplit('->', 1)[-1].strip() 17955a74a43SLisandro Dalcin sig = f"{name}(self) -> {type}" 18055a74a43SLisandro Dalcin doc = f'"""{prop.__doc__}"""' 18155a74a43SLisandro Dalcin doc = textwrap.indent(doc, Lines.INDENT) 18255a74a43SLisandro Dalcin body = Lines.INDENT + "..." 18355a74a43SLisandro Dalcin return f"@property\ndef {sig}:\n{doc}\n{body}\n" 18455a74a43SLisandro Dalcin 18555a74a43SLisandro Dalcin 18655a74a43SLisandro Dalcindef visit_constructor(cls, name='__init__', args=None): 18755a74a43SLisandro Dalcin init = (name == '__init__') 18855a74a43SLisandro Dalcin argname = cls.__mro__[-2].__name__.lower() 18955a74a43SLisandro Dalcin argtype = cls.__name__ 19055a74a43SLisandro Dalcin initarg = args or f"{argname}: Optional[{argtype}] = None" 19155a74a43SLisandro Dalcin selfarg = 'self' if init else 'cls' 19255a74a43SLisandro Dalcin rettype = 'None' if init else argtype 19355a74a43SLisandro Dalcin arglist = f"{selfarg}, {initarg}" 19455a74a43SLisandro Dalcin sig = f"{name}({arglist}) -> {rettype}" 19555a74a43SLisandro Dalcin ret = '...' if init else 'return super().__new__(cls)' 19655a74a43SLisandro Dalcin body = Lines.INDENT + ret 19755a74a43SLisandro Dalcin return f"def {sig}:\n{body}" 19855a74a43SLisandro Dalcin 19955a74a43SLisandro Dalcin 20055a74a43SLisandro Dalcindef visit_class(cls, outer=None, done=None): 20155a74a43SLisandro Dalcin skip = { 20255a74a43SLisandro Dalcin '__doc__', 20355a74a43SLisandro Dalcin '__dict__', 20455a74a43SLisandro Dalcin '__module__', 20555a74a43SLisandro Dalcin '__weakref__', 20655a74a43SLisandro Dalcin '__pyx_vtable__', 20755a74a43SLisandro Dalcin '__lt__', 20855a74a43SLisandro Dalcin '__le__', 20955a74a43SLisandro Dalcin '__ge__', 21055a74a43SLisandro Dalcin '__gt__', 21155a74a43SLisandro Dalcin '__enum2str', # FIXME refactor implemetation 21255a74a43SLisandro Dalcin '_traceback_', # FIXME maybe refactor? 21355a74a43SLisandro Dalcin } 21455a74a43SLisandro Dalcin special = { 21555a74a43SLisandro Dalcin '__len__': "__len__(self) -> int", 21655a74a43SLisandro Dalcin '__bool__': "__bool__(self) -> bool", 21755a74a43SLisandro Dalcin '__hash__': "__hash__(self) -> int", 21855a74a43SLisandro Dalcin '__int__': "__int__(self) -> int", 21955a74a43SLisandro Dalcin '__index__': "__int__(self) -> int", 22055a74a43SLisandro Dalcin '__str__': "__str__(self) -> str", 22155a74a43SLisandro Dalcin '__repr__': "__repr__(self) -> str", 22255a74a43SLisandro Dalcin '__eq__': "__eq__(self, other: object) -> bool", 22355a74a43SLisandro Dalcin '__ne__': "__ne__(self, other: object) -> bool", 22455a74a43SLisandro Dalcin } 22555a74a43SLisandro Dalcin constructor = ( 22655a74a43SLisandro Dalcin '__new__', 22755a74a43SLisandro Dalcin '__init__', 22855a74a43SLisandro Dalcin ) 22955a74a43SLisandro Dalcin 23055a74a43SLisandro Dalcin qualname = cls.__name__ 23155a74a43SLisandro Dalcin cls_name = cls.__name__ 23255a74a43SLisandro Dalcin if outer is not None and cls_name.startswith(outer): 23355a74a43SLisandro Dalcin cls_name = cls_name[len(outer):] 23455a74a43SLisandro Dalcin qualname = f"{outer}.{cls_name}" 23555a74a43SLisandro Dalcin 23655a74a43SLisandro Dalcin override = OVERRIDE.get(qualname, {}) 23755a74a43SLisandro Dalcin done = set() if done is None else done 23855a74a43SLisandro Dalcin lines = Lines() 23955a74a43SLisandro Dalcin 24055a74a43SLisandro Dalcin base = cls.__base__ 24155a74a43SLisandro Dalcin if base is object: 24255a74a43SLisandro Dalcin lines.add = f"class {cls_name}:" 24355a74a43SLisandro Dalcin else: 24455a74a43SLisandro Dalcin lines.add = f"class {cls_name}({base.__name__}):" 24555a74a43SLisandro Dalcin lines.level += 1 24655a74a43SLisandro Dalcin 24755a74a43SLisandro Dalcin lines.add = docstring(cls) 24855a74a43SLisandro Dalcin 24955a74a43SLisandro Dalcin for name in ('__new__', '__init__', '__hash__'): 25055a74a43SLisandro Dalcin if name in cls.__dict__: 25155a74a43SLisandro Dalcin done.add(name) 25255a74a43SLisandro Dalcin 25355a74a43SLisandro Dalcin dct = cls.__dict__ 25455a74a43SLisandro Dalcin keys = list(dct.keys()) 25555a74a43SLisandro Dalcin 25655a74a43SLisandro Dalcin def dunder(name): 25755a74a43SLisandro Dalcin return name.startswith('__') and name.endswith('__') 25855a74a43SLisandro Dalcin 25955a74a43SLisandro Dalcin def members(seq): 26055a74a43SLisandro Dalcin for name in seq: 26155a74a43SLisandro Dalcin if name in skip: 26255a74a43SLisandro Dalcin continue 26355a74a43SLisandro Dalcin if name in done: 26455a74a43SLisandro Dalcin continue 26555a74a43SLisandro Dalcin if dunder(name): 26655a74a43SLisandro Dalcin if name not in special and name not in override: 26755a74a43SLisandro Dalcin done.add(name) 26855a74a43SLisandro Dalcin continue 26955a74a43SLisandro Dalcin yield name 27055a74a43SLisandro Dalcin 27155a74a43SLisandro Dalcin for name in members(keys): 27255a74a43SLisandro Dalcin attr = getattr(cls, name) 27355a74a43SLisandro Dalcin if is_class(attr): 27455a74a43SLisandro Dalcin done.add(name) 27555a74a43SLisandro Dalcin lines.add = visit_class(attr, outer=cls_name) 27655a74a43SLisandro Dalcin continue 27755a74a43SLisandro Dalcin 27855a74a43SLisandro Dalcin for name in members(keys): 27955a74a43SLisandro Dalcin 28055a74a43SLisandro Dalcin if name in override: 28155a74a43SLisandro Dalcin done.add(name) 28255a74a43SLisandro Dalcin lines.add = override[name] 28355a74a43SLisandro Dalcin continue 28455a74a43SLisandro Dalcin 28555a74a43SLisandro Dalcin if name in special: 28655a74a43SLisandro Dalcin done.add(name) 28755a74a43SLisandro Dalcin sig = special[name] 28855a74a43SLisandro Dalcin lines.add = f"def {sig}: ..." 28955a74a43SLisandro Dalcin continue 29055a74a43SLisandro Dalcin 29155a74a43SLisandro Dalcin attr = getattr(cls, name) 29255a74a43SLisandro Dalcin 29355a74a43SLisandro Dalcin if is_method(attr): 29455a74a43SLisandro Dalcin done.add(name) 29555a74a43SLisandro Dalcin if name == attr.__name__: 29655a74a43SLisandro Dalcin obj = dct[name] 29755a74a43SLisandro Dalcin if is_classmethod(obj): 29855a74a43SLisandro Dalcin lines.add = "@classmethod" 29955a74a43SLisandro Dalcin elif is_staticmethod(obj): 30055a74a43SLisandro Dalcin lines.add = "@staticmethod" 30155a74a43SLisandro Dalcin lines.add = visit_method(attr) 30255a74a43SLisandro Dalcin elif False: 30355a74a43SLisandro Dalcin lines.add = f"{name} = {attr.__name__}" 30455a74a43SLisandro Dalcin continue 30555a74a43SLisandro Dalcin 30655a74a43SLisandro Dalcin if is_datadescr(attr): 30755a74a43SLisandro Dalcin done.add(name) 30855a74a43SLisandro Dalcin lines.add = visit_datadescr(attr) 30955a74a43SLisandro Dalcin continue 31055a74a43SLisandro Dalcin 31155a74a43SLisandro Dalcin if is_property(attr): 31255a74a43SLisandro Dalcin done.add(name) 31355a74a43SLisandro Dalcin lines.add = visit_property(attr, name) 31455a74a43SLisandro Dalcin continue 31555a74a43SLisandro Dalcin 31655a74a43SLisandro Dalcin if is_constant(attr): 31755a74a43SLisandro Dalcin done.add(name) 31855a74a43SLisandro Dalcin lines.add = visit_data((name, attr)) 31955a74a43SLisandro Dalcin continue 32055a74a43SLisandro Dalcin 32155a74a43SLisandro Dalcin leftovers = [name for name in keys if 32255a74a43SLisandro Dalcin name not in done and name not in skip] 32355a74a43SLisandro Dalcin if leftovers: 32455a74a43SLisandro Dalcin raise RuntimeError(f"leftovers: {leftovers}") 32555a74a43SLisandro Dalcin 32655a74a43SLisandro Dalcin lines.level -= 1 32755a74a43SLisandro Dalcin return lines 32855a74a43SLisandro Dalcin 32955a74a43SLisandro Dalcin 33055a74a43SLisandro Dalcindef visit_module(module, done=None): 33155a74a43SLisandro Dalcin skip = { 33255a74a43SLisandro Dalcin '__doc__', 33355a74a43SLisandro Dalcin '__name__', 33455a74a43SLisandro Dalcin '__loader__', 33555a74a43SLisandro Dalcin '__spec__', 33655a74a43SLisandro Dalcin '__file__', 33755a74a43SLisandro Dalcin '__package__', 33855a74a43SLisandro Dalcin '__builtins__', 33955a74a43SLisandro Dalcin '__pyx_capi__', 34055a74a43SLisandro Dalcin '__pyx_unpickle_Enum', # FIXME review 34155a74a43SLisandro Dalcin } 34255a74a43SLisandro Dalcin 34355a74a43SLisandro Dalcin done = set() if done is None else done 34455a74a43SLisandro Dalcin lines = Lines() 34555a74a43SLisandro Dalcin 34655a74a43SLisandro Dalcin keys = list(module.__dict__.keys()) 34755a74a43SLisandro Dalcin keys.sort(key=lambda name: name.startswith("_")) 34855a74a43SLisandro Dalcin 34955a74a43SLisandro Dalcin constants = [ 35055a74a43SLisandro Dalcin (name, getattr(module, name)) for name in keys 35155a74a43SLisandro Dalcin if all(( 35255a74a43SLisandro Dalcin name not in done and name not in skip, 35355a74a43SLisandro Dalcin is_constant(getattr(module, name)), 35455a74a43SLisandro Dalcin )) 35555a74a43SLisandro Dalcin ] 35655a74a43SLisandro Dalcin for _, value in constants: 35755a74a43SLisandro Dalcin cls = type(value) 35855a74a43SLisandro Dalcin name = cls.__name__ 35955a74a43SLisandro Dalcin if name in done or name in skip: 36055a74a43SLisandro Dalcin continue 36155a74a43SLisandro Dalcin if cls.__module__ == module.__name__: 36255a74a43SLisandro Dalcin done.add(name) 36355a74a43SLisandro Dalcin lines.add = visit_class(cls) 36455a74a43SLisandro Dalcin lines.add = "" 36555a74a43SLisandro Dalcin for attr in constants: 36655a74a43SLisandro Dalcin name, value = attr 36755a74a43SLisandro Dalcin done.add(name) 36855a74a43SLisandro Dalcin if name in OVERRIDE: 36955a74a43SLisandro Dalcin lines.add = OVERRIDE[name] 37055a74a43SLisandro Dalcin else: 37155a74a43SLisandro Dalcin lines.add = visit_data((name, value)) 37255a74a43SLisandro Dalcin if constants: 37355a74a43SLisandro Dalcin lines.add = "" 37455a74a43SLisandro Dalcin 37555a74a43SLisandro Dalcin for name in keys: 37655a74a43SLisandro Dalcin if name in done or name in skip: 37755a74a43SLisandro Dalcin continue 37855a74a43SLisandro Dalcin value = getattr(module, name) 37955a74a43SLisandro Dalcin 38055a74a43SLisandro Dalcin if is_class(value): 38155a74a43SLisandro Dalcin done.add(name) 38255a74a43SLisandro Dalcin if value.__name__ != name: 38355a74a43SLisandro Dalcin continue 38455a74a43SLisandro Dalcin if value.__module__ != module.__name__: 38555a74a43SLisandro Dalcin continue 38655a74a43SLisandro Dalcin lines.add = visit_class(value) 38755a74a43SLisandro Dalcin lines.add = "" 38855a74a43SLisandro Dalcin instances = [ 38955a74a43SLisandro Dalcin (k, getattr(module, k)) for k in keys 39055a74a43SLisandro Dalcin if all(( 39155a74a43SLisandro Dalcin k not in done and k not in skip, 39255a74a43SLisandro Dalcin type(getattr(module, k)) is value, 39355a74a43SLisandro Dalcin )) 39455a74a43SLisandro Dalcin ] 39555a74a43SLisandro Dalcin for attrname, attrvalue in instances: 39655a74a43SLisandro Dalcin done.add(attrname) 39755a74a43SLisandro Dalcin lines.add = visit_data((attrname, attrvalue)) 39855a74a43SLisandro Dalcin if instances: 39955a74a43SLisandro Dalcin lines.add = "" 40055a74a43SLisandro Dalcin continue 40155a74a43SLisandro Dalcin 40255a74a43SLisandro Dalcin if is_function(value): 40355a74a43SLisandro Dalcin done.add(name) 40455a74a43SLisandro Dalcin if name == value.__name__: 40555a74a43SLisandro Dalcin lines.add = visit_function(value) 40655a74a43SLisandro Dalcin else: 40755a74a43SLisandro Dalcin lines.add = f"{name} = {value.__name__}" 40855a74a43SLisandro Dalcin continue 40955a74a43SLisandro Dalcin 41055a74a43SLisandro Dalcin lines.add = "" 41155a74a43SLisandro Dalcin for name in keys: 41255a74a43SLisandro Dalcin if name in done or name in skip: 41355a74a43SLisandro Dalcin continue 41455a74a43SLisandro Dalcin value = getattr(module, name) 41555a74a43SLisandro Dalcin done.add(name) 41655a74a43SLisandro Dalcin if name in OVERRIDE: 41755a74a43SLisandro Dalcin lines.add = OVERRIDE[name] 41855a74a43SLisandro Dalcin else: 41955a74a43SLisandro Dalcin lines.add = visit_data((name, value)) 42055a74a43SLisandro Dalcin 42155a74a43SLisandro Dalcin leftovers = [name for name in keys if 42255a74a43SLisandro Dalcin name not in done and name not in skip] 42355a74a43SLisandro Dalcin if leftovers: 42455a74a43SLisandro Dalcin raise RuntimeError(f"leftovers: {leftovers}") 42555a74a43SLisandro Dalcin return lines 42655a74a43SLisandro Dalcin 42755a74a43SLisandro Dalcin 42855a74a43SLisandro DalcinIMPORTS = """ 42955a74a43SLisandro Dalcinfrom __future__ import annotations 43055a74a43SLisandro Dalcinimport sys 43155a74a43SLisandro Dalcinfrom typing import ( 43255a74a43SLisandro Dalcin Any, 43355a74a43SLisandro Dalcin Union, 43455a74a43SLisandro Dalcin Literal, 43555a74a43SLisandro Dalcin Optional, 43655a74a43SLisandro Dalcin NoReturn, 43755a74a43SLisandro Dalcin Final, 43855a74a43SLisandro Dalcin) 43955a74a43SLisandro Dalcinfrom typing import ( 44055a74a43SLisandro Dalcin Callable, 44155a74a43SLisandro Dalcin Hashable, 44255a74a43SLisandro Dalcin Iterable, 44355a74a43SLisandro Dalcin Iterator, 44455a74a43SLisandro Dalcin Sequence, 44555a74a43SLisandro Dalcin Mapping, 44655a74a43SLisandro Dalcin) 44755a74a43SLisandro Dalcinif sys.version_info >= (3, 11): 44855a74a43SLisandro Dalcin from typing import Self 44955a74a43SLisandro Dalcinelse: 45055a74a43SLisandro Dalcin from typing_extensions import Self 45155a74a43SLisandro Dalcin 45255a74a43SLisandro Dalcinimport numpy 45355a74a43SLisandro Dalcinfrom numpy import dtype, ndarray 45455a74a43SLisandro Dalcinfrom mpi4py.MPI import ( 45555a74a43SLisandro Dalcin Intracomm, 45655a74a43SLisandro Dalcin Datatype, 45755a74a43SLisandro Dalcin Op, 45855a74a43SLisandro Dalcin) 45955a74a43SLisandro Dalcin 46055a74a43SLisandro Dalcinclass _dtype: 46155a74a43SLisandro Dalcin def __init__(self, name): 46255a74a43SLisandro Dalcin self.name = name 46355a74a43SLisandro Dalcin def __repr__(self): 46455a74a43SLisandro Dalcin return self.name 46555a74a43SLisandro Dalcin 46655a74a43SLisandro DalcinIntType: dtype = _dtype('IntType') 46755a74a43SLisandro DalcinRealType: dtype = _dtype('RealType') 46855a74a43SLisandro DalcinComplexType: dtype = _dtype('ComplexType') 46955a74a43SLisandro DalcinScalarType: dtype = _dtype('ScalarType') 47055a74a43SLisandro Dalcin""" 47155a74a43SLisandro Dalcin 47255a74a43SLisandro DalcinHELPERS = """ 47355a74a43SLisandro Dalcinclass _Int(int): pass 47455a74a43SLisandro Dalcinclass _Str(str): pass 47555a74a43SLisandro Dalcinclass _Float(float): pass 47655a74a43SLisandro Dalcinclass _Dict(dict): pass 47755a74a43SLisandro Dalcin 47855a74a43SLisandro Dalcindef _repr(obj): 47955a74a43SLisandro Dalcin try: 48055a74a43SLisandro Dalcin return obj._name 48155a74a43SLisandro Dalcin except AttributeError: 48255a74a43SLisandro Dalcin return super(obj).__repr__() 48355a74a43SLisandro Dalcin 48455a74a43SLisandro Dalcindef _def(cls, name): 48555a74a43SLisandro Dalcin if cls is int: 48655a74a43SLisandro Dalcin cls = _Int 48755a74a43SLisandro Dalcin if cls is str: 48855a74a43SLisandro Dalcin cls = _Str 48955a74a43SLisandro Dalcin if cls is float: 49055a74a43SLisandro Dalcin cls = _Float 49155a74a43SLisandro Dalcin if cls is dict: 49255a74a43SLisandro Dalcin cls = _Dict 49355a74a43SLisandro Dalcin 49455a74a43SLisandro Dalcin obj = cls() 49555a74a43SLisandro Dalcin obj._name = name 49655a74a43SLisandro Dalcin if '__repr__' not in cls.__dict__: 49755a74a43SLisandro Dalcin cls.__repr__ = _repr 49855a74a43SLisandro Dalcin return obj 49955a74a43SLisandro Dalcin""" 50055a74a43SLisandro Dalcin 50155a74a43SLisandro DalcinOVERRIDE = { 50255a74a43SLisandro Dalcin} 50355a74a43SLisandro Dalcin 50455a74a43SLisandro DalcinTYPING = """ 50555a74a43SLisandro Dalcinfrom .typing import * 50655a74a43SLisandro Dalcin""" 50755a74a43SLisandro Dalcin 50855a74a43SLisandro Dalcin 50955a74a43SLisandro Dalcindef visit_petsc4py_PETSc(done=None): 51055a74a43SLisandro Dalcin from petsc4py import PETSc 51155a74a43SLisandro Dalcin lines = Lines() 51255a74a43SLisandro Dalcin lines.add = f'"""{PETSc.__doc__}"""' 51355a74a43SLisandro Dalcin lines.add = IMPORTS 51455a74a43SLisandro Dalcin lines.add = "" 51555a74a43SLisandro Dalcin lines.add = HELPERS 51655a74a43SLisandro Dalcin lines.add = "" 51755a74a43SLisandro Dalcin lines.add = visit_module(PETSc) 51855a74a43SLisandro Dalcin lines.add = "" 51955a74a43SLisandro Dalcin lines.add = TYPING 52055a74a43SLisandro Dalcin return lines 52155a74a43SLisandro Dalcin 52255a74a43SLisandro Dalcin 52355a74a43SLisandro Dalcindef generate(filename): 52455a74a43SLisandro Dalcin dirname = os.path.dirname(filename) 52555a74a43SLisandro Dalcin os.makedirs(dirname, exist_ok=True) 52655a74a43SLisandro Dalcin with open(filename, 'w') as f: 52755a74a43SLisandro Dalcin for line in visit_petsc4py_PETSc(): 52855a74a43SLisandro Dalcin print(line, file=f) 52955a74a43SLisandro Dalcin 53055a74a43SLisandro Dalcin 53155a74a43SLisandro Dalcindef load_module(filename, name=None): 53255a74a43SLisandro Dalcin if name is None: 53355a74a43SLisandro Dalcin name, _ = os.path.splitext( 53455a74a43SLisandro Dalcin os.path.basename(filename)) 53555a74a43SLisandro Dalcin module = type(sys)(name) 53655a74a43SLisandro Dalcin module.__file__ = filename 53755a74a43SLisandro Dalcin module.__package__ = name.rsplit('.', 1)[0] 53855a74a43SLisandro Dalcin old = replace_module(module) 53955a74a43SLisandro Dalcin with open(filename) as f: 54055a74a43SLisandro Dalcin exec(f.read(), module.__dict__) # noqa: S102 54155a74a43SLisandro Dalcin restore_module(old) 54255a74a43SLisandro Dalcin return module 54355a74a43SLisandro Dalcin 54455a74a43SLisandro Dalcin 54555a74a43SLisandro Dalcin_sys_modules = {} 54655a74a43SLisandro Dalcin 54755a74a43SLisandro Dalcin 54855a74a43SLisandro Dalcindef replace_module(module): 54955a74a43SLisandro Dalcin name = module.__name__ 55055a74a43SLisandro Dalcin assert name not in _sys_modules 55155a74a43SLisandro Dalcin _sys_modules[name] = sys.modules[name] 55255a74a43SLisandro Dalcin sys.modules[name] = module 55355a74a43SLisandro Dalcin return _sys_modules[name] 55455a74a43SLisandro Dalcin 55555a74a43SLisandro Dalcin 55655a74a43SLisandro Dalcindef restore_module(module): 55755a74a43SLisandro Dalcin name = module.__name__ 55855a74a43SLisandro Dalcin assert name in _sys_modules 55955a74a43SLisandro Dalcin sys.modules[name] = _sys_modules[name] 56055a74a43SLisandro Dalcin del _sys_modules[name] 56155a74a43SLisandro Dalcin 56255a74a43SLisandro Dalcin 56355a74a43SLisandro Dalcindef annotate(dest, source): 56455a74a43SLisandro Dalcin try: 56555a74a43SLisandro Dalcin dest.__annotations__ = source.__annotations__ 56655a74a43SLisandro Dalcin except AttributeError: 56755a74a43SLisandro Dalcin pass 56855a74a43SLisandro Dalcin if isinstance(dest, type): 56955a74a43SLisandro Dalcin for name in dest.__dict__.keys(): 57055a74a43SLisandro Dalcin if hasattr(source, name): 57155a74a43SLisandro Dalcin obj = getattr(dest, name) 57255a74a43SLisandro Dalcin annotate(obj, getattr(source, name)) 57355a74a43SLisandro Dalcin if isinstance(dest, type(sys)): 57455a74a43SLisandro Dalcin for name in dir(dest): 57555a74a43SLisandro Dalcin if hasattr(source, name): 57655a74a43SLisandro Dalcin obj = getattr(dest, name) 57755a74a43SLisandro Dalcin mod = getattr(obj, '__module__', None) 57855a74a43SLisandro Dalcin if dest.__name__ == mod: 57955a74a43SLisandro Dalcin annotate(obj, getattr(source, name)) 58055a74a43SLisandro Dalcin for name in dir(source): 58155a74a43SLisandro Dalcin if not hasattr(dest, name): 58255a74a43SLisandro Dalcin setattr(dest, name, getattr(source, name)) 58355a74a43SLisandro Dalcin 58455a74a43SLisandro Dalcin 58555a74a43SLisandro DalcinOUTDIR = 'reference' 58655a74a43SLisandro Dalcin 58755a74a43SLisandro Dalcinif __name__ == '__main__': 58855a74a43SLisandro Dalcin generate(os.path.join(OUTDIR, 'petsc4py.PETSc.py')) 589