16
16
an import statement listing only those names. This practice can help
17
17
prevent polluting the global namespace and improve clarity.
18
18
19
- Example:
20
- deglob.py my_build123d_script.py
19
+ Examples:
20
+ python deglob.py my_build123d_script.py
21
+ python deglob.py -h
21
22
22
23
After parsing my_build123d_script.py, the script prints a line such as:
23
24
from build123d import Workplane, Solid
26
27
27
28
Module Contents:
28
29
- 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.
29
31
- find_used_symbols(): Parse Python source code to find referenced names.
30
32
- main(): Orchestrates reading the file, analyzing symbols, and printing
31
33
the replacement import line.
53
55
import ast
54
56
import sys
55
57
from pathlib import Path
58
+ import re
56
59
57
60
import build123d
58
61
@@ -63,43 +66,61 @@ def parse_args():
63
66
64
67
Returns:
65
68
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.
67
70
"""
68
71
parser = argparse .ArgumentParser (
69
72
description = "Find all the build123d symbols in module."
70
73
)
71
74
72
75
# Required positional argument
73
76
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
+ )
74
82
75
83
args = parser .parse_args ()
76
84
77
85
return args
78
86
79
87
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
82
90
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 *)
84
92
85
93
Args:
86
94
source_code (str): contents of build123d program
87
95
88
96
Returns:
89
- set[str]: extracted symbols
97
+ int: build123d glob import occurence count
90
98
"""
91
99
tree = ast .parse (source_code )
92
100
93
- # Is the glob import from build123d used?
94
- from_glob_import = any (
101
+ # count instances of glob usage
102
+ glob_count = list (
95
103
isinstance (node , ast .ImportFrom )
96
104
and node .module == "build123d"
97
105
and any (alias .name == "*" for alias in node .names )
98
106
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 )
103
124
104
125
symbols = set ()
105
126
@@ -152,15 +173,41 @@ def main():
152
173
with open (args .build123d_file , "r" , encoding = "utf-8" ) as f :
153
174
code = f .read ()
154
175
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
156
185
used_symbols = find_used_symbols (code )
157
186
158
187
# Find the imported build123d symbols
159
188
actual_imports = sorted (used_symbols .intersection (set (build123d .__all__ )))
160
189
161
190
# Create the import statement to replace the glob import
162
191
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 )
164
211
165
212
166
213
if __name__ == "__main__" :
0 commit comments