synaptic-graph 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- synaptic_graph-0.1.0/.gitignore +12 -0
- synaptic_graph-0.1.0/PKG-INFO +143 -0
- synaptic_graph-0.1.0/README.md +114 -0
- synaptic_graph-0.1.0/pyproject.toml +51 -0
- synaptic_graph-0.1.0/synaptic/__init__.py +1 -0
- synaptic_graph-0.1.0/synaptic/cli.py +214 -0
- synaptic_graph-0.1.0/synaptic/cloud_detector.py +47 -0
- synaptic_graph-0.1.0/synaptic/graph.py +166 -0
- synaptic_graph-0.1.0/synaptic/http_detector.py +46 -0
- synaptic_graph-0.1.0/synaptic/parser.py +60 -0
- synaptic_graph-0.1.0/synaptic/scanner.py +39 -0
- synaptic_graph-0.1.0/synaptic/tui.py +562 -0
- synaptic_graph-0.1.0/synaptic/utils.py +41 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: synaptic-graph
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Visualize the dependency graph of any Python project — internal imports, cloud SDKs and HTTP calls.
|
|
5
|
+
Project-URL: Homepage, https://github.com/darkvius/synaptic
|
|
6
|
+
Project-URL: Repository, https://github.com/darkvius/synaptic
|
|
7
|
+
Project-URL: Issues, https://github.com/darkvius/synaptic/issues
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: architecture,ast,dependencies,graph,visualization
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: graphviz>=0.20
|
|
22
|
+
Requires-Dist: networkx>=3.2
|
|
23
|
+
Requires-Dist: numpy>=1.24
|
|
24
|
+
Requires-Dist: pyvis>=0.3
|
|
25
|
+
Requires-Dist: rich>=13
|
|
26
|
+
Requires-Dist: textual>=0.80
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
<div align="center">
|
|
31
|
+
|
|
32
|
+
<img src="assets/demo.svg" alt="synaptic demo" width="100%"/>
|
|
33
|
+
|
|
34
|
+
# synaptic
|
|
35
|
+
|
|
36
|
+
**Visualize the dependency graph of any Python project.**
|
|
37
|
+
Internal imports · Cloud SDKs · HTTP clients · Circular deps — all in one command.
|
|
38
|
+
|
|
39
|
+
[](https://python.org)
|
|
40
|
+
[](LICENSE)
|
|
41
|
+
[](https://typer.tiangolo.com)
|
|
42
|
+
[](https://rich.readthedocs.io)
|
|
43
|
+
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **Static AST analysis** — no runtime execution needed
|
|
51
|
+
- **Internal imports** — maps every `import` and `from X import Y` across your codebase
|
|
52
|
+
- **Cloud SDK detection** — identifies AWS (`boto3`), GCP (`google.cloud`, `firebase_admin`) and Azure (`azure.*`) usage
|
|
53
|
+
- **HTTP client detection** — flags modules using `requests`, `httpx`, `aiohttp`, `urllib3` and more
|
|
54
|
+
- **Circular dependency highlighting** — broken cycles rendered in red
|
|
55
|
+
- **Two output formats** — interactive HTML (`pyvis`) or static SVG/PNG (`graphviz`)
|
|
56
|
+
- **Rich terminal output** — live progress, color-coded summary
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install synaptic
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Or from source:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/your-username/synaptic
|
|
70
|
+
cd synaptic
|
|
71
|
+
pip install -e .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> **Requirements:** Python 3.10+, `graphviz` binary installed on your system (`apt install graphviz` / `brew install graphviz`).
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Quick start
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Interactive HTML graph (default)
|
|
82
|
+
synaptic scan ./my-project
|
|
83
|
+
|
|
84
|
+
# Custom output path
|
|
85
|
+
synaptic scan ./my-project --output architecture.html
|
|
86
|
+
|
|
87
|
+
# SVG with circular dependency highlighting
|
|
88
|
+
synaptic scan ./my-project --output graph.svg --circular
|
|
89
|
+
|
|
90
|
+
# Skip cloud and HTTP detection, filter stdlib
|
|
91
|
+
synaptic scan ./my-project --no-cloud --no-http --filter-stdlib
|
|
92
|
+
|
|
93
|
+
# Include test files in the scan
|
|
94
|
+
synaptic scan ./my-project --tests
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Options
|
|
100
|
+
|
|
101
|
+
| Flag | Default | Description |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `--output`, `-o` | `synaptic_graph.html` | Output file (`.html` or `.svg`) |
|
|
104
|
+
| `--cloud / --no-cloud` | `on` | Detect AWS / GCP / Azure SDKs |
|
|
105
|
+
| `--http / --no-http` | `on` | Detect HTTP client libraries |
|
|
106
|
+
| `--tests / --no-tests` | `off` | Include test files |
|
|
107
|
+
| `--filter-stdlib / --no-filter-stdlib` | `on` | Exclude Python stdlib from graph |
|
|
108
|
+
| `--filter-external / --no-filter-external` | `off` | Exclude third-party packages |
|
|
109
|
+
| `--circular`, `-c` | `off` | Highlight circular dependencies in red |
|
|
110
|
+
| `--version`, `-v` | — | Show version and exit |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Architecture
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
synaptic/
|
|
118
|
+
├── cli.py # Typer CLI + Rich output
|
|
119
|
+
├── scanner.py # Recursive .py file discovery
|
|
120
|
+
├── parser.py # AST-based import analysis
|
|
121
|
+
├── cloud_detector.py # AWS / GCP / Azure SDK detection
|
|
122
|
+
├── http_detector.py # HTTP client library detection
|
|
123
|
+
├── graph.py # networkx graph + graphviz / pyvis rendering
|
|
124
|
+
└── utils.py # Shared helpers
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Node types
|
|
130
|
+
|
|
131
|
+
| Color | Meaning |
|
|
132
|
+
|---|---|
|
|
133
|
+
| 🔵 Blue | Internal project module |
|
|
134
|
+
| 🟠 Orange | AWS / GCP / Azure SDK |
|
|
135
|
+
| 🩷 Pink | HTTP client (requests, httpx…) |
|
|
136
|
+
| ⚫ Grey | Stdlib / external package |
|
|
137
|
+
| 🔴 Red edge | Circular dependency |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT © 2024
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/demo.svg" alt="synaptic demo" width="100%"/>
|
|
4
|
+
|
|
5
|
+
# synaptic
|
|
6
|
+
|
|
7
|
+
**Visualize the dependency graph of any Python project.**
|
|
8
|
+
Internal imports · Cloud SDKs · HTTP clients · Circular deps — all in one command.
|
|
9
|
+
|
|
10
|
+
[](https://python.org)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://typer.tiangolo.com)
|
|
13
|
+
[](https://rich.readthedocs.io)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Static AST analysis** — no runtime execution needed
|
|
22
|
+
- **Internal imports** — maps every `import` and `from X import Y` across your codebase
|
|
23
|
+
- **Cloud SDK detection** — identifies AWS (`boto3`), GCP (`google.cloud`, `firebase_admin`) and Azure (`azure.*`) usage
|
|
24
|
+
- **HTTP client detection** — flags modules using `requests`, `httpx`, `aiohttp`, `urllib3` and more
|
|
25
|
+
- **Circular dependency highlighting** — broken cycles rendered in red
|
|
26
|
+
- **Two output formats** — interactive HTML (`pyvis`) or static SVG/PNG (`graphviz`)
|
|
27
|
+
- **Rich terminal output** — live progress, color-coded summary
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install synaptic
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or from source:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/your-username/synaptic
|
|
41
|
+
cd synaptic
|
|
42
|
+
pip install -e .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> **Requirements:** Python 3.10+, `graphviz` binary installed on your system (`apt install graphviz` / `brew install graphviz`).
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Interactive HTML graph (default)
|
|
53
|
+
synaptic scan ./my-project
|
|
54
|
+
|
|
55
|
+
# Custom output path
|
|
56
|
+
synaptic scan ./my-project --output architecture.html
|
|
57
|
+
|
|
58
|
+
# SVG with circular dependency highlighting
|
|
59
|
+
synaptic scan ./my-project --output graph.svg --circular
|
|
60
|
+
|
|
61
|
+
# Skip cloud and HTTP detection, filter stdlib
|
|
62
|
+
synaptic scan ./my-project --no-cloud --no-http --filter-stdlib
|
|
63
|
+
|
|
64
|
+
# Include test files in the scan
|
|
65
|
+
synaptic scan ./my-project --tests
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Options
|
|
71
|
+
|
|
72
|
+
| Flag | Default | Description |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `--output`, `-o` | `synaptic_graph.html` | Output file (`.html` or `.svg`) |
|
|
75
|
+
| `--cloud / --no-cloud` | `on` | Detect AWS / GCP / Azure SDKs |
|
|
76
|
+
| `--http / --no-http` | `on` | Detect HTTP client libraries |
|
|
77
|
+
| `--tests / --no-tests` | `off` | Include test files |
|
|
78
|
+
| `--filter-stdlib / --no-filter-stdlib` | `on` | Exclude Python stdlib from graph |
|
|
79
|
+
| `--filter-external / --no-filter-external` | `off` | Exclude third-party packages |
|
|
80
|
+
| `--circular`, `-c` | `off` | Highlight circular dependencies in red |
|
|
81
|
+
| `--version`, `-v` | — | Show version and exit |
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Architecture
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
synaptic/
|
|
89
|
+
├── cli.py # Typer CLI + Rich output
|
|
90
|
+
├── scanner.py # Recursive .py file discovery
|
|
91
|
+
├── parser.py # AST-based import analysis
|
|
92
|
+
├── cloud_detector.py # AWS / GCP / Azure SDK detection
|
|
93
|
+
├── http_detector.py # HTTP client library detection
|
|
94
|
+
├── graph.py # networkx graph + graphviz / pyvis rendering
|
|
95
|
+
└── utils.py # Shared helpers
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Node types
|
|
101
|
+
|
|
102
|
+
| Color | Meaning |
|
|
103
|
+
|---|---|
|
|
104
|
+
| 🔵 Blue | Internal project module |
|
|
105
|
+
| 🟠 Orange | AWS / GCP / Azure SDK |
|
|
106
|
+
| 🩷 Pink | HTTP client (requests, httpx…) |
|
|
107
|
+
| ⚫ Grey | Stdlib / external package |
|
|
108
|
+
| 🔴 Red edge | Circular dependency |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT © 2024
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "synaptic-graph"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Visualize the dependency graph of any Python project — internal imports, cloud SDKs and HTTP calls."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["dependencies", "graph", "architecture", "visualization", "ast"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"typer>=0.12",
|
|
27
|
+
"rich>=13",
|
|
28
|
+
"graphviz>=0.20",
|
|
29
|
+
"pyvis>=0.3",
|
|
30
|
+
"networkx>=3.2",
|
|
31
|
+
"numpy>=1.24",
|
|
32
|
+
"textual>=0.80",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/darkvius/synaptic"
|
|
37
|
+
Repository = "https://github.com/darkvius/synaptic"
|
|
38
|
+
Issues = "https://github.com/darkvius/synaptic/issues"
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
synaptic = "synaptic.cli:app"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["synaptic"]
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.sdist]
|
|
47
|
+
include = [
|
|
48
|
+
"synaptic/",
|
|
49
|
+
"README.md",
|
|
50
|
+
"pyproject.toml",
|
|
51
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cli.py — Synaptic CLI built with Typer + Rich.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.panel import Panel
|
|
14
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
15
|
+
from rich import print as rprint
|
|
16
|
+
|
|
17
|
+
from synaptic import __version__
|
|
18
|
+
|
|
19
|
+
app = typer.Typer(
|
|
20
|
+
name="synaptic",
|
|
21
|
+
help="[bold cyan]synaptic[/] — visualize the dependency graph of any Python project.",
|
|
22
|
+
rich_markup_mode="rich",
|
|
23
|
+
add_completion=True,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
console = Console()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _version_callback(value: bool) -> None:
|
|
30
|
+
if value:
|
|
31
|
+
rprint(f"[bold cyan]synaptic[/] version [bold]{__version__}[/]")
|
|
32
|
+
raise typer.Exit()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.callback()
|
|
36
|
+
def main(
|
|
37
|
+
version: Optional[bool] = typer.Option(
|
|
38
|
+
None, "--version", "-v",
|
|
39
|
+
callback=_version_callback,
|
|
40
|
+
is_eager=True,
|
|
41
|
+
help="Show version and exit.",
|
|
42
|
+
),
|
|
43
|
+
) -> None:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.command()
|
|
48
|
+
def scan(
|
|
49
|
+
project: Path = typer.Argument(
|
|
50
|
+
...,
|
|
51
|
+
help="Root path of the Python project to analyse.",
|
|
52
|
+
exists=True,
|
|
53
|
+
file_okay=False,
|
|
54
|
+
dir_okay=True,
|
|
55
|
+
resolve_path=True,
|
|
56
|
+
),
|
|
57
|
+
output: Path = typer.Option(
|
|
58
|
+
Path("synaptic_graph.html"),
|
|
59
|
+
"--output", "-o",
|
|
60
|
+
help="Output file path. Extension determines format: .html (interactive) or .svg.",
|
|
61
|
+
),
|
|
62
|
+
cloud: bool = typer.Option(True, "--cloud/--no-cloud", help="Detect AWS / GCP / Azure SDK usage."),
|
|
63
|
+
http: bool = typer.Option(True, "--http/--no-http", help="Detect HTTP client library usage."),
|
|
64
|
+
tests: bool = typer.Option(False, "--tests/--no-tests", help="Include test files in the scan."),
|
|
65
|
+
filter_stdlib: bool = typer.Option(True, "--filter-stdlib/--no-filter-stdlib", help="Exclude Python stdlib modules from the graph."),
|
|
66
|
+
filter_external: bool = typer.Option(False, "--filter-external/--no-filter-external", help="Exclude third-party (non-project) modules from the graph."),
|
|
67
|
+
circular: bool = typer.Option(False, "--circular", "-c", help="Highlight circular dependencies in red."),
|
|
68
|
+
) -> None:
|
|
69
|
+
"""Scan *PROJECT* and generate a dependency graph."""
|
|
70
|
+
|
|
71
|
+
console.print(
|
|
72
|
+
Panel.fit(
|
|
73
|
+
f"[bold cyan]synaptic[/] [dim]v{__version__}[/]\n"
|
|
74
|
+
f"[dim]Scanning:[/] [bold]{project}[/]",
|
|
75
|
+
border_style="cyan",
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress:
|
|
80
|
+
|
|
81
|
+
# 1. Scan files
|
|
82
|
+
task = progress.add_task("Scanning .py files...", total=None)
|
|
83
|
+
from synaptic.scanner import scan as do_scan
|
|
84
|
+
files = do_scan(project, include_tests=tests)
|
|
85
|
+
progress.update(task, description=f"[green]Found {len(files)} Python files[/]", completed=True)
|
|
86
|
+
|
|
87
|
+
if not files:
|
|
88
|
+
console.print("[yellow]No Python files found. Is the path correct?[/]")
|
|
89
|
+
raise typer.Exit(1)
|
|
90
|
+
|
|
91
|
+
# 2. Parse imports
|
|
92
|
+
task = progress.add_task("Parsing imports (AST)...", total=None)
|
|
93
|
+
from synaptic.parser import parse_project
|
|
94
|
+
edges = parse_project(files, project)
|
|
95
|
+
progress.update(task, description=f"[green]Parsed {len(edges)} import edges[/]", completed=True)
|
|
96
|
+
|
|
97
|
+
# 3. Cloud detection
|
|
98
|
+
cloud_deps = []
|
|
99
|
+
if cloud:
|
|
100
|
+
task = progress.add_task("Detecting cloud SDKs...", total=None)
|
|
101
|
+
from synaptic.cloud_detector import detect as detect_cloud
|
|
102
|
+
cloud_deps = detect_cloud(edges)
|
|
103
|
+
progress.update(task, description=f"[green]Found {len(cloud_deps)} cloud dependencies[/]", completed=True)
|
|
104
|
+
|
|
105
|
+
# 4. HTTP detection
|
|
106
|
+
http_deps = []
|
|
107
|
+
if http:
|
|
108
|
+
task = progress.add_task("Detecting HTTP clients...", total=None)
|
|
109
|
+
from synaptic.http_detector import detect as detect_http
|
|
110
|
+
http_deps = detect_http(edges)
|
|
111
|
+
progress.update(task, description=f"[green]Found {len(http_deps)} HTTP dependencies[/]", completed=True)
|
|
112
|
+
|
|
113
|
+
# 5. Build graph
|
|
114
|
+
task = progress.add_task("Building graph...", total=None)
|
|
115
|
+
from synaptic.utils import get_stdlib_modules, resolve_internal_modules, choose_output_format
|
|
116
|
+
from synaptic.graph import build, render_html, render_svg
|
|
117
|
+
|
|
118
|
+
internal_modules = resolve_internal_modules(files, project)
|
|
119
|
+
stdlib_modules = get_stdlib_modules()
|
|
120
|
+
|
|
121
|
+
G = build(
|
|
122
|
+
edges=edges,
|
|
123
|
+
cloud_deps=cloud_deps,
|
|
124
|
+
http_deps=http_deps,
|
|
125
|
+
internal_modules=internal_modules,
|
|
126
|
+
stdlib_modules=stdlib_modules,
|
|
127
|
+
filter_stdlib=filter_stdlib,
|
|
128
|
+
filter_external=filter_external,
|
|
129
|
+
highlight_circular=circular,
|
|
130
|
+
)
|
|
131
|
+
progress.update(task, description=f"[green]Graph: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges[/]", completed=True)
|
|
132
|
+
|
|
133
|
+
# 6. Render
|
|
134
|
+
fmt = choose_output_format(output)
|
|
135
|
+
task = progress.add_task(f"Rendering {fmt.upper()}...", total=None)
|
|
136
|
+
if fmt == "html":
|
|
137
|
+
render_html(G, output)
|
|
138
|
+
else:
|
|
139
|
+
render_svg(G, output)
|
|
140
|
+
progress.update(task, description=f"[green]Saved to {output}[/]", completed=True)
|
|
141
|
+
|
|
142
|
+
# Summary
|
|
143
|
+
console.print()
|
|
144
|
+
console.print(
|
|
145
|
+
Panel(
|
|
146
|
+
f"[bold green]Done![/]\n\n"
|
|
147
|
+
f" [dim]Files scanned:[/] [bold]{len(files)}[/]\n"
|
|
148
|
+
f" [dim]Import edges:[/] [bold]{len(edges)}[/]\n"
|
|
149
|
+
f" [dim]Cloud deps:[/] [bold]{len(cloud_deps)}[/]\n"
|
|
150
|
+
f" [dim]HTTP deps:[/] [bold]{len(http_deps)}[/]\n"
|
|
151
|
+
f" [dim]Graph nodes:[/] [bold]{G.number_of_nodes()}[/]\n"
|
|
152
|
+
f" [dim]Graph edges:[/] [bold]{G.number_of_edges()}[/]\n\n"
|
|
153
|
+
f" [dim]Output:[/] [bold cyan]{output.resolve()}[/]",
|
|
154
|
+
title="[bold cyan]synaptic[/]",
|
|
155
|
+
border_style="green",
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@app.command()
|
|
161
|
+
def tui(
|
|
162
|
+
project: Path = typer.Argument(
|
|
163
|
+
...,
|
|
164
|
+
help="Root path of the Python project to analyse.",
|
|
165
|
+
exists=True,
|
|
166
|
+
file_okay=False,
|
|
167
|
+
dir_okay=True,
|
|
168
|
+
resolve_path=True,
|
|
169
|
+
),
|
|
170
|
+
cloud: bool = typer.Option(True, "--cloud/--no-cloud", help="Detect AWS / GCP / Azure SDK usage."),
|
|
171
|
+
http: bool = typer.Option(True, "--http/--no-http", help="Detect HTTP client library usage."),
|
|
172
|
+
tests: bool = typer.Option(False, "--tests/--no-tests", help="Include test files in the scan."),
|
|
173
|
+
filter_stdlib: bool = typer.Option(True, "--filter-stdlib/--no-filter-stdlib", help="Exclude stdlib modules from the graph."),
|
|
174
|
+
filter_external: bool = typer.Option(False, "--filter-external/--no-filter-external", help="Exclude third-party modules."),
|
|
175
|
+
circular: bool = typer.Option(True, "--circular/--no-circular", help="Highlight circular dependencies."),
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Launch the interactive TUI graph explorer for *PROJECT*."""
|
|
178
|
+
from synaptic.scanner import scan as do_scan
|
|
179
|
+
from synaptic.parser import parse_project
|
|
180
|
+
from synaptic.cloud_detector import detect as detect_cloud
|
|
181
|
+
from synaptic.http_detector import detect as detect_http
|
|
182
|
+
from synaptic.graph import build
|
|
183
|
+
from synaptic.utils import get_stdlib_modules, resolve_internal_modules
|
|
184
|
+
from synaptic.tui import launch
|
|
185
|
+
|
|
186
|
+
with console.status("[cyan]Building graph…[/]"):
|
|
187
|
+
files = do_scan(project, include_tests=tests)
|
|
188
|
+
if not files:
|
|
189
|
+
console.print("[yellow]No Python files found.[/]")
|
|
190
|
+
raise typer.Exit(1)
|
|
191
|
+
|
|
192
|
+
edges = parse_project(files, project)
|
|
193
|
+
cloud_deps = detect_cloud(edges) if cloud else []
|
|
194
|
+
http_deps = detect_http(edges) if http else []
|
|
195
|
+
|
|
196
|
+
internal_modules = resolve_internal_modules(files, project)
|
|
197
|
+
stdlib_modules = get_stdlib_modules()
|
|
198
|
+
|
|
199
|
+
G = build(
|
|
200
|
+
edges=edges,
|
|
201
|
+
cloud_deps=cloud_deps,
|
|
202
|
+
http_deps=http_deps,
|
|
203
|
+
internal_modules=internal_modules,
|
|
204
|
+
stdlib_modules=stdlib_modules,
|
|
205
|
+
filter_stdlib=filter_stdlib,
|
|
206
|
+
filter_external=filter_external,
|
|
207
|
+
highlight_circular=circular,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
launch(G, project)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
if __name__ == "__main__":
|
|
214
|
+
app()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cloud_detector.py — Identify cloud SDK usage from import edges.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from synaptic.parser import ImportEdge
|
|
7
|
+
|
|
8
|
+
# Map root package prefixes → (provider, service label)
|
|
9
|
+
CLOUD_SIGNATURES: dict[str, tuple[str, str]] = {
|
|
10
|
+
"boto3": ("AWS", "boto3"),
|
|
11
|
+
"botocore": ("AWS", "botocore"),
|
|
12
|
+
"aiobotocore": ("AWS", "aiobotocore"),
|
|
13
|
+
"google.cloud": ("GCP", "google-cloud"),
|
|
14
|
+
"google.api_core": ("GCP", "google-api-core"),
|
|
15
|
+
"firebase_admin": ("GCP", "firebase-admin"),
|
|
16
|
+
"googleapiclient": ("GCP", "google-api-python-client"),
|
|
17
|
+
"azure": ("Azure", "azure-sdk"),
|
|
18
|
+
"msrest": ("Azure", "msrest"),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class CloudDependency:
|
|
24
|
+
source: str # module that imports the SDK
|
|
25
|
+
provider: str # AWS | GCP | Azure
|
|
26
|
+
sdk: str # human-readable SDK name
|
|
27
|
+
raw_import: str # original import target string
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def detect(edges: list[ImportEdge]) -> list[CloudDependency]:
|
|
31
|
+
"""Return CloudDependency entries found in the import edge list."""
|
|
32
|
+
found: list[CloudDependency] = []
|
|
33
|
+
|
|
34
|
+
for edge in edges:
|
|
35
|
+
for prefix, (provider, sdk) in CLOUD_SIGNATURES.items():
|
|
36
|
+
if edge.target == prefix or edge.target.startswith(prefix + "."):
|
|
37
|
+
found.append(
|
|
38
|
+
CloudDependency(
|
|
39
|
+
source=edge.source,
|
|
40
|
+
provider=provider,
|
|
41
|
+
sdk=sdk,
|
|
42
|
+
raw_import=edge.target,
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
break # first match wins
|
|
46
|
+
|
|
47
|
+
return found
|