wexample-wex-addon-process 1.0.4__tar.gz → 2.1.0__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.
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/PKG-INFO +8 -8
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/README.md +4 -4
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/pyproject.toml +7 -4
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/commands/run/execute.py +13 -5
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_addon_manager.py +7 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/process_context.py +6 -9
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/process_type_registry.py +15 -6
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/record/record_file.py +10 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/runner/process_runner.py +14 -1
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/service/process_run_service.py +1 -1
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/commands/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/commands/run/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/commands/worker/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/commands/worker/start.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/const/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/const/process.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/abstract_process_type.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/family/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/family/abstract_process_type_family.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/family/file_state_process_type_family.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/filestate/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/filestate/file_state_option_process_type.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/filestate/file_state_report_process_type.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/process_type/selection.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/py.typed +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/record/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/runner/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/src/wexample_wex_addon_process/service/__init__.py +0 -0
- {wexample_wex_addon_process-1.0.4 → wexample_wex_addon_process-2.1.0}/tests/.gitkeep +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wexample-wex-addon-process
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Executes process runs defined on disk—either directly or by consuming them from a queue as a worker—against filestate selections
|
|
5
5
|
Author-Email: weeger <contact@wexample.com>
|
|
6
6
|
License: MIT
|
|
@@ -8,9 +8,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Requires-Python: >=3.10
|
|
11
|
-
Requires-Dist: wexample-file>=9.
|
|
12
|
-
Requires-Dist: wexample-queue>=1.0
|
|
13
|
-
Requires-Dist: wexample-wex-addon-app>=31.
|
|
11
|
+
Requires-Dist: wexample-file>=9.2.0
|
|
12
|
+
Requires-Dist: wexample-queue>=1.1.0
|
|
13
|
+
Requires-Dist: wexample-wex-addon-app>=31.1.0
|
|
14
14
|
Provides-Extra: dev
|
|
15
15
|
Requires-Dist: pytest; extra == "dev"
|
|
16
16
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -18,7 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
|
|
19
19
|
# wex-addon-process
|
|
20
20
|
|
|
21
|
-
Version: 1.0
|
|
21
|
+
Version: 2.1.0
|
|
22
22
|
|
|
23
23
|
`wexample-wex-addon-process` runs the treatments a board declares. A *process* says what to run and on which selection of files; a *run* is one execution of it, asked for by writing a record and picked up by a worker.
|
|
24
24
|
|
|
@@ -273,9 +273,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
273
273
|
|
|
274
274
|
## Dependencies
|
|
275
275
|
|
|
276
|
-
- wexample-file: >=9.
|
|
277
|
-
- wexample-queue: >=1.0
|
|
278
|
-
- wexample-wex-addon-app: >=31.
|
|
276
|
+
- wexample-file: >=9.2.0
|
|
277
|
+
- wexample-queue: >=1.1.0
|
|
278
|
+
- wexample-wex-addon-app: >=31.1.0
|
|
279
279
|
|
|
280
280
|
## Versioning & Compatibility Policy
|
|
281
281
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# wex-addon-process
|
|
2
2
|
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
|
|
5
5
|
`wexample-wex-addon-process` runs the treatments a board declares. A *process* says what to run and on which selection of files; a *run* is one execution of it, asked for by writing a record and picked up by a worker.
|
|
6
6
|
|
|
@@ -255,9 +255,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
255
255
|
|
|
256
256
|
## Dependencies
|
|
257
257
|
|
|
258
|
-
- wexample-file: >=9.
|
|
259
|
-
- wexample-queue: >=1.0
|
|
260
|
-
- wexample-wex-addon-app: >=31.
|
|
258
|
+
- wexample-file: >=9.2.0
|
|
259
|
+
- wexample-queue: >=1.1.0
|
|
260
|
+
- wexample-wex-addon-app: >=31.1.0
|
|
261
261
|
|
|
262
262
|
## Versioning & Compatibility Policy
|
|
263
263
|
|
|
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "wexample-wex-addon-process"
|
|
9
|
-
version = "1.0
|
|
9
|
+
version = "2.1.0"
|
|
10
10
|
description = "Executes process runs defined on disk—either directly or by consuming them from a queue as a worker—against filestate selections"
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "weeger", email = "contact@wexample.com" },
|
|
@@ -18,9 +18,9 @@ classifiers = [
|
|
|
18
18
|
"Operating System :: OS Independent",
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
|
-
"wexample-file>=9.
|
|
22
|
-
"wexample-queue>=1.0
|
|
23
|
-
"wexample-wex-addon-app>=31.
|
|
21
|
+
"wexample-file>=9.2.0",
|
|
22
|
+
"wexample-queue>=1.1.0",
|
|
23
|
+
"wexample-wex-addon-app>=31.1.0",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
[project.readme]
|
|
@@ -36,6 +36,9 @@ dev = [
|
|
|
36
36
|
"pytest-cov",
|
|
37
37
|
]
|
|
38
38
|
|
|
39
|
+
[project.entry-points."wex.addons"]
|
|
40
|
+
process = "wexample_wex_addon_process.process_addon_manager:ProcessAddonManager"
|
|
41
|
+
|
|
39
42
|
[tool.setuptools.packages.find]
|
|
40
43
|
include = [
|
|
41
44
|
"*",
|
|
@@ -37,17 +37,25 @@ def process__run__execute(
|
|
|
37
37
|
) -> AbstractResponse | None:
|
|
38
38
|
from wexample_app.response.success_response import SuccessResponse
|
|
39
39
|
|
|
40
|
+
from wexample_wex_addon_process.const.process import RUN_KEY_STATE
|
|
40
41
|
from wexample_wex_addon_process.process_type.process_type_registry import (
|
|
41
42
|
ProcessTypeRegistry,
|
|
42
43
|
)
|
|
43
44
|
from wexample_wex_addon_process.runner.process_runner import ProcessRunner
|
|
44
45
|
|
|
45
|
-
runner = ProcessRunner(
|
|
46
|
+
runner = ProcessRunner(
|
|
47
|
+
io=context.io,
|
|
48
|
+
registry=ProcessTypeRegistry.from_kernel(context.kernel),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
record = runner.execute(
|
|
52
|
+
workdir=app_workdir.get_path(),
|
|
53
|
+
run_id=run,
|
|
54
|
+
)
|
|
46
55
|
|
|
56
|
+
# What became of the run is in its record either way; the response only has
|
|
57
|
+
# to say where it landed.
|
|
47
58
|
return SuccessResponse(
|
|
48
59
|
kernel=context.kernel,
|
|
49
|
-
|
|
50
|
-
workdir=app_workdir.get_path(),
|
|
51
|
-
run_id=run,
|
|
52
|
-
),
|
|
60
|
+
message=f"Run {run}: {record.get(RUN_KEY_STATE)}.",
|
|
53
61
|
)
|
|
@@ -13,3 +13,10 @@ class ProcessAddonManager(AbstractAddonManager):
|
|
|
13
13
|
import wexample_wex_addon_process
|
|
14
14
|
|
|
15
15
|
return wexample_wex_addon_process
|
|
16
|
+
|
|
17
|
+
def get_process_type_family_classes(self) -> list[type]:
|
|
18
|
+
from wexample_wex_addon_process.process_type.family.file_state_process_type_family import (
|
|
19
|
+
FileStateProcessTypeFamily,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
return [FileStateProcessTypeFamily]
|
|
@@ -16,26 +16,23 @@ class ProcessContext:
|
|
|
16
16
|
It holds no run and no record. A type that could reach the record could write
|
|
17
17
|
anywhere in it, and what a run is in is decided by the runner alone — a type
|
|
18
18
|
says what it did, not that it is finished.
|
|
19
|
+
|
|
20
|
+
The files are handed over rather than read here: a selection is a rule and
|
|
21
|
+
answers what the disk holds when asked, so a second walk would count files a
|
|
22
|
+
run created itself and leave it done on more than its own total.
|
|
19
23
|
"""
|
|
20
24
|
|
|
21
25
|
def __init__(
|
|
22
26
|
self,
|
|
23
27
|
selection: Selection,
|
|
28
|
+
files: list[Path],
|
|
24
29
|
options: dict[str, Any],
|
|
25
30
|
advance: Callable[[int], None],
|
|
26
31
|
) -> None:
|
|
27
32
|
self.selection = selection
|
|
33
|
+
self.files = files
|
|
28
34
|
self.options = options
|
|
29
35
|
self._advance = advance
|
|
30
|
-
self._files: list[Path] | None = None
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def files(self) -> list[Path]:
|
|
34
|
-
"""The files the selection takes, read once for the length of the run."""
|
|
35
|
-
if self._files is None:
|
|
36
|
-
self._files = self.selection.files()
|
|
37
|
-
|
|
38
|
-
return self._files
|
|
39
36
|
|
|
40
37
|
@property
|
|
41
38
|
def workdir(self) -> Path:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
6
|
from wexample_wex_addon_process.process_type.abstract_process_type import (
|
|
@@ -29,12 +29,21 @@ class ProcessTypeRegistry:
|
|
|
29
29
|
self._types[process_type.name] = process_type
|
|
30
30
|
|
|
31
31
|
@classmethod
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
FileStateProcessTypeFamily,
|
|
35
|
-
)
|
|
32
|
+
def from_kernel(cls, kernel: Any) -> ProcessTypeRegistry:
|
|
33
|
+
"""Every family the loaded addons bring, this one's included.
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
The only way in: a hard-coded list would answer for the addons that
|
|
36
|
+
happened to be known here, and a type brought by an addon installed
|
|
37
|
+
beside it would be a name the runner refuses.
|
|
38
|
+
"""
|
|
39
|
+
families: list[AbstractProcessTypeFamily] = []
|
|
40
|
+
|
|
41
|
+
for addon in kernel.get_addons().values():
|
|
42
|
+
families.extend(
|
|
43
|
+
family_class() for family_class in addon.get_process_type_family_classes()
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return cls(families)
|
|
38
47
|
|
|
39
48
|
def get(self, name: str) -> AbstractProcessType | None:
|
|
40
49
|
return self._types.get(name)
|
|
@@ -43,3 +43,13 @@ def write_record(path: Path, values: dict[str, Any]) -> None:
|
|
|
43
43
|
allow_unicode=True,
|
|
44
44
|
)
|
|
45
45
|
)
|
|
46
|
+
|
|
47
|
+
# Written from both ends over a shared bind mount — the board web as root,
|
|
48
|
+
# a worker as its own user — so each writer leaves the record open for the
|
|
49
|
+
# other rather than at a mode only its own uid can rewrite. Best-effort:
|
|
50
|
+
# chmod is the owner's to make, and a writer that is not the owner is
|
|
51
|
+
# writing a file the owner already opened wide enough to reach.
|
|
52
|
+
import contextlib
|
|
53
|
+
|
|
54
|
+
with contextlib.suppress(PermissionError):
|
|
55
|
+
path.chmod(0o666)
|
|
@@ -97,7 +97,19 @@ class ProcessRunner:
|
|
|
97
97
|
)
|
|
98
98
|
|
|
99
99
|
selection = self._selection(workdir, process.get(PROCESS_KEY_SELECTION_ID))
|
|
100
|
-
|
|
100
|
+
|
|
101
|
+
# The walk reads the disk, and a run whose walk raises must end up
|
|
102
|
+
# `failed` like any other: left out of the guard, the exception would
|
|
103
|
+
# sink the message and freeze the run on `pending` with nothing to show.
|
|
104
|
+
try:
|
|
105
|
+
files = selection.files()
|
|
106
|
+
except Exception as exception:
|
|
107
|
+
self.io.debug(traceback.format_exc())
|
|
108
|
+
|
|
109
|
+
return self._fail(
|
|
110
|
+
run_path, run_id, f"{type(exception).__name__}: {exception}"
|
|
111
|
+
)
|
|
112
|
+
|
|
101
113
|
done = {"count": 0}
|
|
102
114
|
|
|
103
115
|
def advance(count: int) -> None:
|
|
@@ -122,6 +134,7 @@ class ProcessRunner:
|
|
|
122
134
|
data = process_type.run(
|
|
123
135
|
ProcessContext(
|
|
124
136
|
selection=selection,
|
|
137
|
+
files=files,
|
|
125
138
|
options=self._options(process.get(PROCESS_KEY_OPTIONS)),
|
|
126
139
|
advance=advance,
|
|
127
140
|
)
|
|
@@ -60,7 +60,7 @@ class ProcessRunService(AbstractQueueService):
|
|
|
60
60
|
# must not answer for each other.
|
|
61
61
|
return ProcessRunner(
|
|
62
62
|
io=self.io,
|
|
63
|
-
registry=ProcessTypeRegistry.
|
|
63
|
+
registry=ProcessTypeRegistry.from_kernel(self.kernel),
|
|
64
64
|
ring=lambda run_id: self._ring(workdir, run_id),
|
|
65
65
|
)
|
|
66
66
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|