-
Notifications
You must be signed in to change notification settings - Fork 0
/
dep_graph.py
70 lines (60 loc) · 2.49 KB
/
dep_graph.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import os
import json
import sys
from json.decoder import JSONDecodeError
class CyclicalDependencyException(Exception):
"""Exception raised when a cyclical dependency is detected"""
pass
class DependencyGraph:
"""Class to consume and traverse a package list from json format."""
def __init__(self, json_filepath):
self.graph = self.read_json(json_filepath)
@staticmethod
def read_json(json_filepath):
"""consume a json file and return it as an adjacency list
Args:
json_filepath (str): filepath of the json to consume
Returns:
adj_list (dict): the json as an adjacency list
"""
if not os.path.exists(json_filepath):
raise FileNotFoundError(f'JSON file {json_filepath} '
f'does not exist on this system.')
try:
with open(json_filepath) as json_file:
adj_list = json.load(json_file)
except JSONDecodeError:
raise TypeError(f'File {json_filepath} is not in the correct '
f'json format.')
return adj_list
def print_graph(self):
"""Prints the graph in fully-traversed bulletpoint format"""
for package in list(self.graph.keys()):
self.traverse_dependencies(package)
def traverse_dependencies(self, package, indent=0, visited=None):
"""Recursive method to traverse all dependencies.
Args:
package (str): package to traverse sub-dependencies of
indent (int): number of indents to print with the current package
visited (set): set of nodes that have been visited
"""
if visited is None:
visited = set()
if package in visited:
raise CyclicalDependencyException('CYCLICAL DEPENDENCY DETECTED')
print(" " * indent + f"- {package}")
visited.add(package)
if package in self.graph:
dependency_list = self.graph[package]
for item in dependency_list:
self.traverse_dependencies(item, indent+2, visited)
visited.remove(item)
if __name__ == '__main__':
if len(sys.argv) >= 2:
# allow the user to provide the target json from the command line.
# we check the second arg, since the first one is this script.
target_json = sys.argv[1]
else:
target_json = 'test_files/example.json' # default to example file
d = DependencyGraph(target_json)
d.print_graph()