Skip to content

Commit 3ab7700

Browse files
committed
Allow direct call of helpers: shortcut to execute
1 parent 2cf6b8b commit 3ab7700

File tree

9 files changed

+136
-2
lines changed

9 files changed

+136
-2
lines changed

README.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@ This methods are almost the same for `SSHCleint` and `Subprocess`, except specif
167167
exception_class=CalledProcessError, # typing.Type[CalledProcessError]
168168
)
169169
170+
.. code-block:: python
171+
172+
result = helper( # Lazy way: instances are callable and uses `execute`.
173+
command, # type: str
174+
verbose=False, # type: bool
175+
timeout=1 * 60 * 60, # type: typing.Union[int, float, None]
176+
**kwargs
177+
)
178+
170179
If no STDOUT or STDERR required, it is possible to disable this FIFO pipes via `**kwargs` with flags `open_stdout=False` and `open_stderr=False`.
171180

172181
The next command level uses lower level and kwargs are forwarded, so expected exit codes are forwarded from `check_stderr`.

doc/source/SSHClient.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,21 @@ API: SSHClient and SSHAuth.
167167

168168
.. versionchanged:: 1.2.0 default timeout 1 hour
169169

170+
.. py:method:: __call__(command, verbose=False, timeout=1*60*60, **kwargs)
171+
172+
Execute command and wait for return code.
173+
174+
:param command: Command for execution
175+
:type command: ``str``
176+
:param verbose: Produce log.info records for command call and output
177+
:type verbose: ``bool``
178+
:param timeout: Timeout for command execution.
179+
:type timeout: ``typing.Union[int, float, None]``
180+
:rtype: ExecResult
181+
:raises ExecHelperTimeoutError: Timeout exceeded
182+
183+
.. versionadded:: 3.3.0
184+
170185
.. py:method:: check_call(command, verbose=False, timeout=1*60*60, error_info=None, expected=None, raise_on_err=True, *, exception_class=CalledProcessError, **kwargs)
171186
172187
Execute command and check for return code.

doc/source/Subprocess.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,22 @@ API: Subprocess
9090
.. versionchanged:: 1.2.0 default timeout 1 hour
9191
.. versionchanged:: 1.2.0 stdin data
9292

93+
.. py:method:: __call__(command, verbose=False, timeout=1*60*60, **kwargs)
94+
95+
Execute command and wait for return code.
96+
97+
:param command: Command for execution
98+
:type command: ``str``
99+
:param verbose: Produce log.info records for command call and output
100+
:type verbose: ``bool``
101+
:param timeout: Timeout for command execution.
102+
:type timeout: ``typing.Union[int, float, None]``
103+
:rtype: ExecResult
104+
:raises ExecHelperTimeoutError: Timeout exceeded
105+
106+
.. note:: stdin channel is closed after the input processing
107+
.. versionadded:: 3.3.0
108+
93109
.. py:method:: check_call(command, verbose=False, timeout=1*60*60, error_info=None, expected=None, raise_on_err=True, *, exception_class=CalledProcessError, **kwargs)
94110
95111
Execute command and check for return code.

exec_helpers/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@
5353
"async_api",
5454
)
5555

56-
try:
56+
try: # pragma: no cover
5757
__version__ = pkg_resources.get_distribution(__name__).version
58-
except pkg_resources.DistributionNotFound:
58+
except pkg_resources.DistributionNotFound: # pragma: no cover
5959
# package is not installed, try to get from SCM
6060
try:
6161
# noinspection PyPackageRequirements,PyUnresolvedReferences

exec_helpers/api.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,31 @@ def execute(
250250
self.logger.log(level=logging.INFO if verbose else logging.DEBUG, msg=message) # type: ignore
251251
return result
252252

253+
def __call__(
254+
self,
255+
command: str,
256+
verbose: bool = False,
257+
timeout: typing.Union[int, float, None] = constants.DEFAULT_TIMEOUT,
258+
**kwargs: typing.Any
259+
) -> exec_result.ExecResult:
260+
"""Execute command and wait for return code.
261+
262+
:param command: Command for execution
263+
:type command: str
264+
:param verbose: Produce log.info records for command call and output
265+
:type verbose: bool
266+
:param timeout: Timeout for command execution.
267+
:type timeout: typing.Union[int, float, None]
268+
:param kwargs: additional parameters for call.
269+
:type kwargs: typing.Any
270+
:return: Execution result
271+
:rtype: ExecResult
272+
:raises ExecHelperTimeoutError: Timeout exceeded
273+
274+
.. versionadded:: 3.3.0
275+
"""
276+
return self.execute(command=command, verbose=verbose, timeout=timeout, **kwargs)
277+
253278
def check_call(
254279
self,
255280
command: str,

exec_helpers/async_api/api.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,31 @@ async def execute( # type: ignore
171171
self.logger.log(level=logging.INFO if verbose else logging.DEBUG, msg=message) # type: ignore
172172
return result
173173

174+
async def __call__( # type: ignore
175+
self,
176+
command: str,
177+
verbose: bool = False,
178+
timeout: typing.Union[int, float, None] = constants.DEFAULT_TIMEOUT,
179+
**kwargs: typing.Any
180+
) -> exec_result.ExecResult:
181+
"""Execute command and wait for return code.
182+
183+
:param command: Command for execution
184+
:type command: str
185+
:param verbose: Produce log.info records for command call and output
186+
:type verbose: bool
187+
:param timeout: Timeout for command execution.
188+
:type timeout: typing.Union[int, float, None]
189+
:param kwargs: additional parameters for call.
190+
:type kwargs: typing.Any
191+
:return: Execution result
192+
:rtype: ExecResult
193+
:raises ExecHelperTimeoutError: Timeout exceeded
194+
195+
.. versionadded:: 3.3.0
196+
"""
197+
return await self.execute(command=command, verbose=verbose, timeout=timeout, **kwargs)
198+
174199
async def check_call( # type: ignore
175200
self,
176201
command: str,

test/async_api/test_subprocess.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,13 @@ async def test_008_check_stderr_no_raise(execute, exec_result, logger) -> None:
343343
)
344344
== exec_result
345345
)
346+
347+
348+
async def test_009_call(create_subprocess_shell, logger, exec_result, run_parameters) -> None:
349+
runner = exec_helpers.async_api.Subprocess()
350+
res = await runner(command, stdin=run_parameters["stdin"])
351+
assert isinstance(res, exec_helpers.async_api.ExecResult)
352+
assert res == exec_result
353+
assert logger.mock_calls[-1] == mock.call.log(
354+
level=logging.DEBUG, msg="Command {result.cmd!r} exit code: {result.exit_code!s}".format(result=res)
355+
)

test/test_ssh_client_execute.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,24 @@ def test_010_execute_together_expected(ssh, ssh2, execute_async, exec_result, ru
506506
)
507507
)
508508
assert results == {(host, port): exec_result, (host2, port): exec_result}
509+
510+
511+
def test_011_call(ssh, ssh_transport_channel, exec_result, run_parameters) -> None:
512+
kwargs = {}
513+
if "get_pty" in run_parameters:
514+
kwargs["get_pty"] = run_parameters["get_pty"]
515+
if "width" in run_parameters:
516+
kwargs["width"] = run_parameters["width"]
517+
if "height" in run_parameters:
518+
kwargs["height"] = run_parameters["height"]
519+
520+
res = ssh(
521+
command,
522+
stdin=run_parameters["stdin"],
523+
open_stdout=run_parameters["open_stdout"],
524+
open_stderr=run_parameters["open_stderr"],
525+
**kwargs
526+
)
527+
assert isinstance(res, exec_helpers.ExecResult)
528+
assert res == exec_result
529+
ssh_transport_channel.assert_has_calls((mock.call.status_event.is_set(),))

test/test_subprocess.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,3 +317,16 @@ def test_008_check_stderr_no_raise(execute, exec_result, logger) -> None:
317317
runner.check_stderr(command, stdin=exec_result.stdin, expected=[exec_result.exit_code], raise_on_err=False)
318318
== exec_result
319319
)
320+
321+
322+
def test_009_call(popen, logger, exec_result, run_parameters) -> None:
323+
runner = exec_helpers.Subprocess()
324+
res = runner(
325+
command,
326+
stdin=run_parameters["stdin"],
327+
open_stdout=run_parameters["open_stdout"],
328+
open_stderr=run_parameters["open_stderr"],
329+
)
330+
assert isinstance(res, exec_helpers.ExecResult)
331+
assert res == exec_result
332+
popen().wait.assert_called_once_with(timeout=default_timeout)

0 commit comments

Comments
 (0)