|
from asyncio import create_subprocess_exec, subprocess |
|
|
|
|
|
class Terminal: |
|
""" |
|
Class for running terminal commands asynchronously. |
|
|
|
Methods: |
|
|
|
run(commands: str) |
|
commands: Terminal Commands. |
|
Returns Process id (int) |
|
|
|
terminate(pid: int) |
|
pid: Process id returned in `run` method. |
|
Returns True if terminated else False (bool) |
|
|
|
output(pid: int) |
|
pid: Process id returned in `run` method. |
|
Returns Output of process (str) |
|
|
|
error(pid: int) |
|
pid: Process id returned in `run` method. |
|
Returns Error of process (str) |
|
""" |
|
|
|
def __init__(self) -> None: |
|
self._processes = {} |
|
|
|
@staticmethod |
|
def _to_str(data: bytes) -> str: |
|
return data.decode("utf-8").strip() |
|
|
|
async def run(self, *args) -> int: |
|
process = await create_subprocess_exec( |
|
*args, stdout=subprocess.PIPE, stderr=subprocess.PIPE |
|
) |
|
pid = process.pid |
|
self._processes[pid] = process |
|
return pid |
|
|
|
def terminate(self, pid: int) -> bool: |
|
try: |
|
self._processes.pop(pid) |
|
self._processes[pid].kill() |
|
return True |
|
except KeyError: |
|
return False |
|
|
|
async def output(self, pid: int) -> str: |
|
output = [] |
|
while True: |
|
out = self._to_str(await self._processes[pid].stdout.readline()) |
|
if not out: |
|
break |
|
output.append(out) |
|
return "\n".join(output) |
|
|
|
async def error(self, pid: int) -> str: |
|
error = [] |
|
while True: |
|
err = self._to_str(await self._processes[pid].stderr.readline()) |
|
if not err: |
|
break |
|
error.append(err) |
|
return "\n".join(error) |
|
|
|
@property |
|
def _auto_remove_processes(self) -> None: |
|
while self._processes: |
|
for proc in self._processes.keys(): |
|
if proc.returncode is not None: |
|
try: |
|
self._processes.pop(proc) |
|
except KeyError: |
|
pass |
|
|