pyflyby 1.10.4__cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
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.
- pyflyby/__init__.py +61 -0
- pyflyby/__main__.py +9 -0
- pyflyby/_autoimp.py +2228 -0
- pyflyby/_cmdline.py +591 -0
- pyflyby/_comms.py +221 -0
- pyflyby/_dbg.py +1383 -0
- pyflyby/_dynimp.py +154 -0
- pyflyby/_fast_iter_modules.cpython-312-x86_64-linux-gnu.so +0 -0
- pyflyby/_file.py +771 -0
- pyflyby/_flags.py +230 -0
- pyflyby/_format.py +186 -0
- pyflyby/_idents.py +227 -0
- pyflyby/_import_sorting.py +165 -0
- pyflyby/_importclns.py +658 -0
- pyflyby/_importdb.py +535 -0
- pyflyby/_imports2s.py +643 -0
- pyflyby/_importstmt.py +723 -0
- pyflyby/_interactive.py +2113 -0
- pyflyby/_livepatch.py +793 -0
- pyflyby/_log.py +107 -0
- pyflyby/_modules.py +646 -0
- pyflyby/_parse.py +1396 -0
- pyflyby/_py.py +2165 -0
- pyflyby/_saveframe.py +1145 -0
- pyflyby/_saveframe_reader.py +471 -0
- pyflyby/_util.py +458 -0
- pyflyby/_version.py +8 -0
- pyflyby/autoimport.py +20 -0
- pyflyby/etc/pyflyby/canonical.py +10 -0
- pyflyby/etc/pyflyby/common.py +27 -0
- pyflyby/etc/pyflyby/forget.py +10 -0
- pyflyby/etc/pyflyby/mandatory.py +10 -0
- pyflyby/etc/pyflyby/numpy.py +156 -0
- pyflyby/etc/pyflyby/std.py +335 -0
- pyflyby/importdb.py +19 -0
- pyflyby/libexec/pyflyby/colordiff +34 -0
- pyflyby/libexec/pyflyby/diff-colorize +148 -0
- pyflyby/share/emacs/site-lisp/pyflyby.el +112 -0
- pyflyby-1.10.4.data/scripts/collect-exports +76 -0
- pyflyby-1.10.4.data/scripts/collect-imports +58 -0
- pyflyby-1.10.4.data/scripts/find-import +38 -0
- pyflyby-1.10.4.data/scripts/prune-broken-imports +34 -0
- pyflyby-1.10.4.data/scripts/pyflyby-diff +34 -0
- pyflyby-1.10.4.data/scripts/reformat-imports +27 -0
- pyflyby-1.10.4.data/scripts/replace-star-imports +37 -0
- pyflyby-1.10.4.data/scripts/saveframe +299 -0
- pyflyby-1.10.4.data/scripts/tidy-imports +170 -0
- pyflyby-1.10.4.data/scripts/transform-imports +47 -0
- pyflyby-1.10.4.dist-info/METADATA +605 -0
- pyflyby-1.10.4.dist-info/RECORD +53 -0
- pyflyby-1.10.4.dist-info/WHEEL +6 -0
- pyflyby-1.10.4.dist-info/entry_points.txt +4 -0
- pyflyby-1.10.4.dist-info/licenses/LICENSE.txt +19 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Utility to save information for debugging / reproducing an issue.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
If you have a script or command that is currently failing due to an issue
|
|
7
|
+
originating from upstream code, and you cannot share your private code as
|
|
8
|
+
a reproducer, use this utility to save relevant information to a file (e.g.,
|
|
9
|
+
error frames specific to the upstream codebase). Share the generated file
|
|
10
|
+
with the upstream team, enabling them to reproduce and diagnose the issue
|
|
11
|
+
independently.
|
|
12
|
+
|
|
13
|
+
Information saved in the file:
|
|
14
|
+
This utility captures and saves error stack frames to a file. It includes the
|
|
15
|
+
values of local variables from each stack frame, as well as metadata about each
|
|
16
|
+
frame and the exception raised by the user's script or command. Following is the
|
|
17
|
+
sample structure of the info saved in the file:
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
# 5th frame from the bottom
|
|
21
|
+
5: {
|
|
22
|
+
'frame_index': 5,
|
|
23
|
+
'filename': '/path/to/file.py',
|
|
24
|
+
'lineno': 3423,
|
|
25
|
+
'function_name': 'func1',
|
|
26
|
+
'function_qualname': 'FooClass.func1',
|
|
27
|
+
'function_object': <pickled object>,
|
|
28
|
+
'module_name': '<frame_module>'
|
|
29
|
+
'frame_identifier': '/path/to/file.py,3423,func1',
|
|
30
|
+
'code': '... python code line ...'
|
|
31
|
+
'variables': {'local_variable1': <pickled value>, 'local_variable2': <pickled value>, ...}
|
|
32
|
+
},
|
|
33
|
+
# 17th frame from the bottom
|
|
34
|
+
17: {
|
|
35
|
+
'frame_index': 17,
|
|
36
|
+
...
|
|
37
|
+
},
|
|
38
|
+
...
|
|
39
|
+
'exception_full_string': f'{exc.__class.__name__}: {exc}'
|
|
40
|
+
'exception_object': exc,
|
|
41
|
+
'exception_string': str(exc),
|
|
42
|
+
'exception_class_name': exc.__class__.__name__,
|
|
43
|
+
'exception_class_qualname': exc.__class__.__qualname__,
|
|
44
|
+
'traceback': '(multiline traceback)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
NOTE:
|
|
48
|
+
- The above data gets saved in the file in pickled form.
|
|
49
|
+
- In the above data, the key of each frame's entry is the index of that frame
|
|
50
|
+
from the bottom of the error stack trace. So the first frame from the bottom
|
|
51
|
+
(the error frame) has index 1, and so on.
|
|
52
|
+
- 'variables' key in each frame's entry stores the local variables of that frame.
|
|
53
|
+
- The 'exception_object' key stores the actual exception object but without
|
|
54
|
+
the __traceback__ info (for security reasons).
|
|
55
|
+
|
|
56
|
+
Example Usage:
|
|
57
|
+
|
|
58
|
+
Let's say your script / command is raising an error with the following traceback:
|
|
59
|
+
|
|
60
|
+
File "dir/__init__.py", line 6, in init_func1
|
|
61
|
+
func1()
|
|
62
|
+
File "dir/mod1.py", line 14, in func1
|
|
63
|
+
func2()
|
|
64
|
+
File "dir/mod1.py", line 9, in func2
|
|
65
|
+
obj.func2()
|
|
66
|
+
File "dir/pkg1/mod2.py", line 10, in func2
|
|
67
|
+
func3()
|
|
68
|
+
File "dir/pkg1/pkg2/mod3.py", line 6, in func3
|
|
69
|
+
raise ValueError("Error is raised")
|
|
70
|
+
ValueError: Error is raised
|
|
71
|
+
|
|
72
|
+
=> To save the last frame (the error frame) in file '/path/to/file', use:
|
|
73
|
+
$ saveframe --filename=/path/to/file <script_or_command_to_run>
|
|
74
|
+
|
|
75
|
+
=> To save a specific frame like `File "dir/mod1.py", line 9, in func2`, use:
|
|
76
|
+
$ saveframe --filename=/path/to/file --frames=mod1.py:9:func2 <script_or_command_to_run>
|
|
77
|
+
|
|
78
|
+
=> To save the first 3 frames from the bottom, use:
|
|
79
|
+
$ saveframe --frames=3 <script_or_command_to_run>
|
|
80
|
+
|
|
81
|
+
=> To save all the frames from 'mod1.py' and 'mod2.py' files, use:
|
|
82
|
+
$ saveframe --filename=/path/to/file --frames=mod1.py::,mod2.py:: <script_or_command_to_run>
|
|
83
|
+
|
|
84
|
+
=> To save a range of frames from 'mod1.py' to 'mod3.py', use:
|
|
85
|
+
$ saveframe --frames=mod1.py::..mod3.py:: <script_or_command_to_run>
|
|
86
|
+
|
|
87
|
+
=> To save a range of frames from '__init__.py' till the last frame, use:
|
|
88
|
+
$ saveframe --frames=__init__.py::.. <script_or_command_to_run>
|
|
89
|
+
|
|
90
|
+
=> To only save local variables 'var1' and 'var2' from the frames, use:
|
|
91
|
+
$ saveframe --frames=frames_to_save --variables=var1,var2 <script_or_command_to_run>
|
|
92
|
+
|
|
93
|
+
=> To exclude local variables 'var1' and 'var2' from the frames, use:
|
|
94
|
+
$ saveframe --frames=frames_to_save --exclude_variables=var1,var2 <script_or_command_to_run>
|
|
95
|
+
|
|
96
|
+
For interactive use cases, checkout pyflyby.saveframe function.
|
|
97
|
+
"""
|
|
98
|
+
from __future__ import annotations
|
|
99
|
+
|
|
100
|
+
# Save an unspoiled copy of globals for running the user program.
|
|
101
|
+
globals_cpy = globals().copy()
|
|
102
|
+
|
|
103
|
+
import argparse
|
|
104
|
+
import os
|
|
105
|
+
import sys
|
|
106
|
+
|
|
107
|
+
from pyflyby._saveframe import (_SAVEFRAME_LOGGER,
|
|
108
|
+
_save_frames_and_exception_info_to_file,
|
|
109
|
+
_validate_saveframe_arguments)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def getargs():
|
|
113
|
+
"""
|
|
114
|
+
Parse the command-line arguments.
|
|
115
|
+
"""
|
|
116
|
+
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
|
|
117
|
+
description=__doc__,
|
|
118
|
+
prog=os.path.basename(sys.argv[0]))
|
|
119
|
+
parser.add_argument(
|
|
120
|
+
"--filename", default=None,
|
|
121
|
+
help="File path in which to save the frame information. If this file "
|
|
122
|
+
"already exists, it will be overwritten; otherwise, a new file will "
|
|
123
|
+
"be created with permission mode '0o644'\nDefault behavior: "
|
|
124
|
+
"If --filename is not passed, the info gets saved in the "
|
|
125
|
+
"'saveframe.pkl' file in the current working directory."
|
|
126
|
+
)
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"--frames", default=None,
|
|
129
|
+
help="Error stack frames to save.\n"
|
|
130
|
+
"Default behavior: If --frames is not passed, the first frame from "
|
|
131
|
+
"the bottom (the error frame) is saved.\n\n"
|
|
132
|
+
"A single frame follows the format "
|
|
133
|
+
"'filename:line_no:function_name', where:\n"
|
|
134
|
+
" - filename: The file path or a regex pattern matching the file "
|
|
135
|
+
"path (displayed in the stack trace) of that error frame.\n"
|
|
136
|
+
" - line_no (Optional): The code line number (displayed in the "
|
|
137
|
+
"stack trace) of that error frame.\n"
|
|
138
|
+
" - function_name (Optional): The function name (displayed in "
|
|
139
|
+
"the stack trace) of that error frame.\n\n"
|
|
140
|
+
"Partial frames are also supported where line_no and/or function_name "
|
|
141
|
+
"can be omitted:\n"
|
|
142
|
+
" - 'filename::' -> Includes all the frames that matches the filename\n"
|
|
143
|
+
" - 'filename:line_no:' -> Include all the frames that matches "
|
|
144
|
+
"specific line in any function in the filename\n"
|
|
145
|
+
" - 'filename::function_name' -> Include all the frames that matches "
|
|
146
|
+
"any line in the specific function in the filename\n\n"
|
|
147
|
+
"Following formats are supported to pass the frames:\n\n"
|
|
148
|
+
"1. Single frame:\n"
|
|
149
|
+
" --frames=frame\n"
|
|
150
|
+
" Example: --frames=/path/to/file.py:24:some_func\n"
|
|
151
|
+
" Includes only the specified frame.\n\n"
|
|
152
|
+
"2. Multiple frames:\n"
|
|
153
|
+
" --frames=frame1,frame2,...\n"
|
|
154
|
+
" Example: --frames=/dir/foo.py:45:,.*/dir2/bar.py:89:caller\n"
|
|
155
|
+
" Includes all specified frames.\n\n"
|
|
156
|
+
"3. Range of frames:\n"
|
|
157
|
+
" --frames=first_frame..last_frame\n"
|
|
158
|
+
" Example: --frames=/dir/foo.py:45:get_foo../dir3/blah.py:23:myfunc\n"
|
|
159
|
+
" Includes all the frames from first_frame to last_frame (both inclusive).\n\n"
|
|
160
|
+
"4. Range from first_frame to bottom:\n"
|
|
161
|
+
" --frames=first_frame..\n"
|
|
162
|
+
" Example: --frames=/dir/foo.py:45:get_foo..\n"
|
|
163
|
+
" Includes all the frames from first_frame to the bottom of the stack trace.\n\n"
|
|
164
|
+
"5. Number of Frames from Bottom:\n"
|
|
165
|
+
" --frames=num\n"
|
|
166
|
+
" Example: --frames=5\n"
|
|
167
|
+
" Includes the first 'num' frames from the bottom of the stack trace."
|
|
168
|
+
)
|
|
169
|
+
parser.add_argument(
|
|
170
|
+
"--variables", default=None,
|
|
171
|
+
help="Local variables to include in each frame. Allowed format:\n"
|
|
172
|
+
"--variables=var1,var2,var3...\nExample: --variables=foo,bar\n\n"
|
|
173
|
+
"Default behavior: If --variables is not passed, save all the local "
|
|
174
|
+
"variables of the included frames."
|
|
175
|
+
)
|
|
176
|
+
parser.add_argument(
|
|
177
|
+
"--exclude_variables", default=None,
|
|
178
|
+
help="Local variables to exclude from each frame. Allowed format:\n"
|
|
179
|
+
"--exclude_variables=var1,var2,var3...\nExample: "
|
|
180
|
+
"--exclude_variables=foo,bar\n\n"
|
|
181
|
+
"Default behavior: If --exclude_variables is not passed, save all "
|
|
182
|
+
"the local variables of the included frames as per --variables."
|
|
183
|
+
)
|
|
184
|
+
parser.add_argument(
|
|
185
|
+
"command", default=argparse.SUPPRESS, nargs=argparse.REMAINDER,
|
|
186
|
+
help="User's script / command to execute.")
|
|
187
|
+
args = parser.parse_args()
|
|
188
|
+
return args
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def which(program):
|
|
192
|
+
"""
|
|
193
|
+
Find the complete path of the ``program``.
|
|
194
|
+
|
|
195
|
+
:param program:
|
|
196
|
+
Program for which to find the complete path.
|
|
197
|
+
:return:
|
|
198
|
+
Complete path of the program.
|
|
199
|
+
"""
|
|
200
|
+
if os.access(program, os.R_OK):
|
|
201
|
+
return program
|
|
202
|
+
|
|
203
|
+
fpath, fname = os.path.split(program)
|
|
204
|
+
if fpath:
|
|
205
|
+
if os.access(fpath, os.R_OK):
|
|
206
|
+
return program
|
|
207
|
+
else:
|
|
208
|
+
for path in os.environ["PATH"].split(os.pathsep):
|
|
209
|
+
exe_file = os.path.join(path, program)
|
|
210
|
+
if os.access(exe_file, os.X_OK):
|
|
211
|
+
return exe_file
|
|
212
|
+
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def execfile(filepath):
|
|
217
|
+
"""
|
|
218
|
+
Execute the script stored in ``filepath``.
|
|
219
|
+
|
|
220
|
+
:param filepath:
|
|
221
|
+
Path of the script to execute.
|
|
222
|
+
"""
|
|
223
|
+
globals_cpy.update({
|
|
224
|
+
"__file__": filepath,
|
|
225
|
+
"__name__": "__main__",
|
|
226
|
+
})
|
|
227
|
+
with open(filepath, 'rb') as file:
|
|
228
|
+
exec(compile(file.read(), filepath, 'exec'), globals_cpy)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def run_program(command):
|
|
232
|
+
"""
|
|
233
|
+
Run a program.
|
|
234
|
+
|
|
235
|
+
:param command:
|
|
236
|
+
List containing the command to run.
|
|
237
|
+
"""
|
|
238
|
+
if len(command) == 0:
|
|
239
|
+
raise SystemExit("Error: Please pass a valid script / command to run!")
|
|
240
|
+
if command[0] == '-c':
|
|
241
|
+
if len(command) == 1:
|
|
242
|
+
raise SystemExit("Error: Please pass a valid script / command to run!")
|
|
243
|
+
# Set sys.argv. Mimic regular python -c by dropping the code but
|
|
244
|
+
# keeping the rest.
|
|
245
|
+
sys.argv = ['-c'] + command[2:]
|
|
246
|
+
globals_cpy['__file__'] = None
|
|
247
|
+
# Evaluate the command line code.
|
|
248
|
+
code = compile(command[1], "<stdin>", "exec")
|
|
249
|
+
eval(code)
|
|
250
|
+
else:
|
|
251
|
+
prog = which(command[0])
|
|
252
|
+
if not prog:
|
|
253
|
+
raise SystemExit(f"Error: Can't find the script / command: {command[0]!r}")
|
|
254
|
+
|
|
255
|
+
# Set sys.argv to mimic the command execution.
|
|
256
|
+
sys.argv = command
|
|
257
|
+
sys.path.insert(0, os.path.dirname(os.path.realpath(prog)))
|
|
258
|
+
execfile(prog)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def main():
|
|
262
|
+
"""
|
|
263
|
+
Main body of the script.
|
|
264
|
+
"""
|
|
265
|
+
args = getargs()
|
|
266
|
+
# Validate the arguments.
|
|
267
|
+
filename, frames, variables, exclude_variables = _validate_saveframe_arguments(
|
|
268
|
+
filename=args.filename, frames=args.frames, variables=args.variables,
|
|
269
|
+
exclude_variables=args.exclude_variables, utility='script')
|
|
270
|
+
command = args.command
|
|
271
|
+
command_string = ' '.join(command)
|
|
272
|
+
|
|
273
|
+
if len(command) == 0:
|
|
274
|
+
raise SystemExit("Error: Please pass a valid script / command to run!")
|
|
275
|
+
if (command[0] in ['python', 'python3'] or command[0].endswith('/python')
|
|
276
|
+
or command[0].endswith('/python3')):
|
|
277
|
+
del command[0]
|
|
278
|
+
|
|
279
|
+
# Run the user script / command. Explicitly catch Exception and
|
|
280
|
+
# KeyboardInterrupt rather than BaseException, since we don't want to
|
|
281
|
+
# catch SystemExit.
|
|
282
|
+
try:
|
|
283
|
+
_SAVEFRAME_LOGGER.info("Executing the program: %a", command_string)
|
|
284
|
+
run_program(command)
|
|
285
|
+
except (Exception, KeyboardInterrupt) as err:
|
|
286
|
+
_SAVEFRAME_LOGGER.info(
|
|
287
|
+
"Saving frames and metadata info for the exception: %a", err)
|
|
288
|
+
# Save the frames and metadata info to the file.
|
|
289
|
+
_save_frames_and_exception_info_to_file(
|
|
290
|
+
filename=filename, frames=frames, variables=variables,
|
|
291
|
+
exclude_variables=exclude_variables,
|
|
292
|
+
exception_obj=err)
|
|
293
|
+
else:
|
|
294
|
+
raise SystemExit(
|
|
295
|
+
f"Error: No exception is raised by the program: {command_string!a}")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
if __name__ == '__main__':
|
|
299
|
+
main()
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
tidy-imports *.py
|
|
4
|
+
tidy-imports < foo.py
|
|
5
|
+
|
|
6
|
+
Automatically improves python import statements.
|
|
7
|
+
|
|
8
|
+
- Adds missing imports and mandatory imports.
|
|
9
|
+
- Removes unused imports.
|
|
10
|
+
- Nicely formats imports (sorts, aligns, wraps).
|
|
11
|
+
|
|
12
|
+
If filenames are given on the command line, rewrites them. Otherwise, if
|
|
13
|
+
stdin is not a tty, read from stdin and write to stdout.
|
|
14
|
+
|
|
15
|
+
Only top-level import statements are touched.
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# pyflyby/tidy-imports
|
|
20
|
+
# Copyright (C) 2011, 2012, 2014 Karl Chen.
|
|
21
|
+
# License: MIT http://opensource.org/licenses/MIT
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from pyflyby._cmdline import (_get_pyproj_toml_config, hfmt,
|
|
25
|
+
parse_args, process_actions)
|
|
26
|
+
from pyflyby._import_sorting import sort_imports
|
|
27
|
+
from pyflyby._imports2s import (canonicalize_imports,
|
|
28
|
+
fix_unused_and_missing_imports,
|
|
29
|
+
replace_star_imports,
|
|
30
|
+
transform_imports)
|
|
31
|
+
from pyflyby._parse import PythonBlock
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _addopts(parser):
|
|
35
|
+
"""
|
|
36
|
+
Callbacks to the parser to fill in extra options.
|
|
37
|
+
"""
|
|
38
|
+
parser.add_option('--add-missing',
|
|
39
|
+
default=True, action='store_true',
|
|
40
|
+
help=hfmt('''
|
|
41
|
+
(Default) Add missing imports.'''))
|
|
42
|
+
parser.add_option('--no-add-missing', dest='add_missing',
|
|
43
|
+
default=True, action='store_false',
|
|
44
|
+
help=hfmt('''
|
|
45
|
+
Don't add missing imports.'''))
|
|
46
|
+
parser.add_option('--remove-unused',
|
|
47
|
+
default="AUTOMATIC", action='store_true',
|
|
48
|
+
help=hfmt('''
|
|
49
|
+
Remove unused imports
|
|
50
|
+
(default unless filename == __init__.py).'''))
|
|
51
|
+
parser.add_option('--no-remove-unused', dest='remove_unused',
|
|
52
|
+
action='store_false',
|
|
53
|
+
help=hfmt('''
|
|
54
|
+
Don't remove unused imports
|
|
55
|
+
(default if filename == __init__.py).'''))
|
|
56
|
+
parser.add_option('--add-mandatory',
|
|
57
|
+
default=True, action='store_true',
|
|
58
|
+
help=hfmt('''
|
|
59
|
+
(Default) Add mandatory imports.'''))
|
|
60
|
+
parser.add_option('--no-add-mandatory', dest='add_mandatory',
|
|
61
|
+
default=True, action='store_false',
|
|
62
|
+
help=hfmt('''
|
|
63
|
+
Don't add mandatory imports.'''))
|
|
64
|
+
parser.add_option('--replace-star-imports',
|
|
65
|
+
default=False, action='store_true',
|
|
66
|
+
help=hfmt('''
|
|
67
|
+
Replace 'from foo.bar import *' with full list
|
|
68
|
+
of imports before removing unused imports.'''))
|
|
69
|
+
parser.add_option('--no-replace-star-imports',
|
|
70
|
+
dest='replace_star_imports',
|
|
71
|
+
action='store_false',
|
|
72
|
+
help=hfmt('''
|
|
73
|
+
(Default) Don't replace 'from foo.bar import
|
|
74
|
+
*'.'''))
|
|
75
|
+
parser.add_option('--canonicalize',
|
|
76
|
+
default=True, action='store_true',
|
|
77
|
+
help=hfmt('''
|
|
78
|
+
(Default) Replace imports with canonical
|
|
79
|
+
equivalent imports, according to database.'''))
|
|
80
|
+
parser.add_option('--experimental-sort-imports',
|
|
81
|
+
default=False, action='store_true',
|
|
82
|
+
help=hfmt('''
|
|
83
|
+
experimental import sorting'''))
|
|
84
|
+
parser.add_option('--no-canonicalize', dest='canonicalize',
|
|
85
|
+
default=True, action='store_false',
|
|
86
|
+
help=hfmt('''
|
|
87
|
+
Don't canonicalize imports.'''))
|
|
88
|
+
parser.add_option('--exclude', type='string', dest='exclude',
|
|
89
|
+
action='append', help=hfmt('Files to exclude from formatting.'))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def transform_callback(option, opt_str, value, group):
|
|
93
|
+
k, v = value.split("=", 1)
|
|
94
|
+
group.values.transformations[k] = v
|
|
95
|
+
parser.add_option("--transform", action='callback',
|
|
96
|
+
type="string", callback=transform_callback,
|
|
97
|
+
metavar="OLD=NEW",
|
|
98
|
+
dest="transformations", default={},
|
|
99
|
+
help=hfmt('''
|
|
100
|
+
Replace OLD with NEW in imports.
|
|
101
|
+
May be specified multiple times.'''))
|
|
102
|
+
def no_add_callback(option, opt_str, value, group):
|
|
103
|
+
group.values.add_missing = False
|
|
104
|
+
group.values.add_mandatory = False
|
|
105
|
+
parser.add_option('--no-add', action='callback',
|
|
106
|
+
callback=no_add_callback,
|
|
107
|
+
help=hfmt('''
|
|
108
|
+
Equivalent to --no-add-missing
|
|
109
|
+
--no-add-mandatory.'''))
|
|
110
|
+
def main() -> None:
|
|
111
|
+
|
|
112
|
+
config = _get_pyproj_toml_config()
|
|
113
|
+
if config:
|
|
114
|
+
default_config = config.get('tool', {}).get('pyflyby',{})
|
|
115
|
+
else:
|
|
116
|
+
default_config = {}
|
|
117
|
+
|
|
118
|
+
def _add_opts_and_defaults(parser):
|
|
119
|
+
_addopts(parser)
|
|
120
|
+
parser.set_defaults(**default_config)
|
|
121
|
+
|
|
122
|
+
options, args = parse_args(
|
|
123
|
+
_add_opts_and_defaults,
|
|
124
|
+
import_format_params=True,
|
|
125
|
+
modify_action_params=True,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def modify(block:PythonBlock) -> PythonBlock:
|
|
129
|
+
if options.transformations:
|
|
130
|
+
block = transform_imports(block, options.transformations,
|
|
131
|
+
params=options.params)
|
|
132
|
+
if options.replace_star_imports:
|
|
133
|
+
block = replace_star_imports(block, params=options.params)
|
|
134
|
+
block = fix_unused_and_missing_imports(
|
|
135
|
+
block, params=options.params,
|
|
136
|
+
add_missing=options.add_missing,
|
|
137
|
+
remove_unused=options.remove_unused,
|
|
138
|
+
add_mandatory=options.add_mandatory,
|
|
139
|
+
)
|
|
140
|
+
# TODO: disable sorting until we figure out #287
|
|
141
|
+
# https://github.com/deshaw/pyflyby/issues/287
|
|
142
|
+
|
|
143
|
+
# here we get a (single?) PythonBlock, we can access each statement with
|
|
144
|
+
# >>> block.statements
|
|
145
|
+
# and each statement can have a:
|
|
146
|
+
# is_import
|
|
147
|
+
# or
|
|
148
|
+
# is_comment or blank
|
|
149
|
+
|
|
150
|
+
# TODO: we do Python(str(...)) in order to unparse-reparse and get proper ast node numbers.
|
|
151
|
+
if options.experimental_sort_imports:
|
|
152
|
+
sorted_imports = PythonBlock(str(sort_imports(block)))
|
|
153
|
+
else:
|
|
154
|
+
sorted_imports = block
|
|
155
|
+
if options.canonicalize:
|
|
156
|
+
cannonical_imports = canonicalize_imports(sorted_imports, params=options.params)
|
|
157
|
+
else:
|
|
158
|
+
cannonical_imports = sorted_imports
|
|
159
|
+
return cannonical_imports
|
|
160
|
+
|
|
161
|
+
cmdline_exclude = getattr(options, "exclude")
|
|
162
|
+
process_actions(
|
|
163
|
+
args,
|
|
164
|
+
options.actions, modify,
|
|
165
|
+
exclude=default_config.get('tidy-imports', {}).get('exclude', []) + (cmdline_exclude if cmdline_exclude else [])
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
if __name__ == '__main__':
|
|
170
|
+
main()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
transform-imports --transform aa.bb=xx.yy *.py
|
|
4
|
+
transform-imports --transform aa.bb=xx.yy < foo.py
|
|
5
|
+
|
|
6
|
+
Transforms::
|
|
7
|
+
from aa.bb.cc import dd, ee
|
|
8
|
+
from aa import bb
|
|
9
|
+
to::
|
|
10
|
+
from xx.yy.cc import dd, ee
|
|
11
|
+
from xx import yy as bb
|
|
12
|
+
|
|
13
|
+
If filenames are given on the command line, rewrites them. Otherwise, if
|
|
14
|
+
stdin is not a tty, read from stdin and write to stdout.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# pyflyby/transform-imports
|
|
19
|
+
# Copyright (C) 2014 Karl Chen.
|
|
20
|
+
# License: MIT http://opensource.org/licenses/MIT
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
from pyflyby._cmdline import hfmt, parse_args, process_actions
|
|
24
|
+
from pyflyby._imports2s import transform_imports
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main():
|
|
28
|
+
transformations = {}
|
|
29
|
+
def addopts(parser):
|
|
30
|
+
def callback(option, opt_str, value, group):
|
|
31
|
+
k, v = value.split("=", 1)
|
|
32
|
+
transformations[k] = v
|
|
33
|
+
parser.add_option("--transform", action='callback',
|
|
34
|
+
type="string", callback=callback,
|
|
35
|
+
metavar="OLD=NEW",
|
|
36
|
+
help=hfmt('''
|
|
37
|
+
Replace OLD with NEW in imports.
|
|
38
|
+
May be specified multiple times.'''))
|
|
39
|
+
options, args = parse_args(
|
|
40
|
+
addopts, import_format_params=True, modify_action_params=True)
|
|
41
|
+
def modify(x):
|
|
42
|
+
return transform_imports(x, transformations, params=options.params)
|
|
43
|
+
process_actions(args, options.actions, modify)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == '__main__':
|
|
47
|
+
main()
|