load: Add --recursive option.
[zzz-pokedex.git] / pokedex / db / dependencies.py
1 import sqlalchemy.sql.visitors as visitors
2
3 from pokedex.db.tables import metadata
4
5 # stolen from sqlalchemy.sql.util.sort_tables
6 def compute_dependencies(tables):
7 """Construct a reverse dependency graph for the given tables.
8
9 Returns a dict which maps a table to the list of tables which depend on it.
10 """
11 tables = list(tables)
12 graph = {}
13 def visit_foreign_key(fkey):
14 if fkey.use_alter:
15 return
16 parent_table = fkey.column.table
17 if parent_table in tables:
18 child_table = fkey.parent.table
19 if parent_table is not child_table:
20 graph.setdefault(parent_table, []).append(child_table)
21
22 for table in tables:
23 visitors.traverse(table,
24 {'schema_visitor': True},
25 {'foreign_key': visit_foreign_key})
26
27 graph.setdefault(table, []).extend(table._extra_dependencies)
28
29 return graph
30
31 #: The dependency graph for pokedex.db.tables
32 _pokedex_graph = compute_dependencies(metadata.tables.values())
33
34 def find_dependent_tables(tables, graph=None):
35 """Recursively find all tables which depend on the given tables.
36
37 The returned set does not include the original tables.
38 """
39 if graph is None:
40 graph = _pokedex_graph
41 tables = list(tables)
42 dependents = set()
43 def add_dependents_of(table):
44 for dependent_table in graph.get(table, []):
45 if dependent_table not in dependents:
46 dependents.add(dependent_table)
47 add_dependents_of(dependent_table)
48
49 for table in tables:
50 add_dependents_of(table)
51
52 dependents -= set(tables)
53
54 return dependents