| @@ 46-100 (lines=55) @@ | ||
| 43 | yield from _ast_edge(child_ast) |
|
| 44 | ||
| 45 | ||
| 46 | def ast_graph_model(): |
|
| 47 | """ |
|
| 48 | Create an abstract syntax tree (AST) graph model. |
|
| 49 | ||
| 50 | Returns: |
|
| 51 | GraphModel: A graph model representing the AST nodes and their relationships. |
|
| 52 | """ |
|
| 53 | ||
| 54 | code_object = graphinate.builders.D3Builder |
|
| 55 | ||
| 56 | graph_model = graphinate.model(name=f'AST Graph - {code_object.__qualname__}',) |
|
| 57 | ||
| 58 | root_ast_node = ast.parse(inspect.getsource(code_object)) |
|
| 59 | ||
| 60 | def node_type(ast_node): |
|
| 61 | return ast_node.__class__.__name__ |
|
| 62 | ||
| 63 | def node_label(ast_node) -> str: |
|
| 64 | label = ast_node.__class__.__name__ |
|
| 65 | ||
| 66 | for field_name in ('name', 'id'): |
|
| 67 | if field_name in ast_node._fields: |
|
| 68 | value = operator.attrgetter(field_name)(ast_node) |
|
| 69 | label = f"{label}\n{field_name}: {value}" |
|
| 70 | ||
| 71 | return label |
|
| 72 | ||
| 73 | def key(value): |
|
| 74 | # noinspection InsecureHash |
|
| 75 | return hashlib.shake_128(pickle.dumps(value)).hexdigest(20) |
|
| 76 | ||
| 77 | def endpoint(value, endpoint_name): |
|
| 78 | return key(value[endpoint_name]) |
|
| 79 | ||
| 80 | def source(value): |
|
| 81 | return endpoint(value, 'source') |
|
| 82 | ||
| 83 | def target(value): |
|
| 84 | return endpoint(value, 'target') |
|
| 85 | ||
| 86 | @graph_model.node(type_=node_type, |
|
| 87 | key=key, |
|
| 88 | label=node_label, |
|
| 89 | unique=True) |
|
| 90 | def ast_node(**kwargs): |
|
| 91 | yield from _ast_nodes([root_ast_node]) |
|
| 92 | ||
| 93 | @graph_model.edge(type_='edge', |
|
| 94 | source=source, |
|
| 95 | target=target, |
|
| 96 | label=operator.itemgetter('type')) |
|
| 97 | def ast_edge(**kwargs): |
|
| 98 | yield from _ast_edge(root_ast_node) |
|
| 99 | ||
| 100 | return graph_model |
|
| 101 | ||
| 102 | ||
| 103 | def create_server(port: int, root_directory: str, open_browser: bool = True) -> threading.Thread: |
|
| @@ 47-86 (lines=40) @@ | ||
| 44 | yield from _ast_edge(child_ast) |
|
| 45 | ||
| 46 | ||
| 47 | @pytest.fixture |
|
| 48 | def ast_graph_model(): |
|
| 49 | graph_model = model(name='AST Graph') |
|
| 50 | ||
| 51 | root_ast_node = ast.parse(inspect.getsource(D3Builder)) |
|
| 52 | ||
| 53 | def node_type(ast_node): |
|
| 54 | return ast_node.__class__.__name__ |
|
| 55 | ||
| 56 | def node_label(ast_node) -> str: |
|
| 57 | label = ast_node.__class__.__name__ |
|
| 58 | ||
| 59 | for field_name in ('name', 'id'): |
|
| 60 | if field_name in ast_node._fields: |
|
| 61 | label = f"{label}\n{field_name}: {operator.attrgetter(field_name)(ast_node)}" |
|
| 62 | ||
| 63 | return label |
|
| 64 | ||
| 65 | def key(value): |
|
| 66 | # noinspection InsecureHash |
|
| 67 | return hashlib.shake_128(pickle.dumps(value)).hexdigest(20) |
|
| 68 | ||
| 69 | def endpoint(value, endpoint_name): |
|
| 70 | return key(value[endpoint_name]) |
|
| 71 | ||
| 72 | def source(value): |
|
| 73 | return endpoint(value, 'source') |
|
| 74 | ||
| 75 | def target(value): |
|
| 76 | return endpoint(value, 'target') |
|
| 77 | ||
| 78 | @graph_model.node(type_=node_type, key=key, label=node_label, unique=True) |
|
| 79 | def ast_node(**kwargs): |
|
| 80 | yield from _ast_nodes([root_ast_node]) |
|
| 81 | ||
| 82 | @graph_model.edge(type_='edge', source=source, target=target, label=operator.itemgetter('type')) |
|
| 83 | def ast_edge(**kwargs): |
|
| 84 | yield from _ast_edge(root_ast_node) |
|
| 85 | ||
| 86 | return graph_model |
|
| 87 | ||
| 88 | ||
| 89 | @pytest.fixture |
|