diff --git a/CHANGELOG.md b/CHANGELOG.md index c4bad5f3f356b49ca62a8fa178d0797e04f0ae5f..e48ef6a22330f185b5653b91430cd3e4a255c640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # v2.7.16 * extended `Proxy` -* made `Immutable` utilize `__slots__` \ No newline at end of file +* made `Immutable` utilize `__slots__` +* added `satella.processes` \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 28879607644db10e12d55301a2add72de02a05bc..2a7504fcce99d7270da1154daf7dc8283a64c610 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,6 +26,7 @@ Visit the project's page at GitHub_! files time exceptions + processes Indices and tables diff --git a/docs/processes.rst b/docs/processes.rst new file mode 100644 index 0000000000000000000000000000000000000000..19ec90fa6223ec092672a38d3ef7d6cc3ae6dd24 --- /dev/null +++ b/docs/processes.rst @@ -0,0 +1,4 @@ +processes +========= + +.. autofunction:: satella.processes.call_and_return_stdout diff --git a/satella/__init__.py b/satella/__init__.py index 61ad89d8930c52561233eee1a38f3233119771c4..10939f7788af8fa9e6fed2588bd542abcc817572 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.7.16_a3' +__version__ = '2.7.16_a4' diff --git a/satella/exceptions.py b/satella/exceptions.py index a31e15362a95757d29dc7d4b5c73c172173a38dc..098c54ac27b635126e707914f3763a43cce54c04 100644 --- a/satella/exceptions.py +++ b/satella/exceptions.py @@ -4,7 +4,8 @@ import typing as tp __all__ = ['BaseSatellaError', 'ResourceLockingError', 'ResourceNotLocked', 'ResourceLocked', 'ConfigurationValidationError', 'ConfigurationError', 'ConfigurationSchemaError', 'PreconditionError', 'MetricAlreadyExists', 'BaseSatellaException', 'CustomException', - 'CodedCustomException', 'CodedCustomExceptionMetaclass', 'WouldWaitMore', 'LockIsHeld'] + 'CodedCustomException', 'CodedCustomExceptionMetaclass', 'WouldWaitMore', 'LockIsHeld', + 'ProcessFailed'] class CustomException(Exception): @@ -156,3 +157,10 @@ class LockIsHeld(ResourceLocked): def __init__(self, pid): self.pid = pid + + +class ProcessFailed(BaseSatellaError): + """A process finished with other result code that it was requested""" + + def __init__(self, rc: int): + self.rc = rc diff --git a/satella/processes.py b/satella/processes.py new file mode 100644 index 0000000000000000000000000000000000000000..cb8da0550a104dc92bf496bd2e3517cd352301f2 --- /dev/null +++ b/satella/processes.py @@ -0,0 +1,29 @@ +import subprocess +import typing as tp + +from .exceptions import ProcessFailed + + +def call_and_return_stdout(args: tp.Union[str, tp.List[str]], + expected_return_code: int = 0, **kwargs) -> tp.Union[bytes, str]: + """ + Call a process and return it's stdout. + + :param args: arguments to run the program with. If passed a string, it will be split on space. + :param expected_return_code: an expected return code of this process. 0 is the default. If process + returns anything else, ProcessFailed will be raise + :param ProcessFailed: process' result code was different from the requested + """ + if isinstance(args, str): + args = args.split(' ') + + kwargs['capture_output'] = True + + proc = subprocess.run(args, **kwargs) + + if proc.returncode != expected_return_code: + raise ProcessFailed(proc.returncode) + else: + return proc.stdout + + diff --git a/tests/test_processes.py b/tests/test_processes.py new file mode 100644 index 0000000000000000000000000000000000000000..2a1e2510e6fc2ee7a0a7ab98559916944b8db318 --- /dev/null +++ b/tests/test_processes.py @@ -0,0 +1,10 @@ +import unittest +import sys +from satella.processes import call_and_return_stdout + + +class TestProcesses(unittest.TestCase): + @unittest.skipIf('win' in sys.platform, 'Running on Windows') + def test_return_stdout(self): + output = call_and_return_stdout('cat /proc/meminfo', shell=True, encoding='utf8') + self.assertIn('MemTotal', output)