Skip to content

Instantly share code, notes, and snippets.

@abadger
Created August 20, 2024 18:22
Show Gist options
  • Save abadger/a260d656344217307ca0282d2ac501b2 to your computer and use it in GitHub Desktop.
Save abadger/a260d656344217307ca0282d2ac501b2 to your computer and use it in GitHub Desktop.
Parsing "docstrings" for class attributes.
#!/usr/bin/python3 -tt
import ast
import inspect
# Define the classes from your code
class MyField():
"""
That's my field
"""
def __init__(self, value=None):
self.value = value
class KindOfModel():
"""
My docstring title
Docstring body
"""
field1 = MyField("hooray")
"""
Whatever field in the KindOfModel.
"""
class MyModel(KindOfModel):
field2 = MyField("boom")
"""
chick-a-lacka
"""
def get_source(filename):
with open(filename) as f:
return f.read()
# Function to retrieve attribute-level docstrings
def get_attribute_docstrings(source):
# Parse the source code into an AST
tree = ast.parse(source)
# Find the class definition in the AST
models = {}
for node in ast.walk(tree):
### TODO: Need to look at whether this is a subclass of Model
# Will we need to import the code to make this work?
if isinstance(node, ast.ClassDef):
models[node.name] = {}
# Iterate over all the class body elements
for sub_node in node.body:
# Check if the sub_node is an assignment (i.e., a field definition)
if isinstance(sub_node, ast.Assign):
for target in sub_node.targets:
### TODO: do we have any specific attributes to filter out?
if isinstance(target, ast.Name):
# Look for the following Expr node which contains the docstring
models[node.name][target.id] = ""
next_node_index = node.body.index(sub_node) + 1
if next_node_index < len(node.body):
next_node = node.body[next_node_index]
if isinstance(next_node, ast.Expr) and isinstance(next_node.value, ast.Str):
models[node.name][target.id] = next_node.value.s
return models
# Retrieve the class and field docstrings
def get_class_and_field_docstrings(cls):
with open(__file__) as f:
source = f.read()
models = get_attribute_docstrings(source)
for model_name, attributes in models.items():
print("=== {} ===".format(model_name))
for attr_name, docstring in attributes.items():
print("Attribute {}.{}: {}".format(model_name, attr_name, docstring))
# Get docstrings for KindOfModel
get_class_and_field_docstrings(MyModel)
@abadger
Copy link
Author

abadger commented Aug 20, 2024

Output:

=== MyField ===
=== KindOfModel ===
Attribute KindOfModel.field1: 
    Whatever field in the KindOfModel.
    
=== MyModel ===
Attribute MyModel.field2: 
    chick-a-lacka

But what we want is for MyModel to include both field1 and field2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment