libPyshell 0.2.1__tar.gz → 0.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: libPyshell
3
- Version: 0.2.1
3
+ Version: 0.3.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.3.0 (2024-02-01)
41
+ * uniform treatment when capturing stdout and stderr. This lead to changes to RunResult
42
+ and RunError which are slightly backwards incompatible.
43
+
40
44
  * 0.2.0 (2024-01-29)
41
45
  * Better static type information
42
46
 
@@ -26,6 +26,10 @@ magicFiles = run(['grep', 'magic'] + files, captureStdout=splitLines, onError='i
26
26
 
27
27
  ## Changelog
28
28
 
29
+ * 0.3.0 (2024-02-01)
30
+ * uniform treatment when capturing stdout and stderr. This lead to changes to RunResult
31
+ and RunError which are slightly backwards incompatible.
32
+
29
33
  * 0.2.0 (2024-01-29)
30
34
  * Better static type information
31
35
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: libPyshell
3
- Version: 0.2.1
3
+ Version: 0.3.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.3.0 (2024-02-01)
41
+ * uniform treatment when capturing stdout and stderr. This lead to changes to RunResult
42
+ and RunError which are slightly backwards incompatible.
43
+
40
44
  * 0.2.0 (2024-01-29)
41
45
  * Better static type information
42
46
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  from distutils.core import setup
4
4
 
5
- VERSION = '0.2.1'
5
+ VERSION = '0.3.0'
6
6
 
7
7
  with open("README.md", "r", encoding="utf-8") as fh:
8
8
  long_description = fh.read()
@@ -94,11 +94,12 @@ class RunResult:
94
94
  attribute `stdout` contains the output printed in stdout (only if `run`
95
95
  was invoked with `captureStdout=True`).
96
96
  """
97
- def __init__(self, stdout: Any, exitcode: int):
97
+ def __init__(self, stdout: Any, stderr: Any, exitcode: int):
98
98
  self.stdout = stdout
99
+ self.stderr = stderr
99
100
  self.exitcode = exitcode
100
101
  def __repr__(self):
101
- return 'RunResult(exitcode=%d, stdout=%r) '% (self.exitcode, self.stdout)
102
+ return 'RunResult(exitcode=%d, stdout=%r, stderr=%r)' % (self.exitcode, self.stdout, self.stderr)
102
103
  def __eq__(self, other: Any):
103
104
  if type(other) is type(self):
104
105
  return self.__dict__ == other.__dict__
@@ -127,14 +128,19 @@ class RunError(ShellError):
127
128
  """
128
129
  def __init__(self, cmd: Union[str, list[str]],
129
130
  exitcode: int,
130
- stderr: Union[str,bytes,None]=None):
131
+ stdout: Union[str,bytes],
132
+ stderr: Union[str,bytes]):
131
133
  self.cmd = cmd
132
134
  self.exitcode = exitcode
133
135
  self.stderr = stderr
136
+ self.stdout = stdout
134
137
  msg = 'Command ' + repr(self.cmd) + " failed with exit code " + str(self.exitcode)
135
138
  if stderr:
136
139
  msg = msg + '\nstderr:\n' + str(stderr)
137
140
  super(RunError, self).__init__(msg)
141
+ def __repr__(self):
142
+ return 'RunError(cmd=%r, exitcode=%d, stdout=%r, stderr=%r)' % \
143
+ (self.cmd, self.exitcode, self.stdout, self.stderr)
138
144
 
139
145
  def splitOn(splitter: str) -> Callable[[str], list[str]]:
140
146
  """Return a function that splits a string on the given splitter string.
@@ -224,25 +230,29 @@ def run(cmd: Union[list[str], str],
224
230
 
225
231
  Starting with Python 3.5, the `subprocess` module defines a similar function.
226
232
 
227
- >>> run('/bin/echo foo') == RunResult(exitcode=0, stdout='')
228
- True
229
- >>> run('/bin/echo -n foo', captureStdout=True) == RunResult(exitcode=0, stdout='foo')
230
- True
231
- >>> run('/bin/echo -n foo', captureStdout=lambda s: s + 'X') == \
232
- RunResult(exitcode=0, stdout='fooX')
233
- True
234
- >>> run('/bin/echo foo', captureStdout=False) == RunResult(exitcode=0, stdout='')
235
- True
236
- >>> run('cat', captureStdout=True, input='blub') == RunResult(exitcode=0, stdout='blub')
237
- True
233
+ >>> run('/bin/echo foo')
234
+ RunResult(exitcode=0, stdout='', stderr='')
235
+ >>> run('/bin/echo -n foo', captureStdout=True)
236
+ RunResult(exitcode=0, stdout='foo', stderr='')
237
+ >>> run('/bin/echo -n foo', captureStdout=lambda s: s + 'X')
238
+ RunResult(exitcode=0, stdout='fooX', stderr='')
239
+ >>> run('/bin/echo foo', captureStdout=False)
240
+ RunResult(exitcode=0, stdout='', stderr='')
241
+ >>> run('cat', captureStdout=True, input='blub')
242
+ RunResult(exitcode=0, stdout='blub', stderr='')
238
243
  >>> try:
239
- ... run('false')
244
+ ... run('/bin/echo -n foo 1>&2; /bin/echo -n bar; false', captureStdout=True, captureStderr=True)
240
245
  ... raise 'exception expected'
241
- ... except RunError:
242
- ... pass
246
+ ... except RunError as e:
247
+ ... print(repr(e))
243
248
  ...
244
- >>> run('false', onError='ignore') == RunResult(exitcode=1, stdout='')
245
- True
249
+ RunError(cmd='/bin/echo -n foo 1>&2; /bin/echo -n bar; false', exitcode=1, stdout='bar', stderr='foo')
250
+ >>> run('false', onError='ignore')
251
+ RunResult(exitcode=1, stdout='', stderr='')
252
+ >>> run('/bin/echo -n foo; /bin/echo -n bar 1>&2', captureStdout=True, captureStderr=True)
253
+ RunResult(exitcode=0, stdout='foo', stderr='bar')
254
+ >>> run('/bin/echo -n foo 1>&2; /bin/echo -n bar', captureStderr=lambda s: s + 'X')
255
+ RunResult(exitcode=0, stdout='', stderr='fooX')
246
256
  """
247
257
  if type(cmd) != str and type(cmd) != list:
248
258
  raise ShellError('cmd parameter must be a string or a list')
@@ -253,9 +263,7 @@ def run(cmd: Union[list[str], str],
253
263
  decodeErrorsStdout = decodeErrors
254
264
  if decodeErrorsStderr is None:
255
265
  decodeErrorsStderr = decodeErrors
256
- stdoutIsFileLike = isinstance(captureStdout, int) or isinstance(captureStdout, IO)
257
- stdoutIsProcFun = not stdoutIsFileLike and isinstance(captureStdout, Callable)
258
- shouldReturnStdout = (stdoutIsProcFun or
266
+ shouldReturnStdout = (isinstance(captureStdout, Callable) or
259
267
  (type(captureStdout) == bool and captureStdout))
260
268
  stdout: _FILE = None
261
269
  if shouldReturnStdout:
@@ -301,26 +309,25 @@ def run(cmd: Union[list[str], str],
301
309
  cwd=cwd, env=popenEnv
302
310
  )
303
311
  (stdoutData, stderrData) = pipe.communicate(input=inputBytes)
304
- if stdoutData and encoding != 'raw':
305
- stdoutData = stdoutData.decode(encoding, errors=decodeErrorsStdout)
306
- if stderrData and encoding != 'raw':
307
- stderrData = stderrData.decode(encoding, errors=decodeErrorsStderr)
312
+ stdoutData = massageOutput(stdoutData, encoding, decodeErrorsStdout, captureStdout)
313
+ stderrData = massageOutput(stderrData, encoding, decodeErrorsStderr, captureStderr)
308
314
  exitcode = pipe.returncode
309
315
  if onError == 'raise' and exitcode != 0:
310
- d = stderrData
311
- if stderrToStdout:
312
- d = stdoutData
313
- err = RunError(cmd, exitcode, d)
316
+ err = RunError(cmd, exitcode, stdoutData, stderrData)
314
317
  raise err
315
318
  if onError == 'die' and exitcode != 0:
316
319
  sys.exit(exitcode)
317
- stdoutRes = stdoutData
318
- if not stdoutRes:
319
- stdoutRes = ''
320
- if not stdoutIsFileLike and isinstance(captureStdout, Callable) and \
321
- isinstance(stdoutData, str):
322
- stdoutRes = captureStdout(stdoutData)
323
- return RunResult(stdoutRes, exitcode)
320
+ return RunResult(stdoutData, stderrData, exitcode)
321
+
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
324
331
 
325
332
  # the quote function is stolen from https://hg.python.org/cpython/file/3.5/Lib/shlex.py
326
333
  _find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
File without changes
File without changes
File without changes