libPyshell 0.3.0__tar.gz → 0.4.0__tar.gz
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.
- {libPyshell-0.3.0/libPyshell.egg-info → libPyshell-0.4.0}/PKG-INFO +5 -1
- {libPyshell-0.3.0 → libPyshell-0.4.0}/README.md +4 -0
- {libPyshell-0.3.0 → libPyshell-0.4.0/libPyshell.egg-info}/PKG-INFO +5 -1
- {libPyshell-0.3.0 → libPyshell-0.4.0}/setup.py +1 -1
- {libPyshell-0.3.0 → libPyshell-0.4.0}/src/__init__.py +65 -67
- {libPyshell-0.3.0 → libPyshell-0.4.0}/LICENSE +0 -0
- {libPyshell-0.3.0 → libPyshell-0.4.0}/MANIFEST.in +0 -0
- {libPyshell-0.3.0 → libPyshell-0.4.0}/libPyshell.egg-info/SOURCES.txt +0 -0
- {libPyshell-0.3.0 → libPyshell-0.4.0}/libPyshell.egg-info/dependency_links.txt +0 -0
- {libPyshell-0.3.0 → libPyshell-0.4.0}/libPyshell.egg-info/top_level.txt +0 -0
- {libPyshell-0.3.0 → libPyshell-0.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: libPyshell
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Support for writing shell scripts in Python
|
|
5
5
|
Home-page: https://github.com/skogsbaer/libPyshell
|
|
6
6
|
Author: Stefan Wehr
|
|
@@ -37,6 +37,10 @@ magicFiles = run(['grep', 'magic'] + files, captureStdout=splitLines, onError='i
|
|
|
37
37
|
|
|
38
38
|
## Changelog
|
|
39
39
|
|
|
40
|
+
* 0.4.0 (2024-03-20)
|
|
41
|
+
* re-implement run in terms of subprocess.run. This fixes a bug that caused stdout to
|
|
42
|
+
disappear.
|
|
43
|
+
|
|
40
44
|
* 0.3.0 (2024-02-01)
|
|
41
45
|
* uniform treatment when capturing stdout and stderr. This lead to changes to RunResult
|
|
42
46
|
and RunError which are slightly backwards incompatible.
|
|
@@ -26,6 +26,10 @@ magicFiles = run(['grep', 'magic'] + files, captureStdout=splitLines, onError='i
|
|
|
26
26
|
|
|
27
27
|
## Changelog
|
|
28
28
|
|
|
29
|
+
* 0.4.0 (2024-03-20)
|
|
30
|
+
* re-implement run in terms of subprocess.run. This fixes a bug that caused stdout to
|
|
31
|
+
disappear.
|
|
32
|
+
|
|
29
33
|
* 0.3.0 (2024-02-01)
|
|
30
34
|
* uniform treatment when capturing stdout and stderr. This lead to changes to RunResult
|
|
31
35
|
and RunError which are slightly backwards incompatible.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: libPyshell
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Support for writing shell scripts in Python
|
|
5
5
|
Home-page: https://github.com/skogsbaer/libPyshell
|
|
6
6
|
Author: Stefan Wehr
|
|
@@ -37,6 +37,10 @@ magicFiles = run(['grep', 'magic'] + files, captureStdout=splitLines, onError='i
|
|
|
37
37
|
|
|
38
38
|
## Changelog
|
|
39
39
|
|
|
40
|
+
* 0.4.0 (2024-03-20)
|
|
41
|
+
* re-implement run in terms of subprocess.run. This fixes a bug that caused stdout to
|
|
42
|
+
disappear.
|
|
43
|
+
|
|
40
44
|
* 0.3.0 (2024-02-01)
|
|
41
45
|
* uniform treatment when capturing stdout and stderr. This lead to changes to RunResult
|
|
42
46
|
and RunError which are slightly backwards incompatible.
|
|
@@ -50,7 +50,7 @@ except:
|
|
|
50
50
|
|
|
51
51
|
DEV_NULL = _devNull
|
|
52
52
|
|
|
53
|
-
_FILE = Union[int, IO[Any]
|
|
53
|
+
_FILE = Union[int, IO[Any]]
|
|
54
54
|
|
|
55
55
|
atexit.register(lambda: DEV_NULL.close())
|
|
56
56
|
|
|
@@ -179,19 +179,55 @@ def splitLines(s: str) -> list[str]:
|
|
|
179
179
|
else:
|
|
180
180
|
return s.split('\n')
|
|
181
181
|
|
|
182
|
+
def _decode(input: Union[str, bytes, None], encoding: str, errors: str) -> Optional[bytes]:
|
|
183
|
+
inputBytes: Optional[bytes] = None
|
|
184
|
+
if input and isinstance(input, str):
|
|
185
|
+
if encoding != 'raw':
|
|
186
|
+
inputBytes = input.encode(encoding, errors)
|
|
187
|
+
else:
|
|
188
|
+
raise ValueError('Given str object as input, but encoding is raw')
|
|
189
|
+
elif input:
|
|
190
|
+
inputBytes = input
|
|
191
|
+
return inputBytes
|
|
192
|
+
|
|
193
|
+
CaptureType = Union[bool, Callable[[str], Any], _FILE, None]
|
|
194
|
+
|
|
195
|
+
def _handleCapture(capture: CaptureType) -> Optional[_FILE]:
|
|
196
|
+
if capture == True:
|
|
197
|
+
return subprocess.PIPE
|
|
198
|
+
elif callable(capture):
|
|
199
|
+
return subprocess.PIPE
|
|
200
|
+
elif capture is None:
|
|
201
|
+
return None
|
|
202
|
+
else:
|
|
203
|
+
return capture
|
|
204
|
+
|
|
205
|
+
def _massageOutput(data: Any, encoding: str, decodeErrors: Optional[str],
|
|
206
|
+
capture: CaptureType):
|
|
207
|
+
if not decodeErrors:
|
|
208
|
+
decodeErrors = 'strict'
|
|
209
|
+
if data and encoding != 'raw':
|
|
210
|
+
data = data.decode(encoding, errors=decodeErrors)
|
|
211
|
+
if not data:
|
|
212
|
+
data = ''
|
|
213
|
+
if isinstance(capture, Callable) and isinstance(data, str):
|
|
214
|
+
data = capture(data)
|
|
215
|
+
return data
|
|
216
|
+
|
|
182
217
|
def run(cmd: Union[list[str], str],
|
|
183
218
|
onError: Literal['raise', 'die', 'ignore']='raise',
|
|
184
219
|
input: Union[str, bytes, None]=None,
|
|
185
220
|
encoding: str='utf-8',
|
|
186
|
-
captureStdout: Union[bool,Callable[[str], Any],_FILE]=False,
|
|
187
|
-
captureStderr: Union[bool,Callable[[str], Any],_FILE]=False,
|
|
221
|
+
captureStdout: Union[bool, Callable[[str], Any], _FILE, None]=False,
|
|
222
|
+
captureStderr: Union[bool, Callable[[str], Any], _FILE, None]=False,
|
|
188
223
|
stderrToStdout: bool=False,
|
|
189
224
|
cwd: Optional[str]=None,
|
|
190
225
|
env: Optional[Dict[str, str]]=None,
|
|
191
226
|
freshEnv: Optional[Dict[str, str]]=None,
|
|
192
227
|
decodeErrors: str='replace',
|
|
193
228
|
decodeErrorsStdout: Optional[str]=None,
|
|
194
|
-
decodeErrorsStderr: Optional[str]=None
|
|
229
|
+
decodeErrorsStderr: Optional[str]=None,
|
|
230
|
+
encodeErrorsStdin: Optional[str]=None
|
|
195
231
|
) -> RunResult:
|
|
196
232
|
"""Runs the given command.
|
|
197
233
|
|
|
@@ -217,10 +253,11 @@ def run(cmd: Union[list[str], str],
|
|
|
217
253
|
* `stderrToStdout`: should stderr be sent to stdout?
|
|
218
254
|
* `cwd`: working directory
|
|
219
255
|
* `env`: dictionary with additional environment variables.
|
|
220
|
-
* `freshEnv`: dictionary with a completely fresh environment.
|
|
221
|
-
|
|
222
|
-
* `
|
|
223
|
-
|
|
256
|
+
* `freshEnv`: dictionary with a completely fresh environment. If `env` is also given, then
|
|
257
|
+
`freshEnv` is ignored.
|
|
258
|
+
* `decodeErrors`: how to handle decoding/encoding errors on stdout and stderr and stdin.
|
|
259
|
+
* `decodeErrorsStdout` and `decodeErrorsStderr` and `encodeErrorsStdin`: overwrite the value
|
|
260
|
+
of decodeErrors for stdout or stderr or stdin
|
|
224
261
|
|
|
225
262
|
Returns:
|
|
226
263
|
a `RunResult` value, given access to the captured stdout of the child process (if it was
|
|
@@ -228,7 +265,8 @@ def run(cmd: Union[list[str], str],
|
|
|
228
265
|
|
|
229
266
|
Raises: a `RunError` if `onError='raise'` and the command terminates with a non-zero exit code.
|
|
230
267
|
|
|
231
|
-
Starting with Python 3.5, the `subprocess` module defines a similar function.
|
|
268
|
+
Starting with Python 3.5, the `subprocess` module defines a similar function. This function
|
|
269
|
+
is just a wrapper for it.
|
|
232
270
|
|
|
233
271
|
>>> run('/bin/echo foo')
|
|
234
272
|
RunResult(exitcode=0, stdout='', stderr='')
|
|
@@ -254,64 +292,34 @@ def run(cmd: Union[list[str], str],
|
|
|
254
292
|
>>> run('/bin/echo -n foo 1>&2; /bin/echo -n bar', captureStderr=lambda s: s + 'X')
|
|
255
293
|
RunResult(exitcode=0, stdout='', stderr='fooX')
|
|
256
294
|
"""
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
cmd = cmd.replace('\x00', ' ')
|
|
261
|
-
cmd = cmd.replace('\n', ' ')
|
|
262
|
-
if decodeErrorsStdout is None:
|
|
263
|
-
decodeErrorsStdout = decodeErrors
|
|
264
|
-
if decodeErrorsStderr is None:
|
|
265
|
-
decodeErrorsStderr = decodeErrors
|
|
266
|
-
shouldReturnStdout = (isinstance(captureStdout, Callable) or
|
|
267
|
-
(type(captureStdout) == bool and captureStdout))
|
|
268
|
-
stdout: _FILE = None
|
|
269
|
-
if shouldReturnStdout:
|
|
270
|
-
stdout = subprocess.PIPE
|
|
271
|
-
elif isinstance(captureStdout, int) or isinstance(captureStdout, IO):
|
|
272
|
-
stdout = captureStdout
|
|
273
|
-
stdin = None
|
|
274
|
-
if input:
|
|
275
|
-
stdin = subprocess.PIPE
|
|
276
|
-
stderr = None
|
|
295
|
+
shell = isinstance(cmd, str)
|
|
296
|
+
input = _decode(input, encoding, encodeErrorsStdin or decodeErrors)
|
|
297
|
+
stdout = _handleCapture(captureStdout)
|
|
277
298
|
if stderrToStdout:
|
|
278
299
|
stderr = subprocess.STDOUT
|
|
279
|
-
|
|
280
|
-
stderr =
|
|
281
|
-
|
|
282
|
-
inputBytes: Optional[bytes] = None
|
|
283
|
-
if input and isinstance(input, str):
|
|
284
|
-
input_str = '<' + str(len(input)) + ' characters>'
|
|
285
|
-
if encoding != 'raw':
|
|
286
|
-
inputBytes = input.encode(encoding)
|
|
287
|
-
else:
|
|
288
|
-
raise ValueError('Given str object as input, but encoding is raw')
|
|
289
|
-
elif input:
|
|
290
|
-
inputBytes = input
|
|
291
|
-
_debug('Running command ' + repr(cmd) + ' with captureStdout=' + str(captureStdout) +
|
|
292
|
-
', onError=' + onError + ', input=' + input_str)
|
|
293
|
-
popenEnv = None
|
|
300
|
+
else:
|
|
301
|
+
stderr = _handleCapture(captureStderr)
|
|
302
|
+
runEnv = None
|
|
294
303
|
if env:
|
|
295
|
-
|
|
296
|
-
|
|
304
|
+
runEnv = os.environ.copy()
|
|
305
|
+
runEnv.update(env)
|
|
297
306
|
elif freshEnv:
|
|
298
|
-
|
|
307
|
+
runEnv = freshEnv.copy()
|
|
299
308
|
if env:
|
|
300
|
-
|
|
309
|
+
runEnv.update(env)
|
|
301
310
|
# Ensure correct ordering of outputs
|
|
302
311
|
if stdout is None:
|
|
303
312
|
sys.stdout.flush()
|
|
304
313
|
if stderr is None:
|
|
305
314
|
sys.stderr.flush()
|
|
306
|
-
|
|
307
|
-
cmd, shell=
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
exitcode = pipe.returncode
|
|
315
|
+
if _PYSHELL_DEBUG:
|
|
316
|
+
_debug(f'subprocess.run({cmd}, shell={shell}, input={input}, stdout={stdout}, ' \
|
|
317
|
+
f'stderr={stderr}, cwd={cwd}, env={runEnv})')
|
|
318
|
+
res = subprocess.run(cmd, shell=shell, input=input, stdout=stdout, stderr=stderr, cwd=cwd,
|
|
319
|
+
env=runEnv)
|
|
320
|
+
stdoutData = _massageOutput(res.stdout, encoding, decodeErrorsStdout or decodeErrors, captureStdout)
|
|
321
|
+
stderrData = _massageOutput(res.stderr, encoding, decodeErrorsStderr or decodeErrors, captureStderr)
|
|
322
|
+
exitcode = res.returncode
|
|
315
323
|
if onError == 'raise' and exitcode != 0:
|
|
316
324
|
err = RunError(cmd, exitcode, stdoutData, stderrData)
|
|
317
325
|
raise err
|
|
@@ -319,16 +327,6 @@ def run(cmd: Union[list[str], str],
|
|
|
319
327
|
sys.exit(exitcode)
|
|
320
328
|
return RunResult(stdoutData, stderrData, exitcode)
|
|
321
329
|
|
|
322
|
-
def massageOutput(data: Any, encoding: str, decodeErrors: str,
|
|
323
|
-
capture: Union[bool,Callable[[str], Any],_FILE]):
|
|
324
|
-
if data and encoding != 'raw':
|
|
325
|
-
data = data.decode(encoding, errors=decodeErrors)
|
|
326
|
-
if not data:
|
|
327
|
-
data = ''
|
|
328
|
-
if isinstance(capture, Callable) and isinstance(data, str):
|
|
329
|
-
data = capture(data)
|
|
330
|
-
return data
|
|
331
|
-
|
|
332
330
|
# the quote function is stolen from https://hg.python.org/cpython/file/3.5/Lib/shlex.py
|
|
333
331
|
_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
|
|
334
332
|
def quote(s: str) -> str:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|