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.
- {pyallel-1.2.0 → pyallel-1.2.2}/PKG-INFO +25 -4
- {pyallel-1.2.0 → pyallel-1.2.2}/README.md +24 -3
- {pyallel-1.2.0 → pyallel-1.2.2}/pyproject.toml +1 -1
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/constants.py +1 -1
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/process.py +0 -4
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/process_group.py +46 -27
- {pyallel-1.2.0 → pyallel-1.2.2}/LICENSE +0 -0
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/__init__.py +0 -0
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/colours.py +0 -0
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/errors.py +0 -0
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/main.py +0 -0
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/parser.py +0 -0
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/process_group_manager.py +0 -0
- {pyallel-1.2.0 → pyallel-1.2.2}/src/pyallel/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyallel
|
|
3
|
-
Version: 1.2.
|
|
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'
|
|
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 . &&
|
|
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 &&
|
|
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'
|
|
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 . &&
|
|
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 &&
|
|
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)
|
|
@@ -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
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
279
|
+
process_output += "\n"
|
|
275
280
|
else:
|
|
276
|
-
|
|
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
|
-
|
|
286
|
+
process_output += "\n"
|
|
282
287
|
|
|
283
|
-
|
|
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] +=
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
|
|
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
|
|
File without changes
|