pyallel 1.2.0__tar.gz → 1.2.2__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.2.0
3
+ Version: 1.2.2
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
@@ -98,28 +98,49 @@ You can also build an executable with the following (executables will be written
98
98
  > The `arch=x86_64` values in the following code blocks can be replaced with `arch=aarch64` and
99
99
  > any other architecture that is supported by docker to build an executable for that given architecture
100
100
 
101
+ > [!NOTE]
102
+ > To build aarch64 binaries on an x86_64 host machine, you will need to run the following
103
+ > commands to setup qemu to allow this to work
104
+
105
+ ```bash
106
+ sudo apt-get install qemu binfmt-support qemu-user-static && \
107
+ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
108
+ ```
109
+
101
110
  #### Build for generic linux
102
111
 
103
112
  ```bash
104
- docker build --tag pyallel --build-arg 'arch=x86_64' . && docker run -e 'arch=x86_64' --rm --volume "$(pwd):/src" pyallel
113
+ docker build --tag pyallel --build-arg 'arch=x86_64' --build-arg "uid=$(id -u)" . && \
114
+ docker run -e 'arch=x86_64' --rm --volume "$(pwd):/src" pyallel
105
115
  ```
106
116
 
107
117
  #### Build for alpine linux
108
118
 
109
119
  ```bash
110
- docker build --tag pyallel-alpine --build-arg 'arch=x86_64' --file Dockerfile.alpine . && docker run -e 'arch=x86_64' --rm --volume "$(pwd):/src" pyallel-alpine
120
+ docker build --tag pyallel-alpine --build-arg 'arch=x86_64' --build-arg "uid=$(id -u)" --file Dockerfile.alpine . && \
121
+ docker run -e 'arch=x86_64' --rm --volume "$(pwd):/src" pyallel-alpine
111
122
  ```
112
123
 
113
124
  #### Build locally
114
125
 
115
126
  ```bash
116
- python -m venv .venv && source .venv/bin/activate && pip install . -r requirements_build.txt && ./build.sh
127
+ python -m venv .venv && \
128
+ source .venv/bin/activate && \
129
+ pip install . -r requirements_build.txt && \
130
+ ./build.sh
131
+ ```
132
+
133
+ #### Build all
134
+
135
+ ```bash
136
+ ./build_all.sh
117
137
  ```
118
138
 
119
139
  ## TODOs
120
140
 
121
141
  - [x] Add support to have commands depend on other commands (some commands must complete
122
142
  before a given command can start)
143
+ - [ ] Add a debug mode that logs debug information to a log file
123
144
  - [ ] Add support to state how many lines a command can use for it's output in interactive mode
124
145
  - [ ] Maybe add support to allow the user to provide stdin for commands that request it
125
146
  (such as a REPL)
@@ -76,28 +76,49 @@ You can also build an executable with the following (executables will be written
76
76
  > The `arch=x86_64` values in the following code blocks can be replaced with `arch=aarch64` and
77
77
  > any other architecture that is supported by docker to build an executable for that given architecture
78
78
 
79
+ > [!NOTE]
80
+ > To build aarch64 binaries on an x86_64 host machine, you will need to run the following
81
+ > commands to setup qemu to allow this to work
82
+
83
+ ```bash
84
+ sudo apt-get install qemu binfmt-support qemu-user-static && \
85
+ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
86
+ ```
87
+
79
88
  #### Build for generic linux
80
89
 
81
90
  ```bash
82
- docker build --tag pyallel --build-arg 'arch=x86_64' . && docker run -e 'arch=x86_64' --rm --volume "$(pwd):/src" pyallel
91
+ docker build --tag pyallel --build-arg 'arch=x86_64' --build-arg "uid=$(id -u)" . && \
92
+ docker run -e 'arch=x86_64' --rm --volume "$(pwd):/src" pyallel
83
93
  ```
84
94
 
85
95
  #### Build for alpine linux
86
96
 
87
97
  ```bash
88
- docker build --tag pyallel-alpine --build-arg 'arch=x86_64' --file Dockerfile.alpine . && docker run -e 'arch=x86_64' --rm --volume "$(pwd):/src" pyallel-alpine
98
+ docker build --tag pyallel-alpine --build-arg 'arch=x86_64' --build-arg "uid=$(id -u)" --file Dockerfile.alpine . && \
99
+ docker run -e 'arch=x86_64' --rm --volume "$(pwd):/src" pyallel-alpine
89
100
  ```
90
101
 
91
102
  #### Build locally
92
103
 
93
104
  ```bash
94
- python -m venv .venv && source .venv/bin/activate && pip install . -r requirements_build.txt && ./build.sh
105
+ python -m venv .venv && \
106
+ source .venv/bin/activate && \
107
+ pip install . -r requirements_build.txt && \
108
+ ./build.sh
109
+ ```
110
+
111
+ #### Build all
112
+
113
+ ```bash
114
+ ./build_all.sh
95
115
  ```
96
116
 
97
117
  ## TODOs
98
118
 
99
119
  - [x] Add support to have commands depend on other commands (some commands must complete
100
120
  before a given command can start)
121
+ - [ ] Add a debug mode that logs debug information to a log file
101
122
  - [ ] Add support to state how many lines a command can use for it's output in interactive mode
102
123
  - [ ] Maybe add support to allow the user to provide stdin for commands that request it
103
124
  (such as a REPL)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyallel"
3
- version = "1.2.0"
3
+ version = "1.2.2"
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"
@@ -5,7 +5,7 @@ import sys
5
5
  IN_TTY = sys.stdout.isatty()
6
6
  CLEAR_LINE = "\033[2K"
7
7
  UP_LINE = "\033[1F"
8
- ANSI_ESCAPE = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]")
8
+ ANSI_ESCAPE = re.compile(r"(\x9B|\x1B\[|\x1B\()[0-?]*[ -\/]*[@-~]")
9
9
 
10
10
  if IN_TTY:
11
11
 
@@ -70,7 +70,3 @@ class Process:
70
70
  if self._process:
71
71
  return self._process.wait()
72
72
  return -1
73
-
74
- @classmethod
75
- def from_command(cls, id: int, command: str) -> Process:
76
- return cls(id=id, command=command)
@@ -16,7 +16,13 @@ def get_num_lines(output: str, columns: int | None = None) -> int:
16
16
  for line in output.splitlines():
17
17
  line = constants.ANSI_ESCAPE.sub("", line)
18
18
  length = len(line)
19
- lines += 1 * (length // columns + 1 if length > columns else 1)
19
+ line_lines = 1
20
+ if length > columns:
21
+ line_lines = length // columns
22
+ remainder = length % columns
23
+ if remainder:
24
+ line_lines += 1
25
+ lines += 1 * line_lines
20
26
  return lines
21
27
 
22
28
 
@@ -41,6 +47,9 @@ class ProcessGroup:
41
47
  icon: int = 0
42
48
  colours: Colours = field(default_factory=Colours)
43
49
 
50
+ def __post_init__(self) -> None:
51
+ self.process_lines = [0 for _ in self.processes]
52
+
44
53
  def stream(self) -> int:
45
54
  for process in self.processes:
46
55
  process.run()
@@ -227,7 +236,7 @@ class ProcessGroup:
227
236
 
228
237
  for i, command in enumerate(commands):
229
238
  try:
230
- processes.append(Process.from_command(i + 1, command))
239
+ processes.append(Process(i + 1, command))
231
240
  except InvalidExecutableError as e:
232
241
  errors.append(e)
233
242
 
@@ -243,59 +252,69 @@ class ProcessGroup:
243
252
 
244
253
  return process_group
245
254
 
246
- def complete_output(self, tail: int = 20, all: bool = False) -> str:
255
+ def complete_output(self, all: bool = False) -> str:
247
256
  num_processes = len(self.processes)
248
257
  lines = constants.LINES() - (2 * num_processes)
249
258
  remainder = lines % num_processes
250
259
  tail = lines // num_processes
251
-
252
- if self.process_lines:
253
- self.process_lines = []
254
-
255
- for process in self.processes:
256
- self.process_lines.append(tail)
257
-
260
+ for i in range(num_processes):
261
+ self.process_lines[i] = tail
258
262
  if remainder:
259
263
  self.process_lines[-1] += remainder - 2
260
264
  else:
261
265
  self.process_lines[-1] -= 2
262
266
 
263
- output = ""
267
+ output = f"lines = {constants.LINES()}, process_lines = {self.process_lines}\n"
264
268
  for i, process in enumerate(self.processes, start=1):
269
+ process_output = ""
265
270
  if process.poll() is not None:
266
271
  self.completed_processes.add(process.id)
267
272
  if process.return_code() != 0:
268
273
  self.passed = False
269
- output += self._get_command_status(
274
+ process_output += self._get_command_status(
270
275
  process,
271
276
  passed=process.return_code() == 0,
272
277
  timer=self.timer,
273
278
  )
274
- output += "\n"
279
+ process_output += "\n"
275
280
  else:
276
- output += self._get_command_status(
281
+ process_output += self._get_command_status(
277
282
  process,
278
283
  icon=constants.ICONS[self.icon],
279
284
  timer=self.timer,
280
285
  )
281
- output += "\n"
286
+ process_output += "\n"
282
287
 
283
- process_output = process.read().decode()
288
+ command_lines = get_num_lines(process_output)
289
+ p_output = process.read().decode()
284
290
  if not self.output[process.id]:
285
291
  self.output[process.id].append("")
286
- self.output[process.id][0] += process_output
287
- process_output = self.output[process.id][0]
288
- if process_output:
292
+ self.output[process.id][0] += p_output
293
+ p_output = self.output[process.id][0]
294
+ p_output_lines = 0
295
+ if p_output:
289
296
  if not all:
290
- process_output = "\n".join(
291
- process_output.splitlines()[-self.process_lines[i - 1] :]
292
- )
293
- process_output += "\n"
294
- output += self._prefix(process_output)
295
- if output and output[-1] != "\n":
296
- output += "\n"
297
+ p_output_lines = p_output.splitlines()[-self.process_lines[i - 1] :]
298
+ p_output = ""
299
+ for line in p_output_lines:
300
+ if len(line) + 3 > constants.COLUMNS():
301
+ p_output += f"{''.join(line[:constants.COLUMNS()-3])}\n"
302
+ else:
303
+ p_output += line + "\n"
304
+ p_output = self._prefix(p_output)
305
+ if p_output and p_output[-1] != "\n":
306
+ p_output += "\n"
297
307
  if i != num_processes:
298
- output += "\n"
308
+ p_output += "\n"
309
+ p_output_lines = get_num_lines(p_output)
310
+
311
+ if not all and (command_lines + p_output_lines) > self.process_lines[i - 1]:
312
+ truncate = (command_lines + p_output_lines) - self.process_lines[i - 1]
313
+ p_output = "\n".join(p_output.splitlines()[truncate:])
314
+ p_output += "\n"
315
+
316
+ process_output += p_output
317
+ output += process_output
299
318
 
300
319
  if self.interrupt_count == 0:
301
320
  return output
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes