Skip to content

Instantly share code, notes, and snippets.

@tk0miya
Last active August 29, 2020 05:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tk0miya/25aebe948df57a2bb12d6c300329e797 to your computer and use it in GitHub Desktop.
Save tk0miya/25aebe948df57a2bb12d6c300329e797 to your computer and use it in GitHub Desktop.
class Foo:
"""
A class having methods, class variables and instance variables.
"""
CONSTANT = 1
attr1: int
attr2: str
def __init__(self):
self.attr3 = None
self.attr4 = None
def meth(self):
"""Example method."""
pass
"""
generate_document.py
Example program for "How Sphinx generates document from Python code"
Copyright: @tk0miya
License: BSDL
#pyconjp2020
"""
import ast
import inspect
import importlib
import textwrap
def get_ivars_from_constructor(cls):
"""Returns the instance variables of given class."""
# Convert source code to AST
filename = inspect.getsourcefile(cls)
with open(filename) as f:
tree = ast.parse(f.read())
members = []
for _class in tree.body:
# Search specified class from module
if isinstance(_class, ast.ClassDef) and _class.name == cls.__name__:
# Search constructor from class definition
for func in _class.body:
if isinstance(func, ast.FunctionDef) and func.name == "__init__":
# Keep the name of first argument (a.k.a. self)
self = func.args.args[0].arg
# Search assign statements in constructor
for stmt in func.body:
if (isinstance(stmt, ast.Assign) and
isinstance(stmt.targets[0], ast.Attribute) and
isinstance(stmt.targets[0].value, ast.Name) and
stmt.targets[0].value.id == self):
members.append(stmt.targets[0].attr)
return members
def myhelp(name):
"""Document generator for class."""
# Import the class
modname, clsname = name.rsplit(".", 1)
module = importlib.import_module(modname)
cls = getattr(module, clsname)
classname = cls.__module__ + "." + cls.__name__
doc = inspect.getdoc(cls)
members = []
for name in dir(cls):
if name.startswith('__') and name.endswith('__'):
# Filter dunder members (e.g. __init__)
pass
else:
members.append(name)
# Instance variables
for name in get_ivars_from_constructor(cls):
if name not in members:
members.append(name)
print("Usage of %s" % classname)
print(textwrap.indent(doc, ' '))
print("")
print("Members:")
for name in members:
print("- %s" % name)
if __name__ == '__main__':
myhelp("example.Foo")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment