Coretex
process.py
1 # Copyright (C) 2023 Coretex LLC
2 
3 # This file is part of Coretex.ai
4 
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14 
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <https://www.gnu.org/licenses/>.
17 
18 from typing import List, Optional, Tuple
19 from pathlib import Path
20 
21 import logging
22 import subprocess
23 
24 
25 def logProcessOutput(output: bytes, level: int) -> None:
26  decoded = output.decode("UTF-8")
27 
28  for line in decoded.split("\n"):
29  # skip empty lines
30  if line.strip() == "":
31  continue
32 
33  # ignoring type for now, has to be fixed in coretexpylib
34  logging.getLogger("coretexpylib").log(level, line)
35 
36 
37 class CommandException(Exception):
38  pass
39 
40 
41 def command(
42  args: List[str],
43  ignoreStdout: bool = False,
44  ignoreStderr: bool = False,
45  shell: bool = False,
46  check: bool = True
47 ) -> Tuple[int, str, str]:
48 
49  process = subprocess.Popen(
50  args,
51  shell = shell,
52  cwd = Path.cwd(),
53  stdout = subprocess.PIPE,
54  stderr = subprocess.PIPE
55  )
56 
57  stdOutStr = ""
58  stdErrStr = ""
59 
60  returnCode: Optional[int] = None
61 
62  stdout = process.stdout
63  stderr = process.stderr
64 
65  if stdout is None and not ignoreStdout:
66  commandArgs = " ".join(args)
67  raise ValueError(f">> [Coretex] Something went wrong while trying to execute \"{commandArgs}\"")
68 
69  while (returnCode := process.poll()) is None:
70  if stdout is not None:
71  line = stdout.readline()
72  stdOutStr = "\n".join([stdOutStr, line.decode("utf-8")])
73  if not ignoreStdout:
74  logProcessOutput(line, logging.INFO)
75 
76  if stderr is None and not ignoreStderr:
77  commandArgs = " ".join(args)
78  raise ValueError(f">> [Coretex] Something went wrong while trying to execute \"{commandArgs}\"")
79 
80  if stderr is not None:
81  lines = stderr.readlines()
82  stdErrStr = b"".join(lines).decode("utf-8")
83  if not ignoreStderr:
84  logProcessOutput(b"".join(lines), logging.WARNING if returnCode == 0 else logging.FATAL)
85 
86  if returnCode != 0 and check:
87  commandArgs = " ".join(args)
88  raise CommandException(f">> [Coretex] Failed to execute command \"{commandArgs}\". Exit code \"{returnCode}\"")
89 
90  return returnCode, stdOutStr, stdErrStr