diff --git a/acceptance_tests/app/Dockerfile b/acceptance_tests/app/Dockerfile index abe81a287..efe489231 100644 --- a/acceptance_tests/app/Dockerfile +++ b/acceptance_tests/app/Dockerfile @@ -20,6 +20,9 @@ ENV DEVELOPMENT 0 # Step #2 copy the rest of the files (watch for the .dockerignore) COPY . /app -RUN python /app/setup.py install + +RUN python ./setup.py install && \ + ./models_graph.py > models.dot && \ + ./models_graph.py Hello > models_hello.dot CMD ["c2cwsgiutils_run"] diff --git a/acceptance_tests/app/models_graph.py b/acceptance_tests/app/models_graph.py new file mode 100755 index 000000000..2fedeb0a8 --- /dev/null +++ b/acceptance_tests/app/models_graph.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +from c2cwsgiutils.models_graph import generate_model_graph + +from c2cwsgiutils_app import models + + +def main(): + generate_model_graph(models) + + +main() diff --git a/c2cwsgiutils/models_graph.py b/c2cwsgiutils/models_graph.py new file mode 100644 index 000000000..8025682a6 --- /dev/null +++ b/c2cwsgiutils/models_graph.py @@ -0,0 +1,82 @@ +import inspect +import sqlalchemy as sa +import sys + + +def generate_model_graph(module): + if len(sys.argv) == 1: + base_name = 'Base' + elif len(sys.argv) == 2: + base_name = sys.argv[1] + else: + print("Invalid parameters\nUsage: %s [base_class]" % sys.argv[0]) + exit(1) + + _generate_model_graph(module, getattr(module, base_name)) + + +def _generate_model_graph(module, base): + print(""" + digraph { + rankdir=BT; + """) + + interesting = { + getattr(module, symbol_name) + for symbol_name in dir(module) + if _is_interesting(getattr(module, symbol_name), base) + } + + for symbol in list(interesting): + symbol = getattr(module, symbol.__name__) + if _is_interesting(symbol, base): + _print_node(symbol, interesting) + + print("}") + + +def _print_node(symbol, interesting): + print('%s [label="%s", shape=box];' % (symbol.__name__, _get_table_desc(symbol))) + for parent in symbol.__bases__: + if parent != object: + if parent not in interesting: + _print_node(parent, interesting) + interesting.add(parent) + print("%s -> %s;" % (symbol.__name__, parent.__name__)) + + +def _is_interesting(what, base): + return inspect.isclass(what) and issubclass(what, base) + + +def _get_table_desc(symbol): + cols = [symbol.__name__, ""] + _get_local_cols(symbol) + + return "\\n".join(cols) + + +def _get_all_cols(symbol): + cols = [] + + for member_name in symbol.__dict__: + member = getattr(symbol, member_name) + if member_name in ('__table__', 'metadata'): + pass + elif isinstance(member, sa.sql.schema.SchemaItem): + cols.append(member_name + ('[null]' if member.nullable else '')) + elif isinstance(member, sa.orm.attributes.InstrumentedAttribute): + nullable = member.property.columns[0].nullable \ + if isinstance(member.property, sa.orm.ColumnProperty) \ + else False + link = not isinstance(member.property, sa.orm.ColumnProperty) + cols.append(member_name + (' [null]' if nullable else '') + (' ->' if link else '')) + + return cols + + +def _get_local_cols(symbol): + result = set(_get_all_cols(symbol)) + for parent in symbol.__bases__: + result -= set(_get_all_cols(parent)) + + return sorted(list(result)) diff --git a/setup.py b/setup.py index e5c35891b..8c0201082 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages -version = '0.4.0' +version = '0.5.0' setup( name='c2cwsgiutils',