skilleter-thingy 0.0.40__py3-none-any.whl → 0.0.41__py3-none-any.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.
Potentially problematic release.
This version of skilleter-thingy might be problematic. Click here for more details.
- skilleter_thingy/__init__.py +6 -0
- skilleter_thingy/addpath.py +107 -0
- skilleter_thingy/borger.py +269 -0
- skilleter_thingy/console_colours.py +63 -0
- skilleter_thingy/diskspacecheck.py +67 -0
- skilleter_thingy/docker_purge.py +113 -0
- skilleter_thingy/ffind.py +536 -0
- skilleter_thingy/ggit.py +90 -0
- skilleter_thingy/ggrep.py +154 -0
- skilleter_thingy/git_br.py +180 -0
- skilleter_thingy/git_ca.py +142 -0
- skilleter_thingy/git_cleanup.py +287 -0
- skilleter_thingy/git_co.py +220 -0
- skilleter_thingy/git_common.py +61 -0
- skilleter_thingy/git_hold.py +154 -0
- skilleter_thingy/git_mr.py +92 -0
- skilleter_thingy/git_parent.py +77 -0
- skilleter_thingy/git_review.py +1428 -0
- skilleter_thingy/git_update.py +385 -0
- skilleter_thingy/git_wt.py +96 -0
- skilleter_thingy/gitcmp_helper.py +322 -0
- skilleter_thingy/gitprompt.py +274 -0
- skilleter_thingy/gl.py +174 -0
- skilleter_thingy/gphotosync.py +610 -0
- skilleter_thingy/linecount.py +155 -0
- skilleter_thingy/moviemover.py +133 -0
- skilleter_thingy/photodupe.py +136 -0
- skilleter_thingy/phototidier.py +248 -0
- skilleter_thingy/py_audit.py +131 -0
- skilleter_thingy/readable.py +270 -0
- skilleter_thingy/remdir.py +126 -0
- skilleter_thingy/rmdupe.py +550 -0
- skilleter_thingy/rpylint.py +91 -0
- skilleter_thingy/splitpics.py +99 -0
- skilleter_thingy/strreplace.py +82 -0
- skilleter_thingy/sysmon.py +435 -0
- skilleter_thingy/tfm.py +920 -0
- skilleter_thingy/tfparse.py +101 -0
- skilleter_thingy/thingy/__init__.py +6 -0
- skilleter_thingy/thingy/colour.py +213 -0
- skilleter_thingy/thingy/dc_curses.py +278 -0
- skilleter_thingy/thingy/dc_defaults.py +221 -0
- skilleter_thingy/thingy/dc_util.py +50 -0
- skilleter_thingy/thingy/dircolors.py +308 -0
- skilleter_thingy/thingy/docker.py +95 -0
- skilleter_thingy/thingy/files.py +142 -0
- skilleter_thingy/thingy/git.py +1371 -0
- skilleter_thingy/thingy/git2.py +1307 -0
- skilleter_thingy/thingy/gitlab.py +193 -0
- skilleter_thingy/thingy/logger.py +112 -0
- skilleter_thingy/thingy/path.py +156 -0
- skilleter_thingy/thingy/popup.py +87 -0
- skilleter_thingy/thingy/process.py +112 -0
- skilleter_thingy/thingy/run.py +334 -0
- skilleter_thingy/thingy/tfm_pane.py +595 -0
- skilleter_thingy/thingy/tidy.py +160 -0
- skilleter_thingy/trimpath.py +84 -0
- skilleter_thingy/window_rename.py +92 -0
- skilleter_thingy/xchmod.py +125 -0
- skilleter_thingy/yamlcheck.py +89 -0
- {skilleter_thingy-0.0.40.dist-info → skilleter_thingy-0.0.41.dist-info}/METADATA +1 -1
- skilleter_thingy-0.0.41.dist-info/RECORD +66 -0
- skilleter_thingy-0.0.41.dist-info/top_level.txt +1 -0
- skilleter_thingy-0.0.40.dist-info/RECORD +0 -6
- skilleter_thingy-0.0.40.dist-info/top_level.txt +0 -1
- {skilleter_thingy-0.0.40.dist-info → skilleter_thingy-0.0.41.dist-info}/LICENSE +0 -0
- {skilleter_thingy-0.0.40.dist-info → skilleter_thingy-0.0.41.dist-info}/WHEEL +0 -0
- {skilleter_thingy-0.0.40.dist-info → skilleter_thingy-0.0.41.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
################################################################################
|
|
4
|
+
""" Code for running a subprocess, optionally capturing stderr and/or stdout and
|
|
5
|
+
optionally echoing either or both to the console in realtime and storing.
|
|
6
|
+
|
|
7
|
+
Uses threads to capture and output stderr and stdout since this seems to be
|
|
8
|
+
the only way to do it (Popen does not have the ability to output the process
|
|
9
|
+
stdout output to the stdout output).
|
|
10
|
+
|
|
11
|
+
Intended for more versatile replacement for the thingy process.run() function
|
|
12
|
+
which can handle all combinations of foreground/background console/return
|
|
13
|
+
stderr/stdout/both options. """
|
|
14
|
+
|
|
15
|
+
# TODO: This does not run on Python versions <3.5 (so Ubuntu 14.04 is a problem!)
|
|
16
|
+
################################################################################
|
|
17
|
+
|
|
18
|
+
################################################################################
|
|
19
|
+
# Imports
|
|
20
|
+
|
|
21
|
+
import sys
|
|
22
|
+
import subprocess
|
|
23
|
+
import threading
|
|
24
|
+
import shlex
|
|
25
|
+
|
|
26
|
+
import thingy.colour as colour
|
|
27
|
+
import thingy.tidy as tidy
|
|
28
|
+
|
|
29
|
+
################################################################################
|
|
30
|
+
|
|
31
|
+
class RunError(Exception):
|
|
32
|
+
""" Run exception """
|
|
33
|
+
|
|
34
|
+
def __init__(self, msg, status=1):
|
|
35
|
+
super().__init__(msg)
|
|
36
|
+
self.msg = msg
|
|
37
|
+
self.status = status
|
|
38
|
+
|
|
39
|
+
################################################################################
|
|
40
|
+
|
|
41
|
+
def capture_output(cmd, input_stream, output_streams, ansi_clean):
|
|
42
|
+
""" Capture data from a stream (input_stream), optionally
|
|
43
|
+
outputting it (if output_streams is not None and optionally
|
|
44
|
+
saving it into a variable (data, if not None), terminating
|
|
45
|
+
when the specified command (cmd, which is presumed to be the process
|
|
46
|
+
outputting to the input stream) exits.
|
|
47
|
+
TODO: Use of convert_ansi should be controlled via a parameter (off/light/dark)
|
|
48
|
+
TODO: Another issue is that readline() only returns at EOF or EOL, so if you get a prompt "Continue?" with no newline you do not see it until after you respond to it.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
while True:
|
|
52
|
+
output = input_stream.readline()
|
|
53
|
+
|
|
54
|
+
if output:
|
|
55
|
+
if output_streams:
|
|
56
|
+
for stream in output_streams:
|
|
57
|
+
if isinstance(stream, list):
|
|
58
|
+
stream.append(output.rstrip())
|
|
59
|
+
else:
|
|
60
|
+
if stream in (sys.stdout, sys.stderr):
|
|
61
|
+
stream.write(tidy.convert_ansi(output))
|
|
62
|
+
elif ansi_clean:
|
|
63
|
+
stream.write(colour.clean(output))
|
|
64
|
+
else:
|
|
65
|
+
stream.write(output)
|
|
66
|
+
|
|
67
|
+
elif cmd.poll() is not None:
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
################################################################################
|
|
71
|
+
|
|
72
|
+
def capture_continuous(cmd, input_stream, output_streams, ansi_clean):
|
|
73
|
+
""" Capture data from a stream (input_stream), optionally
|
|
74
|
+
outputting it (if output_streams is not None and optionally
|
|
75
|
+
saving it into a variable (data, if not None), terminating
|
|
76
|
+
when the specified command (cmd, which is presumed to be the process
|
|
77
|
+
outputting to the input stream) exits.
|
|
78
|
+
TODO: Use of convert_ansi should be controlled via a parameter (off/light/dark)
|
|
79
|
+
TODO: ansi_clean not implemented
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
output_buffer = []
|
|
83
|
+
|
|
84
|
+
while True:
|
|
85
|
+
output = input_stream.read(1)
|
|
86
|
+
|
|
87
|
+
if output:
|
|
88
|
+
if output_streams:
|
|
89
|
+
for stream in output_streams:
|
|
90
|
+
if isinstance(stream, list):
|
|
91
|
+
if output == '\n':
|
|
92
|
+
stream.append(''.join(output_buffer))
|
|
93
|
+
output_buffer = []
|
|
94
|
+
else:
|
|
95
|
+
output_buffer.append(output)
|
|
96
|
+
else:
|
|
97
|
+
stream.write(output)
|
|
98
|
+
stream.flush()
|
|
99
|
+
|
|
100
|
+
elif cmd.poll() is not None:
|
|
101
|
+
if output_buffer:
|
|
102
|
+
stream.append(''.join(output_buffer))
|
|
103
|
+
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
################################################################################
|
|
107
|
+
|
|
108
|
+
def process(command,
|
|
109
|
+
stdout=None, stderr=None,
|
|
110
|
+
return_stdout=False, return_stderr=False,
|
|
111
|
+
shell=False,
|
|
112
|
+
output=None,
|
|
113
|
+
ansi_clean=False,
|
|
114
|
+
exception=True,
|
|
115
|
+
continuous=False):
|
|
116
|
+
""" Run an external command.
|
|
117
|
+
|
|
118
|
+
stdout and stderr indicate whether stdout/err are output and/or sent to a file and/or stored in a variable.
|
|
119
|
+
They can be boolean (True: output to sys.stdout/err, False: Do nothing), a file handle or a variable, or an
|
|
120
|
+
array of any number of these (except booleans).
|
|
121
|
+
|
|
122
|
+
return_stdout and return_stderr indicate whether stdout/err should be returned from the function (setting
|
|
123
|
+
these to False saves memory if the output is not required).
|
|
124
|
+
|
|
125
|
+
If shell is True the command will be run in a shell and wildcard arguments expanded
|
|
126
|
+
|
|
127
|
+
If exception is True an exception will be raised if the command returns a non-zero status
|
|
128
|
+
|
|
129
|
+
If output is True then stdout and stderr are both output as if stdout=True and stderr=True (in addition to
|
|
130
|
+
any other values passed in those parameters)
|
|
131
|
+
|
|
132
|
+
If ansi_clean is True then ANSI control sequences are removed from any streams in stdout and stderr but
|
|
133
|
+
not from the console output.
|
|
134
|
+
|
|
135
|
+
If continuous is True then output is processed character-by-character (normally for use when output=True)
|
|
136
|
+
TODO: Currently this causes the ansi_clean option to be ignored
|
|
137
|
+
|
|
138
|
+
The return value is a tuple consisting of the status code, captured stdout (if any) and captured
|
|
139
|
+
stderr (if any).
|
|
140
|
+
|
|
141
|
+
Will raise OSError if the command could not be run and RunError if exception is True and the
|
|
142
|
+
command returned a non-zero status code. """
|
|
143
|
+
|
|
144
|
+
# If stdout/stderr are booleans then output to stdout/stderr if True, else discard output
|
|
145
|
+
|
|
146
|
+
if isinstance(stdout, bool):
|
|
147
|
+
stdout = sys.stdout if stdout else None
|
|
148
|
+
|
|
149
|
+
if isinstance(stderr, bool):
|
|
150
|
+
stderr = sys.stderr if stderr else None
|
|
151
|
+
|
|
152
|
+
# If stdout/stderr are not arrays then make them so
|
|
153
|
+
|
|
154
|
+
if not isinstance(stdout, list):
|
|
155
|
+
stdout = [stdout] if stdout else []
|
|
156
|
+
|
|
157
|
+
if not isinstance(stderr, list):
|
|
158
|
+
stderr = [stderr] if stderr else []
|
|
159
|
+
|
|
160
|
+
# If output is True then add stderr/out to the list of outputs
|
|
161
|
+
|
|
162
|
+
if output:
|
|
163
|
+
if sys.stdout not in stdout:
|
|
164
|
+
stdout.append(sys.stdout)
|
|
165
|
+
|
|
166
|
+
if sys.stderr not in stderr:
|
|
167
|
+
stderr.append(sys.stderr)
|
|
168
|
+
|
|
169
|
+
# Capture stdout/stderr to arrays unless asked not to
|
|
170
|
+
|
|
171
|
+
stdout_data = []
|
|
172
|
+
stderr_data = []
|
|
173
|
+
|
|
174
|
+
if return_stdout:
|
|
175
|
+
stdout.append(stdout_data)
|
|
176
|
+
|
|
177
|
+
if return_stderr:
|
|
178
|
+
stderr.append(stderr_data)
|
|
179
|
+
|
|
180
|
+
# If running via the shell then the command should be a string, otherwise
|
|
181
|
+
# it should be an array
|
|
182
|
+
|
|
183
|
+
if shell:
|
|
184
|
+
if not isinstance(command, str):
|
|
185
|
+
command = ' '.join(command)
|
|
186
|
+
else:
|
|
187
|
+
if isinstance(command, str):
|
|
188
|
+
command = shlex.split(command, comments=True)
|
|
189
|
+
|
|
190
|
+
# Use a pipe for stdout/stderr if are are capturing it
|
|
191
|
+
# and send it to /dev/null if we don't care about it at all.
|
|
192
|
+
|
|
193
|
+
if stdout == [sys.stdout] and not stderr:
|
|
194
|
+
stdout_stream = subprocess.STDOUT
|
|
195
|
+
stderr_stream = subprocess.DEVNULL
|
|
196
|
+
else:
|
|
197
|
+
stdout_stream = subprocess.PIPE if stdout else subprocess.DEVNULL
|
|
198
|
+
stderr_stream = subprocess.PIPE if stderr else subprocess.DEVNULL
|
|
199
|
+
|
|
200
|
+
# Run the command with no buffering and capturing output if we
|
|
201
|
+
# want it - this will raise OSError if there was a problem running
|
|
202
|
+
# the command.
|
|
203
|
+
|
|
204
|
+
cmd = subprocess.Popen(command,
|
|
205
|
+
bufsize=0,
|
|
206
|
+
stdout=stdout_stream,
|
|
207
|
+
stderr=stderr_stream,
|
|
208
|
+
text=True,
|
|
209
|
+
errors='ignore',
|
|
210
|
+
encoding='ascii',
|
|
211
|
+
shell=shell)
|
|
212
|
+
|
|
213
|
+
# Create threads to capture stderr and/or stdout if necessary
|
|
214
|
+
|
|
215
|
+
if stdout_stream == subprocess.PIPE:
|
|
216
|
+
stdout_thread = threading.Thread(target=capture_continuous if continuous else capture_output, args=(cmd, cmd.stdout, stdout, ansi_clean), daemon=True)
|
|
217
|
+
stdout_thread.start()
|
|
218
|
+
else:
|
|
219
|
+
stdout_thread = None
|
|
220
|
+
|
|
221
|
+
if stderr_stream == subprocess.PIPE:
|
|
222
|
+
stderr_thread = threading.Thread(target=capture_continuous if continuous else capture_output, args=(cmd, cmd.stderr, stderr, ansi_clean), daemon=True)
|
|
223
|
+
stderr_thread.start()
|
|
224
|
+
else:
|
|
225
|
+
stderr_thread = None
|
|
226
|
+
|
|
227
|
+
# Wait until the command terminates (and set the returncode)
|
|
228
|
+
|
|
229
|
+
if stdout_thread:
|
|
230
|
+
stdout_thread.join()
|
|
231
|
+
|
|
232
|
+
if stderr_thread:
|
|
233
|
+
stderr_thread.join()
|
|
234
|
+
|
|
235
|
+
cmd.wait()
|
|
236
|
+
|
|
237
|
+
# If the command failed, raise an exception (if required)
|
|
238
|
+
|
|
239
|
+
if exception and cmd.returncode:
|
|
240
|
+
if return_stderr:
|
|
241
|
+
raise RunError('\n'.join(stderr_data))
|
|
242
|
+
else:
|
|
243
|
+
raise RunError('Error %d running "%s"' % (cmd.returncode, (command if isinstance(command, str) else ' '.join(command))))
|
|
244
|
+
|
|
245
|
+
# Return status, stdout, stderr (the latter 2 may be empty if we did not capture data).
|
|
246
|
+
|
|
247
|
+
return {'status': cmd.returncode, 'stdout': stdout_data, 'stderr': stderr_data}
|
|
248
|
+
|
|
249
|
+
################################################################################
|
|
250
|
+
|
|
251
|
+
def run(command,
|
|
252
|
+
stdout=None, stderr=None,
|
|
253
|
+
shell=False,
|
|
254
|
+
output=None,
|
|
255
|
+
ansi_clean=False,
|
|
256
|
+
exception=True,
|
|
257
|
+
continuous=False):
|
|
258
|
+
""" Simple interface to the process() function
|
|
259
|
+
Has the same parameters, with the same defaults.
|
|
260
|
+
The return value is either the data output to stdout, if any
|
|
261
|
+
or the data output to stderr otherwise.
|
|
262
|
+
The status code is not returned, but the function will raise an exception
|
|
263
|
+
by default if it is non-zero """
|
|
264
|
+
|
|
265
|
+
result = process(command=command,
|
|
266
|
+
stdout=stdout, stderr=stderr,
|
|
267
|
+
return_stdout=True, return_stderr=True,
|
|
268
|
+
shell=shell,
|
|
269
|
+
output=output,
|
|
270
|
+
ansi_clean=ansi_clean,
|
|
271
|
+
exception=exception,
|
|
272
|
+
continuous=continuous)
|
|
273
|
+
|
|
274
|
+
return result['stdout'] if result['stdout'] else result['stderr']
|
|
275
|
+
|
|
276
|
+
################################################################################
|
|
277
|
+
|
|
278
|
+
def status(command, shell=False, output=False):
|
|
279
|
+
""" Alternative simple interface to the process() function
|
|
280
|
+
Just takes a command and the shell flag.
|
|
281
|
+
Runs the command without capturing the output
|
|
282
|
+
Optionally outputting both stdout and stderr
|
|
283
|
+
and returns the status code.
|
|
284
|
+
Will only raise an exception if the command could not be run. """
|
|
285
|
+
|
|
286
|
+
return process(command,
|
|
287
|
+
stdout=output,
|
|
288
|
+
stderr=output,
|
|
289
|
+
shell=shell,
|
|
290
|
+
exception=False)['status']
|
|
291
|
+
|
|
292
|
+
################################################################################
|
|
293
|
+
|
|
294
|
+
if __name__ == '__main__':
|
|
295
|
+
def test_run(cmd,
|
|
296
|
+
stdout=None, stderr=None,
|
|
297
|
+
return_stdout=True, return_stderr=True,
|
|
298
|
+
shell=False,
|
|
299
|
+
exception=True):
|
|
300
|
+
""" Test wrapper for the process() function. """
|
|
301
|
+
|
|
302
|
+
print('-' * 80)
|
|
303
|
+
print('Running: %s' % (cmd if isinstance(cmd, str) else ' '.join(cmd)))
|
|
304
|
+
|
|
305
|
+
result = process(cmd,
|
|
306
|
+
stdout=stdout, stderr=stderr,
|
|
307
|
+
return_stdout=return_stdout, return_stderr=return_stderr,
|
|
308
|
+
shell=shell,
|
|
309
|
+
exception=exception)
|
|
310
|
+
|
|
311
|
+
print('Status: %d' % result['status'])
|
|
312
|
+
|
|
313
|
+
def test():
|
|
314
|
+
""" Test code """
|
|
315
|
+
|
|
316
|
+
test_run('echo nothing')
|
|
317
|
+
|
|
318
|
+
test_run(['ls', '-l', 'run_jed'])
|
|
319
|
+
test_run(['ls -l run_*'], stdout=True, shell=True)
|
|
320
|
+
test_run('false', exception=False)
|
|
321
|
+
test_run('true', stdout=sys.stdout, exception=False)
|
|
322
|
+
test_run(['git', 'status'], stdout=sys.stdout, stderr=sys.stderr, exception=False)
|
|
323
|
+
|
|
324
|
+
test_run(['make'], stderr=sys.stderr, exception=False)
|
|
325
|
+
test_run(['make'], stdout=sys.stdout, stderr=[sys.stderr], exception=False)
|
|
326
|
+
test_run(['make'], stdout=True, exception=False)
|
|
327
|
+
test_run(['make'], stdout=sys.stdout, exception=False)
|
|
328
|
+
test_run(['make'], exception=False)
|
|
329
|
+
|
|
330
|
+
output = []
|
|
331
|
+
test_run('ls -l x*; sleep 1; echo "Bye!"', stderr=[sys.stderr, output], stdout=sys.stdout, shell=True, return_stdout=False)
|
|
332
|
+
print('Output=%s' % output)
|
|
333
|
+
|
|
334
|
+
test()
|