Skip to content
Snippets Groups Projects
Commit 16464a96 authored by Piotr Maślanka's avatar Piotr Maślanka
Browse files

2.11.2, added read_nowait

parent c24e5e2d
No related branches found
No related tags found
No related merge requests found
...@@ -4,3 +4,4 @@ ...@@ -4,3 +4,4 @@
* added `step` to `smart_enumerate` * added `step` to `smart_enumerate`
* reserve more memory in case of MemoryError * reserve more memory in case of MemoryError
* added `call_in_separate_thread` * added `call_in_separate_thread`
* added `read_nowait`
...@@ -7,3 +7,7 @@ available, so that you don't need to worry about ...@@ -7,3 +7,7 @@ available, so that you don't need to worry about
the buffer overflowing and such. the buffer overflowing and such.
.. autofunction:: satella.processes.call_and_return_stdout .. autofunction:: satella.processes.call_and_return_stdout
This is a subprocess helper:
.. autofunction:: satella.processes.read_nowait
...@@ -17,7 +17,8 @@ def call_in_separate_thread(*t_args, **t_kwargs): ...@@ -17,7 +17,8 @@ def call_in_separate_thread(*t_args, **t_kwargs):
""" """
Decorator to mark given routine as callable in a separate thread. Decorator to mark given routine as callable in a separate thread.
The routine will return a Future that is waitable to get the result of the function The decorated routine will return a Future that is waitable to get the result
(or the exception) of the function
The arguments given here will be passed to thread's constructor. The arguments given here will be passed to thread's constructor.
""" """
......
...@@ -3,17 +3,25 @@ import threading ...@@ -3,17 +3,25 @@ import threading
import typing as tp import typing as tp
from satella.coding.recast_exceptions import silence_excs from satella.coding.recast_exceptions import silence_excs
from .coding.concurrent import call_in_separate_thread
from .exceptions import ProcessFailed from .exceptions import ProcessFailed
__all__ = ['call_and_return_stdout'] __all__ = ['call_and_return_stdout', 'read_nowait']
@call_in_separate_thread(daemon=True)
@silence_excs((IOError, OSError)) @silence_excs((IOError, OSError))
def _read_nowait(process: subprocess.Popen, output_list: tp.List[str]) -> None: def read_nowait(process: subprocess.Popen, output_list: tp.List[str]):
""" """
To be launched as a daemon thread. This reads stdout and appends it's entries to a list. This spawns a thread to read given process' stdout and append it to a list, in
This should finish as soon as the process exits or closes it's stdout. order to prevent buffer filling up completely.
To retrieve entire stdout after process finishes do
>>> ''.join(list)
This thread will terminate automatically after the process closes it's stdout or finishes.
""" """
while True: while True:
with silence_excs(subprocess.TimeoutExpired): with silence_excs(subprocess.TimeoutExpired):
...@@ -54,11 +62,7 @@ def call_and_return_stdout(args: tp.Union[str, tp.List[str]], ...@@ -54,11 +62,7 @@ def call_and_return_stdout(args: tp.Union[str, tp.List[str]],
stdout_list = [] stdout_list = []
proc = subprocess.Popen(args, **kwargs) proc = subprocess.Popen(args, **kwargs)
reader_thread = threading.Thread(name='stdout reader', fut = read_nowait(proc, stdout_list)
target=_read_nowait,
args=(proc, stdout_list),
daemon=True)
reader_thread.start()
try: try:
proc.wait(timeout=timeout) proc.wait(timeout=timeout)
...@@ -67,7 +71,7 @@ def call_and_return_stdout(args: tp.Union[str, tp.List[str]], ...@@ -67,7 +71,7 @@ def call_and_return_stdout(args: tp.Union[str, tp.List[str]],
proc.wait() proc.wait()
raise TimeoutError('Process did not complete within %s seconds' % (timeout, )) raise TimeoutError('Process did not complete within %s seconds' % (timeout, ))
finally: finally:
reader_thread.join() fut.result()
if encoding is None: if encoding is None:
result = b''.join(stdout_list) result = b''.join(stdout_list)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment