pyallel 1.3.4__tar.gz → 1.3.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyallel
3
- Version: 1.3.4
3
+ Version: 1.3.5
4
4
  Summary: Run and handle the output of multiple executables in pyallel (as in parallel)
5
5
  Home-page: https://github.com/Danthewaann/pyallel
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyallel"
3
- version = "1.3.4"
3
+ version = "1.3.5"
4
4
  description = "Run and handle the output of multiple executables in pyallel (as in parallel)"
5
5
  authors = ["Daniel Black <danielcrblack@gmail.com>"]
6
6
  license = "MIT"
@@ -13,11 +13,6 @@ class Colours:
13
13
  blue_bold: str = "\033[1;34m"
14
14
  red_bold: str = "\033[1;31m"
15
15
  yellow_bold: str = "\033[1;33m"
16
- clear_line: str = "\033[2K"
17
- clear_screen: str = "\033[2J"
18
- save_cursor: str = "\033[s"
19
- restore_cursor: str = "\033[u"
20
- up_line: str = "\033[1F"
21
16
  reset_colour: str = "\033[0m"
22
17
  dim_on: str = "\033[2m"
23
18
  dim_off: str = "\033[22m"
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib.metadata
4
+ import sys
5
+ import time
6
+ import traceback
7
+
8
+ from pyallel import constants
9
+ from pyallel.colours import Colours
10
+ from pyallel.errors import InvalidLinesModifierError
11
+ from pyallel.parser import Arguments, create_parser
12
+ from pyallel.printer import (
13
+ InteractiveConsolePrinter,
14
+ NonInteractiveConsolePrinter,
15
+ Printer,
16
+ )
17
+ from pyallel.process_group_manager import ProcessGroupManager
18
+
19
+
20
+ def entry_point(*args: str) -> int:
21
+ args = args or tuple(sys.argv[1:])
22
+ parser = create_parser()
23
+ parsed_args = parser.parse_args(args=args, namespace=Arguments())
24
+
25
+ if parsed_args.version:
26
+ my_version = importlib.metadata.version("pyallel")
27
+ print(my_version)
28
+ return 0
29
+
30
+ if not parsed_args.commands:
31
+ parser.print_help()
32
+ return 2
33
+
34
+ colours = Colours.from_colour(parsed_args.colour)
35
+ printer: Printer
36
+ if not parsed_args.interactive or not constants.IN_TTY:
37
+ printer = NonInteractiveConsolePrinter(colours, timer=parsed_args.timer)
38
+ else:
39
+ printer = InteractiveConsolePrinter(colours, timer=parsed_args.timer)
40
+
41
+ try:
42
+ process_group_manager = ProcessGroupManager.from_args(*parsed_args.commands)
43
+ except InvalidLinesModifierError as e:
44
+ print(f"{colours.red_bold}Error: {str(e)}{colours.reset_colour}")
45
+ return 1
46
+
47
+ try:
48
+ exit_code = run(process_group_manager, printer)
49
+ except Exception:
50
+ print(
51
+ f"{colours.red_bold}Error: {traceback.format_exc()}{colours.reset_colour}"
52
+ )
53
+ return 1
54
+
55
+ if exit_code == 1:
56
+ print(f"{colours.red_bold}\nFailed!{colours.reset_colour}")
57
+ elif exit_code == 0:
58
+ print(f"{colours.green_bold}\nDone!{colours.reset_colour}")
59
+
60
+ return exit_code
61
+
62
+
63
+ def run(process_group_manager: ProcessGroupManager, printer: Printer) -> int:
64
+ process_group_manager.run()
65
+ while True:
66
+ process_group_manager.stream()
67
+ printer.print(process_group_manager)
68
+
69
+ poll = process_group_manager.poll()
70
+ if poll is not None:
71
+ # If we still have new output to print after the process group manager has completed,
72
+ # make sure to print it here before continuing
73
+ if process_group_manager.stream().has_output():
74
+ printer.print(process_group_manager)
75
+
76
+ if poll > 0:
77
+ return poll
78
+
79
+ process_group_manager.run()
80
+ if not process_group_manager.next():
81
+ return 0
82
+
83
+ time.sleep(0.1)
84
+
85
+
86
+ if __name__ == "__main__":
87
+ sys.exit(entry_point())
@@ -1,20 +1,30 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
+ from typing import Protocol
4
5
 
5
6
  from pyallel import constants
6
7
  from pyallel.colours import Colours
7
- from pyallel.process import ProcessOutput
8
+ from pyallel.process import Process, ProcessOutput
8
9
  from pyallel.process_group import ProcessGroupOutput
10
+ from pyallel.process_group_manager import ProcessGroupManager
9
11
 
10
12
 
11
- class Printer:
13
+ class Printer(Protocol):
14
+ def print(self, process_group_manager: ProcessGroupManager) -> None:
15
+ """Print output obtained from the provided process group manager.
16
+
17
+ Args:
18
+ process_group_manager: manager to obtain output from
19
+ """
20
+
21
+
22
+ class ConsolePrinter:
12
23
  def __init__(self, colours: Colours | None = None, timer: bool = False) -> None:
13
24
  self._colours = colours or Colours()
14
25
  self._timer = timer
15
26
  self._prefix = f"{self._colours.dim_on}=>{self._colours.dim_off} "
16
27
  self._icon = 0
17
- self._last_printed: list[tuple[bool, str, str]] = []
18
28
  self._to_print: list[tuple[bool, str, str]] = []
19
29
 
20
30
  def write(
@@ -33,34 +43,10 @@ class Printer:
33
43
  truncate_num = 6
34
44
  if truncate:
35
45
  columns = columns - truncate_num
36
- if get_num_lines(line, columns) > 1:
37
- line = truncate_line(line, columns)
46
+ if self.get_num_lines(line, columns) > 1:
47
+ line = self.truncate_line(line, columns)
38
48
  print(f"{prefix}{line}", end=end, flush=flush)
39
49
 
40
- def info(self, msg: str) -> None:
41
- self.write(
42
- f"{self._colours.white_bold}{msg}{self._colours.reset_colour}",
43
- include_prefix=False,
44
- )
45
-
46
- def ok(self, msg: str) -> None:
47
- self.write(
48
- f"{self._colours.green_bold}{msg}{self._colours.reset_colour}",
49
- include_prefix=False,
50
- )
51
-
52
- def warn(self, msg: str) -> None:
53
- self.write(
54
- f"{self._colours.yellow_bold}{msg}{self._colours.reset_colour}",
55
- include_prefix=False,
56
- )
57
-
58
- def error(self, msg: str) -> None:
59
- self.write(
60
- f"{self._colours.red_bold}{msg}{self._colours.reset_colour}",
61
- include_prefix=False,
62
- )
63
-
64
50
  def generate_process_output(
65
51
  self,
66
52
  output: ProcessOutput,
@@ -154,12 +140,12 @@ class Printer:
154
140
  if not output.process.end:
155
141
  end = time.perf_counter()
156
142
  elapsed = end - output.process.start
157
- timer = f"({format_time_taken(elapsed)})"
143
+ timer = f"({self.format_time_taken(elapsed)})"
158
144
 
159
145
  command = output.process.command
160
- if get_num_lines(output.process.command) > 1:
146
+ if self.get_num_lines(output.process.command) > 1:
161
147
  columns = constants.COLUMNS() - (len(msg) + len(timer) + 9)
162
- command = truncate_line(command, columns)
148
+ command = self.truncate_line(command, columns)
163
149
 
164
150
  out = f"{self._colours.white_bold}[{self._colours.reset_colour}{self._colours.blue_bold}{command}{self._colours.reset_colour}{self._colours.white_bold}]{self._colours.reset_colour}{colour} {msg} {icon}{self._colours.reset_colour}"
165
151
 
@@ -174,7 +160,7 @@ class Printer:
174
160
  interrupt_count: int = 0,
175
161
  tail_output: bool = True,
176
162
  ) -> list[tuple[bool, str, str]]:
177
- set_process_lines(output, interrupt_count)
163
+ self.set_process_lines(output, interrupt_count)
178
164
 
179
165
  for out in output.processes:
180
166
  self.generate_process_output(out, tail_output, append_newlines=True)
@@ -204,27 +190,124 @@ class Printer:
204
190
 
205
191
  return self._to_print
206
192
 
207
- def print_process_output(
193
+ def set_process_lines(
208
194
  self,
209
- output: ProcessOutput,
210
- tail_output: bool = False,
211
- include_cmd: bool = True,
212
- include_output: bool = True,
213
- include_progress: bool = True,
214
- include_timer: bool | None = None,
195
+ output: ProcessGroupOutput,
196
+ interrupt_count: int = 0,
197
+ lines: int = 0,
215
198
  ) -> None:
216
- for include_prefix, line, end in self.generate_process_output(
217
- output,
218
- tail_output,
219
- include_cmd,
220
- include_output,
221
- include_progress,
222
- include_timer,
223
- ):
224
- self.write(line, include_prefix, end)
199
+ lines = lines or constants.LINES() - 1
200
+ if interrupt_count:
201
+ lines -= 2
202
+
203
+ # Allocate lines to processes that have a fixed percentage of lines set
204
+ allocated_process_lines = lines // len(output.processes)
205
+ processes_with_dynamic_lines: list[ProcessOutput] = []
206
+ used_lines = 0
207
+ for process_output in output.processes:
208
+ # This process output doesn't have percentage_lines set, so skip it
209
+ if not process_output.process.percentage_lines:
210
+ processes_with_dynamic_lines.append(process_output)
211
+ continue
212
+
213
+ process_output.process.lines = int(
214
+ lines * process_output.process.percentage_lines
215
+ )
216
+ used_lines += process_output.process.lines
217
+
218
+ # Remove the used lines from the total available lines
219
+ lines -= used_lines
220
+
221
+ while lines:
222
+ # Calculate how many lines each process should have based on how many processes and lines are left
223
+ num_processes = len(processes_with_dynamic_lines) or 1
224
+ allocated_process_lines = lines // num_processes
225
+ processes_with_excess_output: list[ProcessOutput] = []
226
+ recalculate_lines = False
227
+ for process_output in processes_with_dynamic_lines:
228
+ # If the number of lines in this process output is less than how many terminal lines we would allocate it,
229
+ # Set it's allocated terminal lines to the exact number of lines in its output and remove this number from
230
+ # the total available terminal lines
231
+ if process_output.lines < allocated_process_lines:
232
+ process_output.process.lines = process_output.lines
233
+ lines -= process_output.process.lines
234
+ recalculate_lines = True
235
+ continue
236
+
237
+ processes_with_excess_output.append(process_output)
238
+
239
+ # We need to re-calcuate how many terminal lines we can allocate to each process if the output of at least one process
240
+ # contains less lines than what we would normally allocate it. This is done so we can allocate these extra lines to the
241
+ # other processes that contain more lines of output.
242
+ if recalculate_lines:
243
+ processes_with_dynamic_lines = processes_with_excess_output
244
+ else:
245
+ # All remaining processes exceed the number of terminal lines we will allocate them, so allocate them
246
+ # their terminal lines as normal and break out of the while loop
247
+ for process_output in processes_with_excess_output:
248
+ process_output.process.lines = allocated_process_lines
249
+ lines -= allocated_process_lines
250
+
251
+ # If there is any lines left, allocate them to the process that currently contains the most lines in its output, or
252
+ # allocate them to the first process if no process contains enough lines
253
+ if lines:
254
+ process_with_most_lines: ProcessOutput | None = None
255
+ most_lines = 0
256
+ for process_output in output.processes:
257
+ if process_output.process.lines > most_lines:
258
+ process_with_most_lines = process_output
259
+ most_lines = process_output.process.lines
260
+
261
+ if not process_with_most_lines:
262
+ output.processes[0].process.lines += lines
263
+ else:
264
+ process_with_most_lines.process.lines += lines
265
+
266
+ break
267
+
268
+ def get_num_lines(self, line: str, columns: int | None = None) -> int:
269
+ lines = 0
270
+ columns = columns or constants.COLUMNS()
271
+ line = constants.ANSI_ESCAPE.sub("", line)
272
+ length = len(line)
273
+ line_lines = 1
274
+ if length > columns:
275
+ line_lines = length // columns
276
+ remainder = length % columns
277
+ if remainder:
278
+ line_lines += 1
279
+ lines += 1 * line_lines
280
+ return lines
281
+
282
+ def truncate_line(self, line: str, columns: int | None = None) -> str:
283
+ columns = columns or constants.COLUMNS()
284
+ escaped_line = constants.ANSI_ESCAPE.sub("", line)
285
+ return "".join(escaped_line[:columns]) + "..."
286
+
287
+ def format_time_taken(self, time_taken: float) -> str:
288
+ time_taken = round(time_taken, 1)
289
+ seconds = time_taken % (24 * 3600)
290
+
291
+ return f"{seconds}s"
225
292
 
226
- # Force a flush otherwise lines that don't end in a newline character will not get printed as they are read
227
- print("", end="", flush=True)
293
+
294
+ class InteractiveConsolePrinter(ConsolePrinter):
295
+ def __init__(self, colours: Colours | None = None, timer: bool = False) -> None:
296
+ super().__init__(colours, timer)
297
+ self._last_printed: list[tuple[bool, str, str]] = []
298
+
299
+ def print(self, process_group_manager: ProcessGroupManager) -> None:
300
+ output = process_group_manager.get_cur_process_group_output()
301
+ self.print_progress_group_output(output, process_group_manager._interrupt_count)
302
+
303
+ poll = process_group_manager.poll()
304
+ if poll is not None:
305
+ self.clear_last_printed_lines()
306
+ self.reset()
307
+ self.print_progress_group_output(
308
+ output, process_group_manager._interrupt_count, tail_output=False
309
+ )
310
+ self.reset()
228
311
 
229
312
  def print_progress_group_output(
230
313
  self,
@@ -289,104 +372,48 @@ class Printer:
289
372
  self._to_print.clear()
290
373
 
291
374
 
292
- def set_process_lines(
293
- output: ProcessGroupOutput,
294
- interrupt_count: int = 0,
295
- lines: int = 0,
296
- ) -> None:
297
- lines = lines or constants.LINES() - 1
298
- if interrupt_count:
299
- lines -= 2
300
-
301
- # Allocate lines to processes that have a fixed percentage of lines set
302
- allocated_process_lines = lines // len(output.processes)
303
- processes_with_dynamic_lines: list[ProcessOutput] = []
304
- used_lines = 0
305
- for process_output in output.processes:
306
- # This process output doesn't have percentage_lines set, so skip it
307
- if not process_output.process.percentage_lines:
308
- processes_with_dynamic_lines.append(process_output)
309
- continue
310
-
311
- process_output.process.lines = int(
312
- lines * process_output.process.percentage_lines
313
- )
314
- used_lines += process_output.process.lines
315
-
316
- # Remove the used lines from the total available lines
317
- lines -= used_lines
318
-
319
- while lines:
320
- # Calculate how many lines each process should have based on how many processes and lines are left
321
- num_processes = len(processes_with_dynamic_lines) or 1
322
- allocated_process_lines = lines // num_processes
323
- processes_with_excess_output: list[ProcessOutput] = []
324
- recalculate_lines = False
325
- for process_output in processes_with_dynamic_lines:
326
- # If the number of lines in this process output is less than how many terminal lines we would allocate it,
327
- # Set it's allocated terminal lines to the exact number of lines in its output and remove this number from
328
- # the total available terminal lines
329
- if process_output.lines < allocated_process_lines:
330
- process_output.process.lines = process_output.lines
331
- lines -= process_output.process.lines
332
- recalculate_lines = True
333
- continue
334
-
335
- processes_with_excess_output.append(process_output)
336
-
337
- # We need to re-calcuate how many terminal lines we can allocate to each process if the output of at least one process
338
- # contains less lines than what we would normally allocate it. This is done so we can allocate these extra lines to the
339
- # other processes that contain more lines of output.
340
- if recalculate_lines:
341
- processes_with_dynamic_lines = processes_with_excess_output
342
- else:
343
- # All remaining processes exceed the number of terminal lines we will allocate them, so allocate them
344
- # their terminal lines as normal and break out of the while loop
345
- for process_output in processes_with_excess_output:
346
- process_output.process.lines = allocated_process_lines
347
- lines -= allocated_process_lines
348
-
349
- # If there is any lines left, allocate them to the process that currently contains the most lines in its output, or
350
- # allocate them to the first process if no process contains enough lines
351
- if lines:
352
- process_with_most_lines: ProcessOutput | None = None
353
- most_lines = 0
354
- for process_output in output.processes:
355
- if process_output.process.lines > most_lines:
356
- process_with_most_lines = process_output
357
- most_lines = process_output.process.lines
358
-
359
- if not process_with_most_lines:
360
- output.processes[0].process.lines += lines
375
+ class NonInteractiveConsolePrinter(ConsolePrinter):
376
+ def __init__(self, colours: Colours | None = None, timer: bool = False) -> None:
377
+ super().__init__(colours, timer)
378
+ self._current_process: Process | None = None
379
+
380
+ def print(self, process_group_manager: ProcessGroupManager) -> None:
381
+ outputs = process_group_manager.cur_output
382
+ for pg in outputs.process_group_outputs.values():
383
+ for output in pg.processes:
384
+ if self._current_process is None:
385
+ self._current_process = output.process
386
+ output = process_group_manager.get_process(output.id)
387
+ self.print_process_output(
388
+ output, include_progress=False, include_timer=False
389
+ )
390
+ elif self._current_process is not output.process:
391
+ continue
361
392
  else:
362
- process_with_most_lines.process.lines += lines
363
-
364
- break
365
-
393
+ self.print_process_output(output, include_cmd=False)
366
394
 
367
- def get_num_lines(line: str, columns: int | None = None) -> int:
368
- lines = 0
369
- columns = columns or constants.COLUMNS()
370
- line = constants.ANSI_ESCAPE.sub("", line)
371
- length = len(line)
372
- line_lines = 1
373
- if length > columns:
374
- line_lines = length // columns
375
- remainder = length % columns
376
- if remainder:
377
- line_lines += 1
378
- lines += 1 * line_lines
379
- return lines
395
+ if output.process.poll() is not None:
396
+ self.print_process_output(output, include_output=False)
397
+ self._current_process = None
380
398
 
399
+ def print_process_output(
400
+ self,
401
+ output: ProcessOutput,
402
+ tail_output: bool = False,
403
+ include_cmd: bool = True,
404
+ include_output: bool = True,
405
+ include_progress: bool = True,
406
+ include_timer: bool | None = None,
407
+ ) -> None:
408
+ for include_prefix, line, end in self.generate_process_output(
409
+ output,
410
+ tail_output,
411
+ include_cmd,
412
+ include_output,
413
+ include_progress,
414
+ include_timer,
415
+ ):
416
+ self.write(line, include_prefix, end)
381
417
 
382
- def truncate_line(line: str, columns: int | None = None) -> str:
383
- columns = columns or constants.COLUMNS()
384
- escaped_line = constants.ANSI_ESCAPE.sub("", line)
385
- return "".join(escaped_line[:columns]) + "..."
386
-
387
-
388
- def format_time_taken(time_taken: float) -> str:
389
- time_taken = round(time_taken, 1)
390
- seconds = time_taken % (24 * 3600)
391
-
392
- return f"{seconds}s"
418
+ # Force a flush otherwise lines that don't end in a newline character will not get printed as they are read
419
+ print("", end="", flush=True)
@@ -24,6 +24,14 @@ class ProcessGroupManagerOutput:
24
24
  else:
25
25
  self.process_group_outputs[key] = value
26
26
 
27
+ def has_output(self) -> bool:
28
+ for pg in self.process_group_outputs.values():
29
+ for process in pg.processes:
30
+ if process.data:
31
+ return True
32
+
33
+ return False
34
+
27
35
 
28
36
  class ProcessGroupManager:
29
37
  def __init__(self, process_groups: list[ProcessGroup]) -> None:
@@ -31,7 +39,7 @@ class ProcessGroupManager:
31
39
  self._interrupt_count = 0
32
40
  self._cur_process_group: ProcessGroup | None = None
33
41
  self._process_groups = process_groups
34
- self._output = ProcessGroupManagerOutput(
42
+ self._all_output = ProcessGroupManagerOutput(
35
43
  process_group_outputs={
36
44
  pg.id: ProcessGroupOutput(
37
45
  id=pg.id,
@@ -40,6 +48,7 @@ class ProcessGroupManager:
40
48
  for pg in self._process_groups
41
49
  }
42
50
  )
51
+ self.cur_output = ProcessGroupManagerOutput()
43
52
 
44
53
  def run(self) -> None:
45
54
  if self._process_groups:
@@ -62,18 +71,19 @@ class ProcessGroupManager:
62
71
  },
63
72
  )
64
73
 
65
- self._output.merge(output)
74
+ self._all_output.merge(output)
75
+ self.cur_output = output
66
76
 
67
77
  return output
68
78
 
69
79
  def get_cur_process_group_output(self) -> ProcessGroupOutput:
70
80
  if self._cur_process_group:
71
- return self._output.process_group_outputs[self._cur_process_group.id]
81
+ return self._all_output.process_group_outputs[self._cur_process_group.id]
72
82
 
73
83
  raise KeyError("no current process group output")
74
84
 
75
85
  def get_process(self, id: int) -> ProcessOutput:
76
- for pg in self._output.process_group_outputs.values():
86
+ for pg in self._all_output.process_group_outputs.values():
77
87
  for process in pg.processes:
78
88
  if process.id == id:
79
89
  return process
@@ -1,137 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import importlib.metadata
4
- import sys
5
- import traceback
6
- import time
7
-
8
- from pyallel import constants
9
- from pyallel.colours import Colours
10
- from pyallel.errors import InvalidLinesModifierError
11
- from pyallel.parser import Arguments, create_parser
12
- from pyallel.printer import Printer
13
- from pyallel.process_group_manager import ProcessGroupManager
14
-
15
-
16
- def run_interactive(
17
- process_group_manager: ProcessGroupManager, printer: Printer
18
- ) -> int:
19
- while True:
20
- process_group_manager.stream()
21
-
22
- output = process_group_manager.get_cur_process_group_output()
23
- printer.print_progress_group_output(
24
- output, process_group_manager._interrupt_count
25
- )
26
-
27
- poll = process_group_manager.poll()
28
- if poll is not None:
29
- printer.clear_last_printed_lines()
30
- printer.reset()
31
- printer.print_progress_group_output(
32
- output, process_group_manager._interrupt_count, tail_output=False
33
- )
34
-
35
- if poll > 0:
36
- return poll
37
-
38
- printer.reset()
39
- process_group_manager.run()
40
- if not process_group_manager.next():
41
- return 0
42
-
43
- time.sleep(0.1)
44
-
45
-
46
- def run_non_interactive(
47
- process_group_manager: ProcessGroupManager, printer: Printer
48
- ) -> int:
49
- current_process = None
50
-
51
- while True:
52
- outputs = process_group_manager.stream()
53
-
54
- for pg in outputs.process_group_outputs.values():
55
- for output in pg.processes:
56
- if current_process is None:
57
- current_process = output.process
58
- output = process_group_manager.get_process(output.id)
59
- printer.print_process_output(
60
- output, include_progress=False, include_timer=False
61
- )
62
- elif current_process is not output.process:
63
- continue
64
- else:
65
- printer.print_process_output(output, include_cmd=False)
66
-
67
- if output.process.poll() is not None:
68
- printer.print_process_output(output, include_output=False)
69
- current_process = None
70
-
71
- poll = process_group_manager.poll()
72
- if poll is not None:
73
- if poll > 0:
74
- return poll
75
-
76
- process_group_manager.run()
77
- if not process_group_manager.next():
78
- return 0
79
-
80
- time.sleep(0.1)
81
-
82
-
83
- def run(*args: str) -> int:
84
- parser = create_parser()
85
- parsed_args = parser.parse_args(args=args, namespace=Arguments())
86
-
87
- if parsed_args.version:
88
- my_version = importlib.metadata.version("pyallel")
89
- print(my_version)
90
- return 0
91
-
92
- if not parsed_args.commands:
93
- parser.print_help()
94
- return 2
95
-
96
- colours = Colours.from_colour(parsed_args.colour)
97
- printer = Printer(colours, timer=parsed_args.timer)
98
-
99
- interactive = True
100
- if not parsed_args.interactive:
101
- interactive = False
102
- elif not constants.IN_TTY:
103
- interactive = False
104
-
105
- message = None
106
- try:
107
- process_group_manager = ProcessGroupManager.from_args(*parsed_args.commands)
108
- process_group_manager.run()
109
-
110
- if interactive:
111
- exit_code = run_interactive(process_group_manager, printer)
112
- else:
113
- exit_code = run_non_interactive(process_group_manager, printer)
114
- except InvalidLinesModifierError as e:
115
- exit_code = 1
116
- message = str(e)
117
- except Exception:
118
- exit_code = 1
119
- message = traceback.format_exc()
120
-
121
- if exit_code == 1:
122
- if not message:
123
- printer.error("\nFailed!")
124
- else:
125
- printer.error(f"Error: {message}")
126
- elif exit_code == 0:
127
- printer.ok("\nDone!")
128
-
129
- return exit_code
130
-
131
-
132
- def entry_point() -> None:
133
- sys.exit(run(*sys.argv[1:]))
134
-
135
-
136
- if __name__ == "__main__":
137
- entry_point()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes