libPyshell 0.4.0__tar.gz → 0.5.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.4.0/libPyshell.egg-info → libpyshell-0.5.0}/PKG-INFO +16 -2
- {libPyshell-0.4.0 → libpyshell-0.5.0}/README.md +7 -0
- {libPyshell-0.4.0 → libpyshell-0.5.0/libPyshell.egg-info}/PKG-INFO +16 -2
- {libPyshell-0.4.0 → libpyshell-0.5.0}/setup.py +1 -1
- {libPyshell-0.4.0 → libpyshell-0.5.0}/src/__init__.py +47 -22
- {libPyshell-0.4.0 → libpyshell-0.5.0}/LICENSE +0 -0
- {libPyshell-0.4.0 → libpyshell-0.5.0}/MANIFEST.in +0 -0
- {libPyshell-0.4.0 → libpyshell-0.5.0}/libPyshell.egg-info/SOURCES.txt +0 -0
- {libPyshell-0.4.0 → libpyshell-0.5.0}/libPyshell.egg-info/dependency_links.txt +0 -0
- {libPyshell-0.4.0 → libpyshell-0.5.0}/libPyshell.egg-info/top_level.txt +0 -0
- {libPyshell-0.4.0 → libpyshell-0.5.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: libPyshell
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -8,6 +8,13 @@ Author-email: stefan.wehr@gmail.com
|
|
|
8
8
|
Requires-Python: >=3.9
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
+
Dynamic: author
|
|
12
|
+
Dynamic: author-email
|
|
13
|
+
Dynamic: description
|
|
14
|
+
Dynamic: description-content-type
|
|
15
|
+
Dynamic: home-page
|
|
16
|
+
Dynamic: requires-python
|
|
17
|
+
Dynamic: summary
|
|
11
18
|
|
|
12
19
|
# Pyshell
|
|
13
20
|
|
|
@@ -37,6 +44,13 @@ magicFiles = run(['grep', 'magic'] + files, captureStdout=splitLines, onError='i
|
|
|
37
44
|
|
|
38
45
|
## Changelog
|
|
39
46
|
|
|
47
|
+
* 0.5.0 (2025-03-11)
|
|
48
|
+
* support of timeouts for run
|
|
49
|
+
|
|
50
|
+
* 0.4.1 (2024-09-12)
|
|
51
|
+
* fix capture handling
|
|
52
|
+
* add failOnError option to rm commands
|
|
53
|
+
|
|
40
54
|
* 0.4.0 (2024-03-20)
|
|
41
55
|
* re-implement run in terms of subprocess.run. This fixes a bug that caused stdout to
|
|
42
56
|
disappear.
|
|
@@ -26,6 +26,13 @@ magicFiles = run(['grep', 'magic'] + files, captureStdout=splitLines, onError='i
|
|
|
26
26
|
|
|
27
27
|
## Changelog
|
|
28
28
|
|
|
29
|
+
* 0.5.0 (2025-03-11)
|
|
30
|
+
* support of timeouts for run
|
|
31
|
+
|
|
32
|
+
* 0.4.1 (2024-09-12)
|
|
33
|
+
* fix capture handling
|
|
34
|
+
* add failOnError option to rm commands
|
|
35
|
+
|
|
29
36
|
* 0.4.0 (2024-03-20)
|
|
30
37
|
* re-implement run in terms of subprocess.run. This fixes a bug that caused stdout to
|
|
31
38
|
disappear.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: libPyshell
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -8,6 +8,13 @@ Author-email: stefan.wehr@gmail.com
|
|
|
8
8
|
Requires-Python: >=3.9
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
+
Dynamic: author
|
|
12
|
+
Dynamic: author-email
|
|
13
|
+
Dynamic: description
|
|
14
|
+
Dynamic: description-content-type
|
|
15
|
+
Dynamic: home-page
|
|
16
|
+
Dynamic: requires-python
|
|
17
|
+
Dynamic: summary
|
|
11
18
|
|
|
12
19
|
# Pyshell
|
|
13
20
|
|
|
@@ -37,6 +44,13 @@ magicFiles = run(['grep', 'magic'] + files, captureStdout=splitLines, onError='i
|
|
|
37
44
|
|
|
38
45
|
## Changelog
|
|
39
46
|
|
|
47
|
+
* 0.5.0 (2025-03-11)
|
|
48
|
+
* support of timeouts for run
|
|
49
|
+
|
|
50
|
+
* 0.4.1 (2024-09-12)
|
|
51
|
+
* fix capture handling
|
|
52
|
+
* add failOnError option to rm commands
|
|
53
|
+
|
|
40
54
|
* 0.4.0 (2024-03-20)
|
|
41
55
|
* re-implement run in terms of subprocess.run. This fixes a bug that caused stdout to
|
|
42
56
|
disappear.
|
|
@@ -197,7 +197,7 @@ def _handleCapture(capture: CaptureType) -> Optional[_FILE]:
|
|
|
197
197
|
return subprocess.PIPE
|
|
198
198
|
elif callable(capture):
|
|
199
199
|
return subprocess.PIPE
|
|
200
|
-
elif capture
|
|
200
|
+
elif capture == False:
|
|
201
201
|
return None
|
|
202
202
|
else:
|
|
203
203
|
return capture
|
|
@@ -227,7 +227,8 @@ def run(cmd: Union[list[str], str],
|
|
|
227
227
|
decodeErrors: str='replace',
|
|
228
228
|
decodeErrorsStdout: Optional[str]=None,
|
|
229
229
|
decodeErrorsStderr: Optional[str]=None,
|
|
230
|
-
encodeErrorsStdin: Optional[str]=None
|
|
230
|
+
encodeErrorsStdin: Optional[str]=None,
|
|
231
|
+
timeout: Optional[int]=None
|
|
231
232
|
) -> RunResult:
|
|
232
233
|
"""Runs the given command.
|
|
233
234
|
|
|
@@ -258,7 +259,8 @@ def run(cmd: Union[list[str], str],
|
|
|
258
259
|
* `decodeErrors`: how to handle decoding/encoding errors on stdout and stderr and stdin.
|
|
259
260
|
* `decodeErrorsStdout` and `decodeErrorsStderr` and `encodeErrorsStdin`: overwrite the value
|
|
260
261
|
of decodeErrors for stdout or stderr or stdin
|
|
261
|
-
|
|
262
|
+
* `timeout`: an optional timeout value in seconds. If a timeout occurs, the exit code will
|
|
263
|
+
be 124 (as for the unix timeout command)
|
|
262
264
|
Returns:
|
|
263
265
|
a `RunResult` value, given access to the captured stdout of the child process (if it was
|
|
264
266
|
captured at all) and to the exit code of the child process.
|
|
@@ -315,11 +317,20 @@ def run(cmd: Union[list[str], str],
|
|
|
315
317
|
if _PYSHELL_DEBUG:
|
|
316
318
|
_debug(f'subprocess.run({cmd}, shell={shell}, input={input}, stdout={stdout}, ' \
|
|
317
319
|
f'stderr={stderr}, cwd={cwd}, env={runEnv})')
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
320
|
+
try:
|
|
321
|
+
res = subprocess.run(cmd, shell=shell, input=input, stdout=stdout, stderr=stderr, cwd=cwd,
|
|
322
|
+
env=runEnv, timeout=timeout)
|
|
323
|
+
out = res.stdout
|
|
324
|
+
err = res.stderr
|
|
325
|
+
exitcode = res.returncode
|
|
326
|
+
hadTimeout = False
|
|
327
|
+
except subprocess.TimeoutExpired as e:
|
|
328
|
+
out = e.stdout
|
|
329
|
+
err = e.stderr
|
|
330
|
+
exitcode = 124
|
|
331
|
+
hadTimeout = True
|
|
332
|
+
stdoutData = _massageOutput(out, encoding, decodeErrorsStdout or decodeErrors, captureStdout)
|
|
333
|
+
stderrData = _massageOutput(err, encoding, decodeErrorsStderr or decodeErrors, captureStderr)
|
|
323
334
|
if onError == 'raise' and exitcode != 0:
|
|
324
335
|
err = RunError(cmd, exitcode, stdoutData, stderrData)
|
|
325
336
|
raise err
|
|
@@ -430,13 +441,15 @@ def removeFile(path: str):
|
|
|
430
441
|
|
|
431
442
|
def cp(src: str, target: str):
|
|
432
443
|
"""
|
|
433
|
-
Copy `src` to `target`.
|
|
444
|
+
Copy `src` to `target`. Behaves like the cp shell command.
|
|
434
445
|
|
|
435
446
|
* If `src` is a file and `target` is a file: overwrites `target`.
|
|
436
447
|
* If `src` is a file and `target` is a dirname: places the copy in directory `target`,
|
|
437
448
|
with the basename of `src.
|
|
438
|
-
* If `src` is a directory then `target` must also be a directory:
|
|
439
|
-
the `src` directory (*not* its content) to `target`.
|
|
449
|
+
* If `src` is a directory and `target` exists, then `target` must also be a directory:
|
|
450
|
+
copies the `src` directory (*not* its content) to `target`.
|
|
451
|
+
* If `src` is a directory and `target` does not exist: copies the `src` directory
|
|
452
|
+
and names the copy `target`.
|
|
440
453
|
"""
|
|
441
454
|
if isFile(src):
|
|
442
455
|
if isDir(target):
|
|
@@ -511,22 +524,34 @@ class workingDir:
|
|
|
511
524
|
cd(self.old_dir)
|
|
512
525
|
return False # reraise expection
|
|
513
526
|
|
|
514
|
-
def rm(path: str, force: bool=False):
|
|
527
|
+
def rm(path: str, force: bool=False, failOnError: bool=True):
|
|
515
528
|
"""
|
|
516
529
|
Remove the file at `path`.
|
|
517
530
|
"""
|
|
518
531
|
if force and not exists(path):
|
|
519
532
|
return
|
|
520
|
-
|
|
533
|
+
try:
|
|
534
|
+
os.remove(path)
|
|
535
|
+
except Exception as e:
|
|
536
|
+
if failOnError:
|
|
537
|
+
raise
|
|
538
|
+
else:
|
|
539
|
+
sys.stderr.write(str(e) + '\n')
|
|
521
540
|
|
|
522
|
-
def rmdir(d: str, recursive: bool=False):
|
|
541
|
+
def rmdir(d: str, recursive: bool=False, failOnError: bool=True):
|
|
523
542
|
"""
|
|
524
543
|
Remove directory `d`. Set `recursive=True` if the directory is not empty.
|
|
525
544
|
"""
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
545
|
+
try:
|
|
546
|
+
if recursive:
|
|
547
|
+
shutil.rmtree(d)
|
|
548
|
+
else:
|
|
549
|
+
os.rmdir(d)
|
|
550
|
+
except Exception as e:
|
|
551
|
+
if failOnError:
|
|
552
|
+
raise
|
|
553
|
+
else:
|
|
554
|
+
sys.stderr.write(str(e) + '\n')
|
|
530
555
|
|
|
531
556
|
# See https://stackoverflow.com/questions/9741351/how-to-find-exit-code-or-reason-when-atexit-callback-is-called-in-python
|
|
532
557
|
class _ExitHooks(object):
|
|
@@ -581,7 +606,7 @@ def _registerAtExit(action: Any, mode: AtExitMode):
|
|
|
581
606
|
def mkTempFile(suffix: str='', prefix: str='',
|
|
582
607
|
dir:Optional[str]=None,
|
|
583
608
|
deleteAtExit:AtExitMode=True):
|
|
584
|
-
"""Create a temporary file.
|
|
609
|
+
"""Create a temporary file name.
|
|
585
610
|
|
|
586
611
|
`deleteAtExit` controls if and how the file is deleted once the shell sript terminates.
|
|
587
612
|
It has one of the following values.
|
|
@@ -596,7 +621,7 @@ def mkTempFile(suffix: str='', prefix: str='',
|
|
|
596
621
|
if deleteAtExit:
|
|
597
622
|
def action():
|
|
598
623
|
if isFile(f):
|
|
599
|
-
rm(f)
|
|
624
|
+
rm(f, failOnError=False)
|
|
600
625
|
_registerAtExit(action, deleteAtExit)
|
|
601
626
|
return f
|
|
602
627
|
|
|
@@ -610,7 +635,7 @@ def mkTempDir(suffix: str='', prefix: str='tmp',
|
|
|
610
635
|
if deleteAtExit:
|
|
611
636
|
def action():
|
|
612
637
|
if isDir(d):
|
|
613
|
-
rmdir(d, True)
|
|
638
|
+
rmdir(d, recursive=True, failOnError=False)
|
|
614
639
|
_registerAtExit(action, deleteAtExit)
|
|
615
640
|
return d
|
|
616
641
|
|
|
@@ -646,7 +671,7 @@ class tempDir:
|
|
|
646
671
|
return False # reraise
|
|
647
672
|
if self.delete:
|
|
648
673
|
if isDir(self.dir_to_delete):
|
|
649
|
-
rmdir(self.dir_to_delete, recursive=True)
|
|
674
|
+
rmdir(self.dir_to_delete, recursive=True, failOnError=False)
|
|
650
675
|
return False # reraise expection
|
|
651
676
|
|
|
652
677
|
def ls(d: str, *globs: str) -> list[str]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|