This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
"""Export GAE model to dot format.""" | |
# Usage example: | |
# ./ndb2dot.py cool.model | dot -Tpng -o model.png | |
# Install graphviz to get the dot command line utility | |
from inspect import isclass | |
from os import environ | |
import imp | |
import sys | |
# Inject GAE SDK path | |
sys.path.insert(0, environ.get('GAE_PY_SDK', '/opt/google_appengine')) | |
import dev_appserver # noqa | |
dev_appserver.fix_sys_path() | |
from google.appengine.ext import ndb # noqa | |
header = '''\ | |
digraph G { | |
graph [rankdir=LR]; | |
node [shape=none]; | |
''' | |
def load_module(name): | |
"""Load module from name""" | |
mod = None | |
for mod_name in name.split('.'): | |
file, pathname, desc = imp.find_module(mod_name, mod and mod.__path__) | |
mod = imp.load_module(mod_name, file, pathname, desc) | |
return mod | |
def models(module): | |
"""Sorted list (by name) of models in module""" | |
for attr in dir(module): | |
obj = getattr(module, attr) | |
if isclass(obj) and issubclass(obj, ndb.Model): | |
yield obj | |
def prop_type(prop): | |
"""Property type (string)""" | |
if isinstance(prop, ndb.StructuredProperty): | |
return prop._modelclass.__name__ | |
name = prop.__class__.__name__ | |
suffix = 'Property' | |
if name.endswith(suffix): | |
name = name[:-len(suffix)] | |
return name | |
def model2dot(model): | |
"""dot represtantation of model""" | |
cls = model.__name__ | |
print('''\ | |
%s [ | |
label = <<table> | |
<tr><td colspan="2"><b>%s</b></td></tr>''' % (cls, cls)) | |
links = [] | |
for name, prop in sorted(model._properties.iteritems()): | |
if isinstance(prop, ndb.StructuredProperty): | |
port = 'port="%s"' % name | |
links.append((name, prop)) | |
else: | |
port = '' | |
print('%s<tr><td %s>%s</td><td>%s</td></tr>' % ( | |
' ' * 8, port, name, prop_type(prop))) | |
print('''\ | |
</table>> | |
];''') | |
for name, prop in links: | |
print(' %s:%s -> %s;' % (cls, name, prop_type(prop))) | |
if __name__ == '__main__': | |
from operator import attrgetter | |
from argparse import ArgumentParser | |
parser = ArgumentParser(description=__doc__) | |
parser.add_argument('module', help='module name (e.g. "cool.model")') | |
args = parser.parse_args() | |
try: | |
mod = load_module(args.module) | |
except ImportError as err: | |
raise SystemExit('error: cannot load %r - %s' % (args.module, err)) | |
print(header) | |
for model in sorted(models(mod), key=attrgetter('__name__')): | |
model2dot(model) | |
print('}') |
No comments:
Post a Comment