ecr.core._Runner

src/ecr/core/_Runner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import subprocess
import time
from enum import Enum
from typing import Dict, Optional, Tuple, Callable

from .. import log, ui
from ..types import CommandList
from ..ui import color


class RunResult(Enum):
    Success: int = 0
    Error: int = 1
    TimeOut: int = 2


class Runner:
    def __init__(self, proc: subprocess.Popen, io: str, timelimit: Optional[int] = None):
        self.proc: subprocess.Popen = proc
        self.timeLimit: Optional[int] = timelimit
        self.communicate = self.proc.communicate
        self.usedTime: float = 0
        self.isRunning: bool = False
        self.io: str = io
        self.canInput: bool = io[0] == "s"

    def terminate(self) -> None:
        self.proc.terminate()
        # os.kill(self.proc.pid, 9)
        self.isRunning = False
        self.proc.wait()

    def input(self, data: str)->None:
        self.proc.stdin.write(data.encode("utf-8"))
        self.proc.stdin.flush()

    def run(self)->Tuple[RunResult, Optional[int]]:
        self.isRunning = True
        isTimeout = False
        bg_time = time.time()
        try:
            # if self.io[1] != "s":  # not stdout
            self.communicate(timeout=self.timeLimit)
            # pylint: disable=W0105
            """else:  # stdout for async
                while not self.proc.poll() and self.isRunning:
                    if self.timeLimit != None and time.time() - bg_time > self.timeLimit:
                        raise subprocess.TimeoutExpired(
                            self.proc.cmd, self.timeLimit)
                    s = self.proc.stdout.readline().decode("utf-8")
                    safeOutput(s)
                if self.proc.poll() != 0 and self.isRunning:  # hit error
                    s = self.proc.stderr.read().decode("utf-8")
                    safeOutput(s)"""
        except subprocess.TimeoutExpired:
            isTimeout = True
            self.terminate()
        except KeyboardInterrupt:
            self.terminate()
        finally:
            ed_time = time.time()
            self.isRunning = False
            self.usedTime = ed_time - bg_time
        if isTimeout:
            return (RunResult.TimeOut, self.proc.returncode)
        if self.proc.returncode != 0:
            return (RunResult.Error, self.proc.returncode)
        return (RunResult.Success, self.proc.returncode)


def runCommands(io: str, commands: CommandList, variables: Dict[str, str], wdir: str, getSystemCommand: Callable[[str], str], inputFile: str, outputFile: str, defaultTimeLimit: Optional[int] = None, showLog: bool = True) -> bool:
    errf = color.useRed("×")
    passf = color.useGreen("√")
    isSuccess = True
    sumStep = len(commands)
    cwd = wdir
    console = ui.getConsole()
    for ind, bcmd in enumerate(commands):
        if not isSuccess:
            break
        cmd, timelimit = None, None
        if not isinstance(bcmd, str):
            cmd, timelimit = bcmd
        else:
            cmd, timelimit = bcmd, defaultTimeLimit
        _cmd = cmd.format(**variables)
        if showLog:
            console.write(
                "(", color.useYellow(str(ind+1)), f"/{sumStep}) ", _cmd, sep="")
        proc = None
        rresult, retcode = None, None
        runner = None
        try:
            rcmd = getSystemCommand(_cmd)
            if ind == sumStep - 1:  # last command
                if io[0] == "s":  # stdin
                    timelimit = None
                if showLog:
                    console.write("-"*20)
                proc = subprocess.Popen(
                    rcmd,
                    cwd=cwd,
                    stdin=None if io[0] == "s"
                    else open(inputFile, "r"),
                    stdout=None if io[1] == "s"
                    else open(outputFile, "w"),
                    stderr=None)
            else:
                proc = subprocess.Popen(
                    rcmd,
                    cwd=cwd,
                    stdin=None, stdout=None, stderr=None)

            runner = Runner(proc=proc, io=io, timelimit=timelimit)
            rresult, retcode = runner.run()
        except BaseException:
            log.errorWithException(f"Run command failed: {rcmd}")
            isSuccess = False
        finally:
            if ind == sumStep - 1:  # last command
                if showLog:
                    console.write("-"*20)
            if showLog:
                console.write(
                    "   ->",
                    passf if retcode == 0 else errf,
                    f"{round(runner.usedTime*1000)/1000}s")
            if rresult != RunResult.Success:
                if showLog:
                    console.write(
                        "(", color.useRed(str(ind + 1)), f"/{sumStep}) ",
                        _cmd, " -> ", color.useRed(str(retcode)), sep="", end=" ")
                    if rresult == RunResult.TimeOut:
                        console.write(color.useRed("Time out"))
                    else:
                        console.write()
                isSuccess = False
    return isSuccess