Skip to content

Commit 1e541dc

Browse files
authored
Merge pull request #15 from chriscarrollsmith/main
Fixed broken build, made file paths OS-agnostic
2 parents 22b9a14 + 3f285c7 commit 1e541dc

File tree

11 files changed

+802
-718
lines changed

11 files changed

+802
-718
lines changed

.github/workflows/main.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ jobs:
1010
flake8_py3:
1111
runs-on: ubuntu-latest
1212
steps:
13-
- uses: actions/checkout@v2
14-
- name: Set up Python 3.8
15-
uses: actions/setup-python@v2
13+
- uses: actions/checkout@v4
14+
- name: Set up Python 3.12
15+
uses: actions/setup-python@v5
1616
with:
17-
python-version: 3.8
17+
python-version: 3.12
1818
- name: Install dependencies
1919
run: |
2020
python -m pip install --upgrade pip
@@ -32,11 +32,11 @@ jobs:
3232
needs: [flake8_py3]
3333
strategy:
3434
matrix:
35-
python: [3.8, 3.9, '3.10']
35+
python: [3.12]
3636
steps:
37-
- uses: actions/checkout@v2
37+
- uses: actions/checkout@v4
3838
- name: Set up Python
39-
uses: actions/setup-python@v2
39+
uses: actions/setup-python@v5
4040
with:
4141
python-version: ${{ matrix.python }}
4242
- name: Install dependencies

.isort.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[settings]
2-
known_third_party = clifier,matplotlib,networkx,pytest
2+
known_third_party = click,matplotlib,networkx,pytest

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ repos:
44
hooks:
55
- id: seed-isort-config
66
- repo: https://github.com/pycqa/isort
7-
rev: 5.4.2
7+
rev: 5.13.2
88
hooks:
99
- id: isort
1010
- repo: https://github.com/ambv/black
11-
rev: stable
11+
rev: 24.10.0
1212
hooks:
1313
- id: black
14-
language_version: python3.8
14+
language_version: python3.12
1515
- repo: https://github.com/PyCQA/flake8
16-
rev: 3.8.3
16+
rev: 7.1.1
1717
hooks:
1818
- id: flake8

codegraph/conf/cli.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

codegraph/core.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
from argparse import Namespace
3-
from collections import defaultdict
4-
from typing import Dict, List, Text, Tuple
3+
from collections import defaultdict, deque
4+
from typing import Dict, List, Set, Text, Tuple
55

66
from codegraph.parser import Import, create_objects_array
77
from codegraph.utils import get_python_paths_list
@@ -87,6 +87,42 @@ def usage_graph(self) -> Dict:
8787
dependencies = populate_free_nodes(self.modules_data, dependencies)
8888
return dependencies
8989

90+
def get_dependencies(self, file_path: str, distance: int) -> Dict[str, Set[str]]:
91+
"""
92+
Get dependencies that are 'distance' nodes away from the given file.
93+
94+
:param file_path: Path of the file to start from
95+
:param distance: Number of edges to traverse
96+
:return: Dictionary with distances as keys and sets of dependent files as values
97+
"""
98+
dependencies = {i: set() for i in range(1, distance + 1)}
99+
graph = self.usage_graph()
100+
101+
if file_path not in graph:
102+
return dependencies
103+
104+
queue = deque([(file_path, 0)])
105+
visited = set()
106+
107+
while queue:
108+
current_file, current_distance = queue.popleft()
109+
110+
if current_distance >= distance:
111+
continue
112+
113+
if current_file not in visited:
114+
visited.add(current_file)
115+
116+
for entity, used_entities in graph[current_file].items():
117+
for used_entity in used_entities:
118+
if "." in used_entity:
119+
dependent_file = used_entity.split(".")[0] + ".py"
120+
if dependent_file != current_file:
121+
dependencies[current_distance + 1].add(dependent_file)
122+
queue.append((dependent_file, current_distance + 1))
123+
124+
return dependencies
125+
90126

91127
def get_module_name(code_path: Text) -> Text:
92128
module_name = os.path.basename(code_path).replace(".py", "")

codegraph/main.py

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,65 @@
1-
""" main module of testsdiffer for console (cli) usage"""
2-
import os
1+
""" main module of testsdiffer for console (cli) usage"""
2+
33
import pprint
4+
import sys
45

5-
import clifier
6+
import click
67

78
from codegraph import __version__, core
89

9-
CLI_CFG_NAME = "conf/cli.yml"
10+
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
11+
12+
13+
@click.command(context_settings=CONTEXT_SETTINGS)
14+
@click.version_option(version=__version__, message="CodeGraph version %(version)s")
15+
@click.argument("paths", nargs=-1, type=click.Path(exists=True))
16+
@click.option(
17+
"-o",
18+
"--object-only",
19+
is_flag=True,
20+
help="Don't visualize code dependencies as graph",
21+
)
22+
@click.option("--file-path", help="File path to start dependency search from")
23+
@click.option("--distance", type=int, help="Distance to search for dependencies")
24+
def cli(paths, object_only, file_path, distance):
25+
"""
26+
Tool that creates a graph of code to show dependencies between code entities (methods, classes, etc.).
27+
CodeGraph does not execute code, it is based only on lex and syntax parsing.
1028
29+
PATHS: Provide path(s) to code base
30+
"""
31+
if not paths:
32+
click.echo(
33+
"Error: No paths provided. Please specify at least one path to the code base.",
34+
err=True,
35+
)
36+
sys.exit(1)
1137

12-
def cli():
13-
config_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), CLI_CFG_NAME)
14-
_cli = clifier.Clifier(config_path, prog_version=__version__)
15-
parser = _cli.create_parser()
16-
args = parser.parse_args()
38+
args = {
39+
"paths": paths,
40+
"object_only": object_only,
41+
"file_path": file_path,
42+
"distance": distance,
43+
}
1744
main(args)
1845

1946

2047
def main(args):
21-
usage_graph = core.CodeGraph(args).usage_graph()
22-
pprint.pprint(usage_graph)
23-
if not args.object_only:
24-
# to make more quick work if not needed to visualize
25-
import codegraph.vizualyzer as vz
48+
code_graph = core.CodeGraph(args)
49+
usage_graph = code_graph.usage_graph()
50+
51+
if args.get("file_path") and args.get("distance"):
52+
dependencies = code_graph.get_dependencies(args["file_path"], args["distance"])
53+
print(f"Dependencies for {args['file_path']}:")
54+
for distance, files in dependencies.items():
55+
print(f" Distance {distance}: {', '.join(files)}")
56+
else:
57+
pprint.pprint(usage_graph)
58+
if not args["object_only"]:
59+
import codegraph.vizualyzer as vz
60+
61+
vz.draw_graph(usage_graph)
62+
2663

27-
vz.draw_graph(usage_graph)
64+
if __name__ == "__main__":
65+
cli()

codegraph/utils.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import List, Union
44

55

6-
def get_python_paths_list(paths: Union[str, List]) -> List:
6+
def get_python_paths_list(paths: Union[str, List]) -> List[str]:
77
"""
88
return list of paths to python files, that found in provided path
99
:param paths: paths to folder or python file that need to tests
@@ -22,11 +22,10 @@ def get_python_paths_list(paths: Union[str, List]) -> List:
2222
for path in paths:
2323
path = Path(path).absolute()
2424
if not path.exists():
25-
raise ValueError(f"Path {path.as_posix()} does not exists")
26-
path = path.as_posix()
25+
raise ValueError(f"Path {path.as_posix()} does not exist")
2726
paths_list += [
28-
path
29-
for path in glob.glob(path + "/*", recursive=True)
30-
if path.endswith(".py") and not path.endswith("__init__.py")
27+
Path(p).as_posix()
28+
for p in glob.glob(str(path / "**" / "*.py"), recursive=True)
29+
if not p.endswith("__init__.py")
3130
]
3231
return paths_list

codegraph/vizualyzer.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,17 @@ def draw_graph(modules_entities: Dict) -> None:
7575
font_family="Arial",
7676
font_size=10,
7777
)
78+
79+
arrow_size = 15
80+
7881
nx.draw_networkx_edges(
7982
G,
8083
pos,
8184
edgelist=module_edges_all,
8285
edge_color="#009c2c",
8386
width=2,
84-
arrows=False,
87+
arrows=True,
88+
arrowsize=arrow_size,
8589
style="dashed",
8690
node_size=50,
8791
)
@@ -91,9 +95,10 @@ def draw_graph(modules_entities: Dict) -> None:
9195
edgelist=sub_edges_all,
9296
edge_color="r",
9397
width=2,
94-
arrows=False,
98+
arrows=True,
99+
arrowsize=arrow_size,
95100
style="dashed",
96101
)
97-
for p in pos: # raise text positions
102+
for p in pos:
98103
pos[p][1] += 0.07
99104
plt.show()

0 commit comments

Comments
 (0)