38 | 38 |
#
|
39 | 39 |
# Returns:
|
40 | 40 |
#
|
41 | |
# ReturnObject(
|
|
41 |
# subprocess.CompletedProcess(
|
42 | 42 |
# args=('/bin/ls', '/some/path/containing spaces'),
|
43 | 43 |
# returncode=2,
|
44 | 44 |
# stdout=b'',
|
45 | 45 |
# stderr=b'/bin/ls: cannot access /some/path/containing spaces: No such file or directory\n'
|
46 | 46 |
# )
|
47 | |
#
|
48 | |
# which also offers (albeit deprecated)
|
49 | |
#
|
50 | |
# result.runtime == 0.12990689277648926
|
51 | |
# result.time_end == '2017-11-12 19:54:49 GMT'
|
52 | |
# result.time_start == '2017-11-12 19:54:49 GMT'
|
53 | |
# result.timeout == False
|
54 | 47 |
|
55 | 48 |
__version__ = "2.3.1"
|
56 | 49 |
|
|
306 | 299 |
return command
|
307 | 300 |
|
308 | 301 |
|
309 | |
class ReturnObject(subprocess.CompletedProcess):
|
310 | |
"""
|
311 | |
A subprocess.CompletedProcess-like object containing the executed
|
312 | |
command, stdout and stderr (both as bytestrings), and the exitcode.
|
313 | |
The check_returncode() function raises an exception if the process
|
314 | |
exited with a non-zero exit code.
|
315 | |
"""
|
316 | |
|
317 | |
def __init__(self, exitcode=None, command=None, stdout=None, stderr=None, **kw):
|
318 | |
super().__init__(
|
319 | |
args=command, returncode=exitcode, stdout=stdout, stderr=stderr
|
320 | |
)
|
321 | |
self._extras = {
|
322 | |
"timeout": kw.get("timeout"),
|
323 | |
"runtime": kw.get("runtime"),
|
324 | |
"time_start": kw.get("time_start"),
|
325 | |
"time_end": kw.get("time_end"),
|
326 | |
}
|
327 | |
|
328 | |
def __getitem__(self, key):
|
329 | |
warnings.warn(
|
330 | |
"dictionary access to a procrunner return object is deprecated",
|
331 | |
DeprecationWarning,
|
332 | |
stacklevel=2,
|
333 | |
)
|
334 | |
if key in self._extras:
|
335 | |
return self._extras[key]
|
336 | |
if not hasattr(self, key):
|
337 | |
raise KeyError(f"Unknown attribute {key}")
|
338 | |
return getattr(self, key)
|
339 | |
|
340 | |
def __eq__(self, other):
|
341 | |
"""Override equality operator to account for added fields"""
|
342 | |
if type(other) is type(self):
|
343 | |
return self.__dict__ == other.__dict__
|
344 | |
return False
|
345 | |
|
346 | |
def __hash__(self):
|
347 | |
"""This object is not immutable, so mark it as unhashable"""
|
348 | |
return None
|
349 | |
|
350 | |
@property
|
351 | |
def cmd(self):
|
352 | |
warnings.warn(
|
353 | |
"procrunner return object .cmd is deprecated, use .args",
|
354 | |
DeprecationWarning,
|
355 | |
stacklevel=2,
|
356 | |
)
|
357 | |
return self.args
|
358 | |
|
359 | |
@property
|
360 | |
def command(self):
|
361 | |
warnings.warn(
|
362 | |
"procrunner return object .command is deprecated, use .args",
|
363 | |
DeprecationWarning,
|
364 | |
stacklevel=2,
|
365 | |
)
|
366 | |
return self.args
|
367 | |
|
368 | |
@property
|
369 | |
def exitcode(self):
|
370 | |
warnings.warn(
|
371 | |
"procrunner return object .exitcode is deprecated, use .returncode",
|
372 | |
DeprecationWarning,
|
373 | |
stacklevel=2,
|
374 | |
)
|
375 | |
return self.returncode
|
376 | |
|
377 | |
@property
|
378 | |
def timeout(self):
|
379 | |
warnings.warn(
|
380 | |
"procrunner return object .timeout is deprecated",
|
381 | |
DeprecationWarning,
|
382 | |
stacklevel=2,
|
383 | |
)
|
384 | |
return self._extras["timeout"]
|
385 | |
|
386 | |
@property
|
387 | |
def runtime(self):
|
388 | |
warnings.warn(
|
389 | |
"procrunner return object .runtime is deprecated",
|
390 | |
DeprecationWarning,
|
391 | |
stacklevel=2,
|
392 | |
)
|
393 | |
return self._extras["runtime"]
|
394 | |
|
395 | |
@property
|
396 | |
def time_start(self):
|
397 | |
warnings.warn(
|
398 | |
"procrunner return object .time_start is deprecated",
|
399 | |
DeprecationWarning,
|
400 | |
stacklevel=2,
|
401 | |
)
|
402 | |
return self._extras["time_start"]
|
403 | |
|
404 | |
@property
|
405 | |
def time_end(self):
|
406 | |
warnings.warn(
|
407 | |
"procrunner return object .time_end is deprecated",
|
408 | |
DeprecationWarning,
|
409 | |
stacklevel=2,
|
410 | |
)
|
411 | |
return self._extras["time_end"]
|
412 | |
|
413 | |
def update(self, dictionary):
|
414 | |
self._extras.update(dictionary)
|
415 | |
|
416 | |
|
417 | 302 |
def _deprecate_argument_calling(f):
|
418 | 303 |
@functools.wraps(f)
|
419 | 304 |
def wrapper(*args, **kwargs):
|
|
444 | 329 |
win32resolve=True,
|
445 | 330 |
working_directory=None,
|
446 | 331 |
raise_timeout_exception=False,
|
447 | |
):
|
|
332 |
) -> subprocess.CompletedProcess:
|
448 | 333 |
"""
|
449 | 334 |
Run an external process.
|
450 | 335 |
|
|
479 | 364 |
as a subprocess.CompletedProcess object.
|
480 | 365 |
"""
|
481 | 366 |
|
482 | |
time_start = time.strftime("%Y-%m-%d %H:%M:%S GMT", time.gmtime())
|
483 | 367 |
logger.debug("Starting external process: %s", command)
|
484 | 368 |
|
485 | 369 |
if stdin is None:
|
|
654 | 538 |
cmd=command, timeout=timeout, output=stdout, stderr=stderr
|
655 | 539 |
)
|
656 | 540 |
|
657 | |
time_end = time.strftime("%Y-%m-%d %H:%M:%S GMT", time.gmtime())
|
658 | |
result = ReturnObject(
|
659 | |
exitcode=p.returncode,
|
660 | |
command=command,
|
661 | |
stdout=stdout,
|
662 | |
stderr=stderr,
|
663 | |
timeout=timeout_encountered,
|
664 | |
runtime=runtime,
|
665 | |
time_start=time_start,
|
666 | |
time_end=time_end,
|
|
541 |
return subprocess.CompletedProcess(
|
|
542 |
args=command, returncode=p.returncode, stdout=stdout, stderr=stderr
|
667 | 543 |
)
|
668 | |
if stdin is not None:
|
669 | |
result.update(
|
670 | |
{
|
671 | |
"stdin_bytes_sent": stdin.bytes_sent(),
|
672 | |
"stdin_bytes_remain": stdin.bytes_remaining(),
|
673 | |
}
|
674 | |
)
|
675 | |
|
676 | |
return result
|