Skip to content

Commit 9b51064

Browse files
authored
deglob.py -> add ability to write deglobbed change back to target file
1 parent 297ca9a commit 9b51064

File tree

1 file changed

+62
-15
lines changed

1 file changed

+62
-15
lines changed

tools/deglob.py

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
an import statement listing only those names. This practice can help
1717
prevent polluting the global namespace and improve clarity.
1818
19-
Example:
20-
deglob.py my_build123d_script.py
19+
Examples:
20+
python deglob.py my_build123d_script.py
21+
python deglob.py -h
2122
2223
After parsing my_build123d_script.py, the script prints a line such as:
2324
from build123d import Workplane, Solid
@@ -26,6 +27,7 @@
2627
2728
Module Contents:
2829
- parse_args(): Parse the command-line argument for the input file path.
30+
- count_glob_imports(): Count the number of occurences of a glob import.
2931
- find_used_symbols(): Parse Python source code to find referenced names.
3032
- main(): Orchestrates reading the file, analyzing symbols, and printing
3133
the replacement import line.
@@ -53,6 +55,7 @@
5355
import ast
5456
import sys
5557
from pathlib import Path
58+
import re
5659

5760
import build123d
5861

@@ -63,43 +66,61 @@ def parse_args():
6366
6467
Returns:
6568
argparse.Namespace: An object containing the parsed command-line arguments:
66-
- build123d_file (Path): Path to the input build123dO file.
69+
- build123d_file (Path): Path to the input build123d file.
6770
"""
6871
parser = argparse.ArgumentParser(
6972
description="Find all the build123d symbols in module."
7073
)
7174

7275
# Required positional argument
7376
parser.add_argument("build123d_file", type=Path, help="Path to the build123d file")
77+
parser.add_argument(
78+
"--write",
79+
help="Overwrite glob import in input file, defaults to read-only and printed to stdout",
80+
action="store_true",
81+
)
7482

7583
args = parser.parse_args()
7684

7785
return args
7886

7987

80-
def find_used_symbols(source_code: str) -> set[str]:
81-
"""find_used_symbols
88+
def count_glob_imports(source_code: str) -> int:
89+
"""count_glob_imports
8290
83-
Extract all of the symbols from the source code into a set of strings.
91+
Count the number of occurences of a glob import e.g. (from build123d import *)
8492
8593
Args:
8694
source_code (str): contents of build123d program
8795
8896
Returns:
89-
set[str]: extracted symbols
97+
int: build123d glob import occurence count
9098
"""
9199
tree = ast.parse(source_code)
92100

93-
# Is the glob import from build123d used?
94-
from_glob_import = any(
101+
# count instances of glob usage
102+
glob_count = list(
95103
isinstance(node, ast.ImportFrom)
96104
and node.module == "build123d"
97105
and any(alias.name == "*" for alias in node.names)
98106
for node in ast.walk(tree)
99-
)
100-
if not from_glob_import:
101-
print("Glob import from build123d not found")
102-
sys.exit(0)
107+
).count(True)
108+
109+
return glob_count
110+
111+
112+
def find_used_symbols(source_code: str) -> set[str]:
113+
"""find_used_symbols
114+
115+
Extract all of the symbols from the source code into a set of strings.
116+
117+
Args:
118+
source_code (str): contents of build123d program
119+
120+
Returns:
121+
set[str]: extracted symbols
122+
"""
123+
tree = ast.parse(source_code)
103124

104125
symbols = set()
105126

@@ -152,15 +173,41 @@ def main():
152173
with open(args.build123d_file, "r", encoding="utf-8") as f:
153174
code = f.read()
154175

155-
# Check for the glob import and extract the symbols
176+
# Get the glob import count
177+
glob_count = count_glob_imports(code)
178+
179+
# Exit if no glob import was found
180+
if not glob_count:
181+
print("Glob import from build123d not found")
182+
sys.exit(0)
183+
184+
# Extract the symbols
156185
used_symbols = find_used_symbols(code)
157186

158187
# Find the imported build123d symbols
159188
actual_imports = sorted(used_symbols.intersection(set(build123d.__all__)))
160189

161190
# Create the import statement to replace the glob import
162191
import_line = f"from build123d import {', '.join(actual_imports)}"
163-
print(import_line)
192+
193+
if args.write:
194+
# Replace only the first instance, warn if more are found
195+
updated_code = re.sub(r"from build123d import\s*\*", import_line, code, count=1)
196+
197+
# Write code back to target file
198+
with open(args.build123d_file, "w", encoding="utf-8") as f:
199+
f.write(updated_code)
200+
201+
if glob_count:
202+
print(f"Replaced build123d glob import with '{import_line}'")
203+
204+
if glob_count > 1:
205+
print(
206+
"Warning: more than one instance of glob import was detected "
207+
f"(count: {glob_count}), only the first instance was replaced"
208+
)
209+
else:
210+
print(import_line)
164211

165212

166213
if __name__ == "__main__":

0 commit comments

Comments
 (0)