Skip to content

Instantly share code, notes, and snippets.

@timmc-edx
Last active August 12, 2022 18:36
Show Gist options
  • Select an option

  • Save timmc-edx/d6d48f62c4287ae7147aaed6c2a18f25 to your computer and use it in GitHub Desktop.

Select an option

Save timmc-edx/d6d48f62c4287ae7147aaed6c2a18f25 to your computer and use it in GitHub Desktop.
Load and display Python class hierarchy
"""
Build and display a graph of classes in a module.
Call from virtualenv of repo you're inspecting.
Example call, from inside edx-platform:
DJANGO_SETTINGS_MODULE=lms.envs.test python3 ./class_graph.py xmodule.modulestore BulkOperationsMixin | dot -Tsvg > BulkOperationsMixin.svg
"""
import pkgutil
import re
import sys
from importlib import import_module
# Some nonsense to allow importing of modules in Django repos that try
# to load settings or work with models on module load.
try:
import django
django.setup()
except BaseException:
pass
def main(in_module, *start_classes_rel):
re_in = re.compile('^' + re.escape(in_module) + r'(\.|$)')
def is_in_module(cls):
return re_in.search(cls.__module__)
# Make sure all submodules are loaded first (so that subclasses can be found)
start_load = import_module(in_module)
for m in pkgutil.walk_packages(start_load.__path__, start_load.__name__ + '.'):
if re.search(r'(^|[_.])tests?([_.]|$)', m.name):
continue
import_module(m.name)
# Load the initial set of classes
classes = set()
for crel in start_classes_rel:
(full_mod, classname) = f'{in_module}.{crel}'.rsplit('.', 1)
classes.add(getattr(import_module(full_mod), classname))
# Transitive closure on class ancestors and descendents (within module)
while True:
wider = set()
for cls in classes:
wider.add(cls)
wider |= set(cls.__subclasses__())
wider |= set(cls.__bases__)
wider = {c for c in wider if is_in_module(c)}
expanded = wider == classes
classes = wider
if expanded:
break
# Print to DOT language for graphviz
def outname(cls):
""" Dotted module/class path relative to base module. """
return (cls.__module__ + '.' + cls.__name__)[len(in_module):]
print('digraph {')
for cls in classes:
for base in cls.__bases__:
if is_in_module(base):
# dot lays out top to bottom, but we want arrows to point upwards.
# So, we have edges go down, but with a backwards arrow!
print(f' "{outname(base)}" -> "{outname(cls)}" [dir=back]')
print('}')
if __name__ == '__main__':
main(*sys.argv[1:])
Display the source blob
Display the rendered blob
Raw
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: %3 Pages: 1 -->
<svg width="1665pt" height="404pt"
viewBox="0.00 0.00 1665.47 404.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 400)">
<title>%3</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-400 1661.47,-400 1661.47,4 -4,4"/>
<!-- .BulkOperationsMixin -->
<g id="node1" class="node">
<title>.BulkOperationsMixin</title>
<ellipse fill="none" stroke="black" cx="823.99" cy="-306" rx="112.38" ry="18"/>
<text text-anchor="middle" x="823.99" y="-302.3" font-family="Times,serif" font-size="14.00">.BulkOperationsMixin</text>
</g>
<!-- .split_mongo.split.SplitBulkWriteMixin -->
<g id="node2" class="node">
<title>.split_mongo.split.SplitBulkWriteMixin</title>
<ellipse fill="none" stroke="black" cx="922.99" cy="-162" rx="190.37" ry="18"/>
<text text-anchor="middle" x="922.99" y="-158.3" font-family="Times,serif" font-size="14.00">.split_mongo.split.SplitBulkWriteMixin</text>
</g>
<!-- .BulkOperationsMixin&#45;&gt;.split_mongo.split.SplitBulkWriteMixin -->
<g id="edge1" class="edge">
<title>.BulkOperationsMixin&#45;&gt;.split_mongo.split.SplitBulkWriteMixin</title>
<path fill="none" stroke="black" d="M841.72,-279.56C861.73,-250.86 893.73,-204.96 911,-180.19"/>
<polygon fill="black" stroke="black" points="838.77,-277.67 835.93,-287.87 844.52,-281.67 838.77,-277.67"/>
</g>
<!-- .draft_and_published.ModuleStoreDraftAndPublished -->
<g id="node5" class="node">
<title>.draft_and_published.ModuleStoreDraftAndPublished</title>
<ellipse fill="none" stroke="black" cx="455.99" cy="-162" rx="258.85" ry="18"/>
<text text-anchor="middle" x="455.99" y="-158.3" font-family="Times,serif" font-size="14.00">.draft_and_published.ModuleStoreDraftAndPublished</text>
</g>
<!-- .BulkOperationsMixin&#45;&gt;.draft_and_published.ModuleStoreDraftAndPublished -->
<g id="edge19" class="edge">
<title>.BulkOperationsMixin&#45;&gt;.draft_and_published.ModuleStoreDraftAndPublished</title>
<path fill="none" stroke="black" d="M815.69,-278.17C807.97,-257.82 794.37,-230.88 772.99,-216 746.19,-197.35 677.68,-184.68 611.17,-176.41"/>
<polygon fill="black" stroke="black" points="812.48,-279.58 819.12,-287.83 819.08,-277.24 812.48,-279.58"/>
</g>
<!-- .mongo.base.MongoBulkOpsMixin -->
<g id="node9" class="node">
<title>.mongo.base.MongoBulkOpsMixin</title>
<ellipse fill="none" stroke="black" cx="168.99" cy="-234" rx="168.97" ry="18"/>
<text text-anchor="middle" x="168.99" y="-230.3" font-family="Times,serif" font-size="14.00">.mongo.base.MongoBulkOpsMixin</text>
</g>
<!-- .BulkOperationsMixin&#45;&gt;.mongo.base.MongoBulkOpsMixin -->
<g id="edge6" class="edge">
<title>.BulkOperationsMixin&#45;&gt;.mongo.base.MongoBulkOpsMixin</title>
<path fill="none" stroke="black" d="M723.08,-294.22C604.43,-281.54 407.94,-260.54 283.63,-247.25"/>
<polygon fill="black" stroke="black" points="722.94,-297.72 733.25,-295.3 723.68,-290.76 722.94,-297.72"/>
</g>
<!-- .ModuleStoreReadBase -->
<g id="node18" class="node">
<title>.ModuleStoreReadBase</title>
<ellipse fill="none" stroke="black" cx="1063.99" cy="-234" rx="119.68" ry="18"/>
<text text-anchor="middle" x="1063.99" y="-230.3" font-family="Times,serif" font-size="14.00">.ModuleStoreReadBase</text>
</g>
<!-- .BulkOperationsMixin&#45;&gt;.ModuleStoreReadBase -->
<g id="edge23" class="edge">
<title>.BulkOperationsMixin&#45;&gt;.ModuleStoreReadBase</title>
<path fill="none" stroke="black" d="M885.17,-287.16C924.29,-275.74 974.51,-261.1 1011.76,-250.23"/>
<polygon fill="black" stroke="black" points="884.09,-283.82 875.47,-289.98 886.05,-290.54 884.09,-283.82"/>
</g>
<!-- .split_mongo.split.SplitMongoModuleStore -->
<g id="node3" class="node">
<title>.split_mongo.split.SplitMongoModuleStore</title>
<ellipse fill="none" stroke="black" cx="1054.99" cy="-90" rx="209.86" ry="18"/>
<text text-anchor="middle" x="1054.99" y="-86.3" font-family="Times,serif" font-size="14.00">.split_mongo.split.SplitMongoModuleStore</text>
</g>
<!-- .split_mongo.split.SplitBulkWriteMixin&#45;&gt;.split_mongo.split.SplitMongoModuleStore -->
<g id="edge7" class="edge">
<title>.split_mongo.split.SplitBulkWriteMixin&#45;&gt;.split_mongo.split.SplitMongoModuleStore</title>
<path fill="none" stroke="black" d="M963.69,-139.41C982.83,-129.27 1005.31,-117.35 1023.17,-107.87"/>
<polygon fill="black" stroke="black" points="961.8,-136.46 954.6,-144.23 965.08,-142.64 961.8,-136.46"/>
</g>
<!-- .split_mongo.split_draft.DraftVersioningModuleStore -->
<g id="node4" class="node">
<title>.split_mongo.split_draft.DraftVersioningModuleStore</title>
<ellipse fill="none" stroke="black" cx="912.99" cy="-18" rx="256.95" ry="18"/>
<text text-anchor="middle" x="912.99" y="-14.3" font-family="Times,serif" font-size="14.00">.split_mongo.split_draft.DraftVersioningModuleStore</text>
</g>
<!-- .split_mongo.split.SplitMongoModuleStore&#45;&gt;.split_mongo.split_draft.DraftVersioningModuleStore -->
<g id="edge2" class="edge">
<title>.split_mongo.split.SplitMongoModuleStore&#45;&gt;.split_mongo.split_draft.DraftVersioningModuleStore</title>
<path fill="none" stroke="black" d="M1011.75,-67.69C991.05,-57.48 966.6,-45.43 947.21,-35.87"/>
<polygon fill="black" stroke="black" points="1010.46,-70.95 1020.97,-72.23 1013.55,-64.67 1010.46,-70.95"/>
</g>
<!-- .draft_and_published.ModuleStoreDraftAndPublished&#45;&gt;.split_mongo.split_draft.DraftVersioningModuleStore -->
<g id="edge3" class="edge">
<title>.draft_and_published.ModuleStoreDraftAndPublished&#45;&gt;.split_mongo.split_draft.DraftVersioningModuleStore</title>
<path fill="none" stroke="black" d="M481.58,-136.77C509.41,-110.41 549.99,-72 549.99,-72 586.37,-54.35 675.99,-41.17 757.08,-32.36"/>
<polygon fill="black" stroke="black" points="478.96,-134.42 474.11,-143.84 483.78,-139.51 478.96,-134.42"/>
</g>
<!-- .mongo.base.MongoModuleStore -->
<g id="node12" class="node">
<title>.mongo.base.MongoModuleStore</title>
<ellipse fill="none" stroke="black" cx="338.99" cy="-90" rx="163.57" ry="18"/>
<text text-anchor="middle" x="338.99" y="-86.3" font-family="Times,serif" font-size="14.00">.mongo.base.MongoModuleStore</text>
</g>
<!-- .draft_and_published.ModuleStoreDraftAndPublished&#45;&gt;.mongo.base.MongoModuleStore -->
<g id="edge14" class="edge">
<title>.draft_and_published.ModuleStoreDraftAndPublished&#45;&gt;.mongo.base.MongoModuleStore</title>
<path fill="none" stroke="black" d="M418.68,-138.68C401.94,-128.67 382.52,-117.05 367.05,-107.79"/>
<polygon fill="black" stroke="black" points="417.28,-141.92 427.66,-144.05 420.88,-135.92 417.28,-141.92"/>
</g>
<!-- .mixed.MixedModuleStore -->
<g id="node14" class="node">
<title>.mixed.MixedModuleStore</title>
<ellipse fill="none" stroke="black" cx="692.99" cy="-90" rx="133.78" ry="18"/>
<text text-anchor="middle" x="692.99" y="-86.3" font-family="Times,serif" font-size="14.00">.mixed.MixedModuleStore</text>
</g>
<!-- .draft_and_published.ModuleStoreDraftAndPublished&#45;&gt;.mixed.MixedModuleStore -->
<g id="edge11" class="edge">
<title>.draft_and_published.ModuleStoreDraftAndPublished&#45;&gt;.mixed.MixedModuleStore</title>
<path fill="none" stroke="black" d="M521.92,-141.53C559.19,-130.52 605.17,-116.94 640.07,-106.63"/>
<polygon fill="black" stroke="black" points="520.75,-138.22 512.15,-144.41 522.73,-144.93 520.75,-138.22"/>
</g>
<!-- .ModuleStoreRead -->
<g id="node6" class="node">
<title>.ModuleStoreRead</title>
<ellipse fill="none" stroke="black" cx="1063.99" cy="-306" rx="96.68" ry="18"/>
<text text-anchor="middle" x="1063.99" y="-302.3" font-family="Times,serif" font-size="14.00">.ModuleStoreRead</text>
</g>
<!-- .ModuleStoreWrite -->
<g id="node7" class="node">
<title>.ModuleStoreWrite</title>
<ellipse fill="none" stroke="black" cx="1299.99" cy="-234" rx="98.58" ry="18"/>
<text text-anchor="middle" x="1299.99" y="-230.3" font-family="Times,serif" font-size="14.00">.ModuleStoreWrite</text>
</g>
<!-- .ModuleStoreRead&#45;&gt;.ModuleStoreWrite -->
<g id="edge4" class="edge">
<title>.ModuleStoreRead&#45;&gt;.ModuleStoreWrite</title>
<path fill="none" stroke="black" d="M1122.89,-287.53C1162.18,-275.87 1213.26,-260.72 1250.46,-249.69"/>
<polygon fill="black" stroke="black" points="1121.76,-284.21 1113.17,-290.41 1123.75,-290.92 1121.76,-284.21"/>
</g>
<!-- .ModuleStoreRead&#45;&gt;.ModuleStoreReadBase -->
<g id="edge24" class="edge">
<title>.ModuleStoreRead&#45;&gt;.ModuleStoreReadBase</title>
<path fill="none" stroke="black" d="M1063.99,-277.67C1063.99,-269.05 1063.99,-259.79 1063.99,-252.1"/>
<polygon fill="black" stroke="black" points="1060.49,-277.7 1063.99,-287.7 1067.49,-277.7 1060.49,-277.7"/>
</g>
<!-- .ModuleStoreWriteBase -->
<g id="node10" class="node">
<title>.ModuleStoreWriteBase</title>
<ellipse fill="none" stroke="black" cx="1252.99" cy="-162" rx="121.58" ry="18"/>
<text text-anchor="middle" x="1252.99" y="-158.3" font-family="Times,serif" font-size="14.00">.ModuleStoreWriteBase</text>
</g>
<!-- .ModuleStoreWrite&#45;&gt;.ModuleStoreWriteBase -->
<g id="edge22" class="edge">
<title>.ModuleStoreWrite&#45;&gt;.ModuleStoreWriteBase</title>
<path fill="none" stroke="black" d="M1283,-207.71C1276.89,-198.6 1270.13,-188.54 1264.58,-180.28"/>
<polygon fill="black" stroke="black" points="1280.13,-209.7 1288.61,-216.05 1285.94,-205.8 1280.13,-209.7"/>
</g>
<!-- .ModuleStoreAssetWriteInterface -->
<g id="node8" class="node">
<title>.ModuleStoreAssetWriteInterface</title>
<ellipse fill="none" stroke="black" cx="1344.99" cy="-306" rx="166.27" ry="18"/>
<text text-anchor="middle" x="1344.99" y="-302.3" font-family="Times,serif" font-size="14.00">.ModuleStoreAssetWriteInterface</text>
</g>
<!-- .ModuleStoreAssetWriteInterface&#45;&gt;.ModuleStoreWrite -->
<g id="edge5" class="edge">
<title>.ModuleStoreAssetWriteInterface&#45;&gt;.ModuleStoreWrite</title>
<path fill="none" stroke="black" d="M1328.32,-279.08C1322.53,-270.08 1316.19,-260.21 1310.98,-252.1"/>
<polygon fill="black" stroke="black" points="1325.51,-281.18 1333.86,-287.7 1331.4,-277.39 1325.51,-281.18"/>
</g>
<!-- .mongo.base.MongoBulkOpsMixin&#45;&gt;.mongo.base.MongoModuleStore -->
<g id="edge16" class="edge">
<title>.mongo.base.MongoBulkOpsMixin&#45;&gt;.mongo.base.MongoModuleStore</title>
<path fill="none" stroke="black" d="M168.12,-205.69C168.95,-186.29 173.19,-160.81 187.99,-144 204.51,-125.23 228.07,-113.11 251.69,-105.28"/>
<polygon fill="black" stroke="black" points="164.62,-205.69 167.96,-215.74 171.62,-205.8 164.62,-205.69"/>
</g>
<!-- .ModuleStoreWriteBase&#45;&gt;.split_mongo.split.SplitMongoModuleStore -->
<g id="edge8" class="edge">
<title>.ModuleStoreWriteBase&#45;&gt;.split_mongo.split.SplitMongoModuleStore</title>
<path fill="none" stroke="black" d="M1198.48,-141.73C1168.15,-131.01 1130.8,-117.8 1101.8,-107.55"/>
<polygon fill="black" stroke="black" points="1197.46,-145.08 1208.06,-145.12 1199.79,-138.48 1197.46,-145.08"/>
</g>
<!-- .ModuleStoreWriteBase&#45;&gt;.mongo.base.MongoModuleStore -->
<g id="edge15" class="edge">
<title>.ModuleStoreWriteBase&#45;&gt;.mongo.base.MongoModuleStore</title>
<path fill="none" stroke="black" d="M1157.75,-147.89C1145.73,-146.46 1133.58,-145.12 1121.99,-144 868.43,-119.6 804.06,-126.23 549.99,-108 522.49,-106.03 493,-103.75 465.04,-101.52"/>
<polygon fill="black" stroke="black" points="1157.46,-151.38 1167.81,-149.12 1158.31,-144.43 1157.46,-151.38"/>
</g>
<!-- .ModuleStoreWriteBase&#45;&gt;.mixed.MixedModuleStore -->
<g id="edge12" class="edge">
<title>.ModuleStoreWriteBase&#45;&gt;.mixed.MixedModuleStore</title>
<path fill="none" stroke="black" d="M1155.05,-148.22C1143.9,-146.79 1132.7,-145.36 1121.99,-144 1007.52,-129.5 875.8,-113.3 788.85,-102.67"/>
<polygon fill="black" stroke="black" points="1154.9,-151.73 1165.26,-149.54 1155.79,-144.79 1154.9,-151.73"/>
</g>
<!-- .ModuleStoreAssetBase -->
<g id="node11" class="node">
<title>.ModuleStoreAssetBase</title>
<ellipse fill="none" stroke="black" cx="1203.99" cy="-378" rx="121.58" ry="18"/>
<text text-anchor="middle" x="1203.99" y="-374.3" font-family="Times,serif" font-size="14.00">.ModuleStoreAssetBase</text>
</g>
<!-- .ModuleStoreAssetBase&#45;&gt;.ModuleStoreRead -->
<g id="edge13" class="edge">
<title>.ModuleStoreAssetBase&#45;&gt;.ModuleStoreRead</title>
<path fill="none" stroke="black" d="M1162.08,-356.05C1140.94,-345.48 1115.7,-332.86 1096.12,-323.07"/>
<polygon fill="black" stroke="black" points="1160.65,-359.25 1171.16,-360.59 1163.78,-352.99 1160.65,-359.25"/>
</g>
<!-- .ModuleStoreAssetBase&#45;&gt;.ModuleStoreAssetWriteInterface -->
<g id="edge9" class="edge">
<title>.ModuleStoreAssetBase&#45;&gt;.ModuleStoreAssetWriteInterface</title>
<path fill="none" stroke="black" d="M1246.27,-356.01C1267.02,-345.71 1291.67,-333.47 1311.15,-323.8"/>
<polygon fill="black" stroke="black" points="1244.44,-353.01 1237.04,-360.59 1247.56,-359.28 1244.44,-353.01"/>
</g>
<!-- .mongo.draft.DraftModuleStore -->
<g id="node13" class="node">
<title>.mongo.draft.DraftModuleStore</title>
<ellipse fill="none" stroke="black" cx="338.99" cy="-18" rx="157.87" ry="18"/>
<text text-anchor="middle" x="338.99" y="-14.3" font-family="Times,serif" font-size="14.00">.mongo.draft.DraftModuleStore</text>
</g>
<!-- .mongo.base.MongoModuleStore&#45;&gt;.mongo.draft.DraftModuleStore -->
<g id="edge10" class="edge">
<title>.mongo.base.MongoModuleStore&#45;&gt;.mongo.draft.DraftModuleStore</title>
<path fill="none" stroke="black" d="M338.99,-61.67C338.99,-53.05 338.99,-43.79 338.99,-36.1"/>
<polygon fill="black" stroke="black" points="335.49,-61.7 338.99,-71.7 342.49,-61.7 335.49,-61.7"/>
</g>
<!-- .xml.XMLModuleStore -->
<g id="node15" class="node">
<title>.xml.XMLModuleStore</title>
<ellipse fill="none" stroke="black" cx="1507.99" cy="-162" rx="115.08" ry="18"/>
<text text-anchor="middle" x="1507.99" y="-158.3" font-family="Times,serif" font-size="14.00">.xml.XMLModuleStore</text>
</g>
<!-- .xml.LibraryXMLModuleStore -->
<g id="node16" class="node">
<title>.xml.LibraryXMLModuleStore</title>
<ellipse fill="none" stroke="black" cx="1507.99" cy="-90" rx="149.47" ry="18"/>
<text text-anchor="middle" x="1507.99" y="-86.3" font-family="Times,serif" font-size="14.00">.xml.LibraryXMLModuleStore</text>
</g>
<!-- .xml.XMLModuleStore&#45;&gt;.xml.LibraryXMLModuleStore -->
<g id="edge17" class="edge">
<title>.xml.XMLModuleStore&#45;&gt;.xml.LibraryXMLModuleStore</title>
<path fill="none" stroke="black" d="M1507.99,-133.67C1507.99,-125.05 1507.99,-115.79 1507.99,-108.1"/>
<polygon fill="black" stroke="black" points="1504.49,-133.7 1507.99,-143.7 1511.49,-133.7 1504.49,-133.7"/>
</g>
<!-- .draft_and_published.BranchSettingMixin -->
<g id="node17" class="node">
<title>.draft_and_published.BranchSettingMixin</title>
<ellipse fill="none" stroke="black" cx="559.99" cy="-234" rx="204.16" ry="18"/>
<text text-anchor="middle" x="559.99" y="-230.3" font-family="Times,serif" font-size="14.00">.draft_and_published.BranchSettingMixin</text>
</g>
<!-- .draft_and_published.BranchSettingMixin&#45;&gt;.draft_and_published.ModuleStoreDraftAndPublished -->
<g id="edge18" class="edge">
<title>.draft_and_published.BranchSettingMixin&#45;&gt;.draft_and_published.ModuleStoreDraftAndPublished</title>
<path fill="none" stroke="black" d="M526.48,-210.45C511.81,-200.57 494.88,-189.18 481.29,-180.03"/>
<polygon fill="black" stroke="black" points="524.56,-213.37 534.81,-216.05 528.47,-207.57 524.56,-213.37"/>
</g>
<!-- .ModuleStoreReadBase&#45;&gt;.ModuleStoreWriteBase -->
<g id="edge21" class="edge">
<title>.ModuleStoreReadBase&#45;&gt;.ModuleStoreWriteBase</title>
<path fill="none" stroke="black" d="M1116.57,-213.52C1145.99,-202.63 1182.22,-189.21 1209.94,-178.94"/>
<polygon fill="black" stroke="black" points="1115.03,-210.36 1106.87,-217.12 1117.47,-216.92 1115.03,-210.36"/>
</g>
<!-- .ModuleStoreReadBase&#45;&gt;.xml.XMLModuleStore -->
<g id="edge20" class="edge">
<title>.ModuleStoreReadBase&#45;&gt;.xml.XMLModuleStore</title>
<path fill="none" stroke="black" d="M1153.55,-218.88C1234.61,-206.1 1352.8,-187.47 1430.04,-175.29"/>
<polygon fill="black" stroke="black" points="1152.7,-215.47 1143.36,-220.49 1153.79,-222.39 1152.7,-215.47"/>
</g>
</g>
</svg>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment