-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.py
More file actions
133 lines (111 loc) · 3.69 KB
/
Copy pathmain.py
File metadata and controls
133 lines (111 loc) · 3.69 KB
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# ruff: noqa: T201 TC003
from __future__ import annotations
import json
import subprocess
from collections import defaultdict
from pathlib import Path
import tree_sitter_python as tspython
import typer
from tree_sitter import Language, Parser
import python_import
from python_import.utils import get_all_imports_in_file_as_absolute
PY_LANGUAGE = Language(tspython.language())
app = typer.Typer(
no_args_is_help=True, context_settings={"help_option_names": ["-h", "--help"]}
)
def version_callback(*, value: bool):
if value:
print(f"python-import v{python_import.__version__}")
raise typer.Exit
@app.callback()
def common(
ctx: typer.Context,
*,
version: bool = typer.Option(
None, "-v", "--version", callback=version_callback, help="Show version"
),
):
pass
@app.command()
def count(
project_root: Path,
module_name: str,
) -> None:
"""
Count python imports in a project and print them in descending order of count.
For example,
00002:from my_module import logging
00001:import logging
Todo:
- [ ] Test import abcd
- [ ] Test import abcd as efg
- [ ] Test import a.b.c
- [ ] Test import a, b, c as d, e
- [ ] Test import a, b, c, d
- [ ] Test from a import b
- [ ] Test from a import b as c
- [ ] Test from a import b, c
- [ ] Test from a import b, c as d, e
- [ ] Test from a.keyword.c import d (should not be counted. keyword has to be import or as, not in from)
- [ ] Test imports within a function
- [ ] Test relative imports
"""
parser = Parser(PY_LANGUAGE)
# NOTE: rg json outputs are (1, 0)-indexed
rg_outputs = subprocess.run(
[
"rg",
"--word-regexp",
"--fixed-strings",
"--json",
"--type",
"python",
module_name,
],
cwd=project_root,
capture_output=True,
check=False,
)
# print(rg_outputs)
# 0-indexed row, col
file_path_to_rowcol: dict[str, list[tuple[int, int]]] = defaultdict(list)
for line in rg_outputs.stdout.decode("utf-8").split("\n"):
if not line:
continue
# print(line)
rg_output = json.loads(line)
# print(rg_output["type"])
if rg_output["type"] == "match":
file_path = str(
(project_root / rg_output["data"]["path"]["text"]).resolve()
)
row = rg_output["data"]["line_number"] - 1
col = rg_output["data"]["submatches"][0]["start"]
# col_end = rg_output["data"]["submatches"][0]["end"]
file_path_to_rowcol[file_path].append((row, col))
# print(file_path_to_rowcol)
import_statement_to_count: dict[str, int] = defaultdict(int)
for python_file_path, rowcols in file_path_to_rowcol.items():
import_statement_to_count_file = get_all_imports_in_file_as_absolute(
project_root=project_root,
python_file_path=python_file_path,
parser=parser,
rowcols=rowcols,
)
# merge counts
for import_statement, count in import_statement_to_count_file.items():
import_statement_to_count[import_statement] += count
# sort and print as json-line
for import_statement, count in sorted(
import_statement_to_count.items(), key=lambda x: x[1], reverse=True
):
print(f"{count:05d}:{import_statement}")
# print(
# json.dumps(
# {"import_statement": import_statement, "count": count},
# indent=None,
# separators=(",", ":"),
# )
# )
if __name__ == "__main__":
app()