wexample-wex-addon-process 3.0.1__tar.gz → 4.0.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-3.0.1 → wexample_wex_addon_process-4.0.0}/PKG-INFO +14 -6
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/README.md +12 -4
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/pyproject.toml +2 -2
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/const/process.py +4 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/process_context.py +13 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/runner/process_runner.py +38 -18
- wexample_wex_addon_process-4.0.0/tests/unit/test_skip_and_failure.py +111 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/commands/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/commands/run/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/commands/run/execute.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/commands/worker/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/commands/worker/start.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/const/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/const/filestate.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/exception/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/exception/run_cancelled_exception.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/helper/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/helper/git_commit.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_addon_manager.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/abstract_process_type.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/family/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/family/abstract_process_type_family.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/family/file_state_process_type_family.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/filestate/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/filestate/file_state_option_process_type.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/filestate/file_state_report_process_type.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/process_type_registry.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/process_type/selection.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/py.typed +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/record/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/record/record_file.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/runner/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/selection/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/selection/abstract_code_from_directory_selection.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/selection/abstract_process_selection.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/selection/selection_registry.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/service/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/src/wexample_wex_addon_process/service/process_run_service.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/.gitkeep +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/__init__.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/test_artifacts.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/test_long_runs.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/test_run_overrides.py +0 -0
- {wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/test_selection.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wexample-wex-addon-process
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.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
|
|
@@ -10,7 +10,7 @@ Classifier: Operating System :: OS Independent
|
|
|
10
10
|
Requires-Python: >=3.10
|
|
11
11
|
Requires-Dist: wexample-file>=9.2.0
|
|
12
12
|
Requires-Dist: wexample-queue>=2.0.0
|
|
13
|
-
Requires-Dist: wexample-wex-addon-app>=32.
|
|
13
|
+
Requires-Dist: wexample-wex-addon-app>=32.2.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:
|
|
21
|
+
Version: 4.0.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
|
|
|
@@ -249,10 +249,18 @@ moves on: the run still completes, and lists what it could not do under its data
|
|
|
249
249
|
"data": {"errors": [{"path": "docs/a.pdf", "reason": "docling timed out"}]}
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
+
An item left as it was on purpose — its output already up to date — is said with
|
|
253
|
+
`context.skip(path, reason)`, listed under `data.skipped` the same way. It counts as
|
|
254
|
+
dealt with and is never committed; that is what lets a long batch be started again
|
|
255
|
+
without redoing what is done.
|
|
256
|
+
|
|
257
|
+
A run whose type raises ends `failed`, its `error` beside the `artifacts`, `errors` and
|
|
258
|
+
`skipped` collected until then.
|
|
259
|
+
|
|
252
260
|
To stop a run, write `"cancel": true` into its record. The runner checks it before every
|
|
253
261
|
item (`context.each()`, `context.advance()`, or `context.check_cancel()` in a type's own
|
|
254
|
-
loop) and ends the run `cancelled`, with `items_done` where it got to and the errors
|
|
255
|
-
artifacts collected so far. A run cancelled before it was taken ends `cancelled` without
|
|
262
|
+
loop) and ends the run `cancelled`, with `items_done` where it got to and the errors,
|
|
263
|
+
artifacts and skipped items collected so far. A run cancelled before it was taken ends `cancelled` without
|
|
256
264
|
starting.
|
|
257
265
|
|
|
258
266
|
## Tests
|
|
@@ -358,7 +366,7 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
358
366
|
|
|
359
367
|
- wexample-file: >=9.2.0
|
|
360
368
|
- wexample-queue: >=2.0.0
|
|
361
|
-
- wexample-wex-addon-app: >=32.
|
|
369
|
+
- wexample-wex-addon-app: >=32.2.0
|
|
362
370
|
|
|
363
371
|
## Versioning & Compatibility Policy
|
|
364
372
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# wex-addon-process
|
|
2
2
|
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.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
|
|
|
@@ -231,10 +231,18 @@ moves on: the run still completes, and lists what it could not do under its data
|
|
|
231
231
|
"data": {"errors": [{"path": "docs/a.pdf", "reason": "docling timed out"}]}
|
|
232
232
|
```
|
|
233
233
|
|
|
234
|
+
An item left as it was on purpose — its output already up to date — is said with
|
|
235
|
+
`context.skip(path, reason)`, listed under `data.skipped` the same way. It counts as
|
|
236
|
+
dealt with and is never committed; that is what lets a long batch be started again
|
|
237
|
+
without redoing what is done.
|
|
238
|
+
|
|
239
|
+
A run whose type raises ends `failed`, its `error` beside the `artifacts`, `errors` and
|
|
240
|
+
`skipped` collected until then.
|
|
241
|
+
|
|
234
242
|
To stop a run, write `"cancel": true` into its record. The runner checks it before every
|
|
235
243
|
item (`context.each()`, `context.advance()`, or `context.check_cancel()` in a type's own
|
|
236
|
-
loop) and ends the run `cancelled`, with `items_done` where it got to and the errors
|
|
237
|
-
artifacts collected so far. A run cancelled before it was taken ends `cancelled` without
|
|
244
|
+
loop) and ends the run `cancelled`, with `items_done` where it got to and the errors,
|
|
245
|
+
artifacts and skipped items collected so far. A run cancelled before it was taken ends `cancelled` without
|
|
238
246
|
starting.
|
|
239
247
|
|
|
240
248
|
## Tests
|
|
@@ -340,7 +348,7 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
340
348
|
|
|
341
349
|
- wexample-file: >=9.2.0
|
|
342
350
|
- wexample-queue: >=2.0.0
|
|
343
|
-
- wexample-wex-addon-app: >=32.
|
|
351
|
+
- wexample-wex-addon-app: >=32.2.0
|
|
344
352
|
|
|
345
353
|
## Versioning & Compatibility Policy
|
|
346
354
|
|
|
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "wexample-wex-addon-process"
|
|
9
|
-
version = "
|
|
9
|
+
version = "4.0.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" },
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
dependencies = [
|
|
21
21
|
"wexample-file>=9.2.0",
|
|
22
22
|
"wexample-queue>=2.0.0",
|
|
23
|
-
"wexample-wex-addon-app>=32.
|
|
23
|
+
"wexample-wex-addon-app>=32.2.0",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
[project.readme]
|
|
@@ -18,6 +18,10 @@ DATA_KEY_ARTIFACTS: str = "artifacts"
|
|
|
18
18
|
# The run still completes — one bad file does not sink the batch.
|
|
19
19
|
DATA_KEY_ERRORS: str = "errors"
|
|
20
20
|
|
|
21
|
+
# Under a run's data: the items a type left as they were on purpose, each
|
|
22
|
+
# {path, reason} — an output already up to date. They count as dealt with.
|
|
23
|
+
DATA_KEY_SKIPPED: str = "skipped"
|
|
24
|
+
|
|
21
25
|
DEFAULT_COMMIT_AUTHOR: str = "wex <wex@localhost>"
|
|
22
26
|
"""Who commits a run's work when neither the run nor git names anyone."""
|
|
23
27
|
|
|
@@ -34,6 +34,7 @@ class ProcessContext:
|
|
|
34
34
|
touch: Callable[[Path], None] | None = None,
|
|
35
35
|
artifact: Callable[[Path, str | None], None] | None = None,
|
|
36
36
|
fail: Callable[[Path, str], None] | None = None,
|
|
37
|
+
skip: Callable[[Path, str], None] | None = None,
|
|
37
38
|
check_cancel: Callable[[], None] | None = None,
|
|
38
39
|
) -> None:
|
|
39
40
|
self.selection = selection
|
|
@@ -42,6 +43,7 @@ class ProcessContext:
|
|
|
42
43
|
self._touch = touch
|
|
43
44
|
self._artifact = artifact
|
|
44
45
|
self._fail = fail
|
|
46
|
+
self._skip = skip
|
|
45
47
|
self._check_cancel = check_cancel
|
|
46
48
|
self.options = options
|
|
47
49
|
self._advance = advance
|
|
@@ -104,6 +106,17 @@ class ProcessContext:
|
|
|
104
106
|
if self._fail is not None:
|
|
105
107
|
self._fail(path, reason)
|
|
106
108
|
|
|
109
|
+
def skip(self, path: Path, reason: str) -> None:
|
|
110
|
+
"""Says that item was left as it was on purpose, and why — « output up to
|
|
111
|
+
date ».
|
|
112
|
+
|
|
113
|
+
Listed under `data.skipped` whatever the run ends in, so a run started
|
|
114
|
+
again over a long batch says what it did not need to redo. The item still
|
|
115
|
+
counts as dealt with, and is not touched: nothing of it is committed.
|
|
116
|
+
"""
|
|
117
|
+
if self._skip is not None:
|
|
118
|
+
self._skip(path, reason)
|
|
119
|
+
|
|
107
120
|
def touch(self, path: Path) -> None:
|
|
108
121
|
"""Says the run wrote that path — changed, created, or removed it.
|
|
109
122
|
|
|
@@ -162,6 +162,7 @@ class ProcessRunner:
|
|
|
162
162
|
touched: set[Path] = set()
|
|
163
163
|
artifacts: list[dict[str, Any]] = []
|
|
164
164
|
errors: list[dict[str, str]] = []
|
|
165
|
+
skipped: list[dict[str, str]] = []
|
|
165
166
|
|
|
166
167
|
def advance(count: int) -> None:
|
|
167
168
|
progress[RUN_KEY_ITEMS_DONE] += count
|
|
@@ -204,13 +205,14 @@ class ProcessRunner:
|
|
|
204
205
|
kernel=self.kernel,
|
|
205
206
|
touch=touched.add,
|
|
206
207
|
artifact=self._artifact_collector(workdir, artifacts),
|
|
207
|
-
fail=self.
|
|
208
|
+
fail=self._reason_collector(workdir, errors),
|
|
209
|
+
skip=self._reason_collector(workdir, skipped),
|
|
208
210
|
check_cancel=self._cancel_checker(run_path),
|
|
209
211
|
)
|
|
210
212
|
)
|
|
211
213
|
except RunCancelledException:
|
|
212
214
|
data: dict[str, Any] = {}
|
|
213
|
-
self._add_collected(data, artifacts, errors)
|
|
215
|
+
self._add_collected(data, artifacts, errors, skipped)
|
|
214
216
|
return self._write(
|
|
215
217
|
run_path,
|
|
216
218
|
run_id,
|
|
@@ -225,15 +227,20 @@ class ProcessRunner:
|
|
|
225
227
|
except Exception as exception:
|
|
226
228
|
self.io.debug(traceback.format_exc())
|
|
227
229
|
|
|
228
|
-
# The file it was on stays named: a failed run says where it failed
|
|
230
|
+
# The file it was on stays named: a failed run says where it failed,
|
|
231
|
+
# and what it did before keeps being said — a run started again over a
|
|
232
|
+
# long batch is told by it what is already there.
|
|
233
|
+
data = {}
|
|
234
|
+
self._add_collected(data, artifacts, errors, skipped)
|
|
229
235
|
return self._fail(
|
|
230
236
|
run_path,
|
|
231
237
|
run_id,
|
|
232
238
|
f"{type(exception).__name__}: {exception}",
|
|
233
239
|
progress,
|
|
240
|
+
data,
|
|
234
241
|
)
|
|
235
242
|
|
|
236
|
-
self._add_collected(data, artifacts, errors)
|
|
243
|
+
self._add_collected(data, artifacts, errors, skipped)
|
|
237
244
|
|
|
238
245
|
values: dict[str, Any] = {
|
|
239
246
|
RUN_KEY_STATE: STATE_COMPLETE,
|
|
@@ -348,6 +355,7 @@ class ProcessRunner:
|
|
|
348
355
|
touched: set[Path] = set()
|
|
349
356
|
artifacts: list[dict[str, Any]] = []
|
|
350
357
|
errors: list[dict[str, str]] = []
|
|
358
|
+
skipped: list[dict[str, str]] = []
|
|
351
359
|
data = process_type.run(
|
|
352
360
|
ProcessContext(
|
|
353
361
|
selection=selection,
|
|
@@ -358,10 +366,11 @@ class ProcessRunner:
|
|
|
358
366
|
kernel=self.kernel,
|
|
359
367
|
touch=touched.add,
|
|
360
368
|
artifact=self._artifact_collector(workdir, artifacts),
|
|
361
|
-
fail=self.
|
|
369
|
+
fail=self._reason_collector(workdir, errors),
|
|
370
|
+
skip=self._reason_collector(workdir, skipped),
|
|
362
371
|
)
|
|
363
372
|
)
|
|
364
|
-
self._add_collected(data, artifacts, errors)
|
|
373
|
+
self._add_collected(data, artifacts, errors, skipped)
|
|
365
374
|
|
|
366
375
|
if commit:
|
|
367
376
|
from wexample_wex_addon_process.const.filestate import OPTION_KEY_ACTION
|
|
@@ -381,18 +390,23 @@ class ProcessRunner:
|
|
|
381
390
|
data: dict[str, Any],
|
|
382
391
|
artifacts: list[dict[str, Any]],
|
|
383
392
|
errors: list[dict[str, str]],
|
|
393
|
+
skipped: list[dict[str, str]],
|
|
384
394
|
) -> None:
|
|
385
395
|
"""Writes what every type may report the same way under its data — the files
|
|
386
|
-
it produced, the items it could not deal with
|
|
396
|
+
it produced, the items it could not deal with, those it left as they were —
|
|
397
|
+
left out when empty."""
|
|
387
398
|
from wexample_wex_addon_process.const.process import (
|
|
388
399
|
DATA_KEY_ARTIFACTS,
|
|
389
400
|
DATA_KEY_ERRORS,
|
|
401
|
+
DATA_KEY_SKIPPED,
|
|
390
402
|
)
|
|
391
403
|
|
|
392
404
|
if artifacts:
|
|
393
405
|
data[DATA_KEY_ARTIFACTS] = artifacts
|
|
394
406
|
if errors:
|
|
395
407
|
data[DATA_KEY_ERRORS] = errors
|
|
408
|
+
if skipped:
|
|
409
|
+
data[DATA_KEY_SKIPPED] = skipped
|
|
396
410
|
|
|
397
411
|
def _alive(self, run_path: Path) -> Any:
|
|
398
412
|
"""Keeps the run's record saying it is alive while the block runs.
|
|
@@ -496,23 +510,16 @@ class ProcessRunner:
|
|
|
496
510
|
)
|
|
497
511
|
return git_commit_paths(workdir, paths, message, author=author)
|
|
498
512
|
|
|
499
|
-
def _error_collector(
|
|
500
|
-
self, workdir: Path, errors: list[dict[str, str]]
|
|
501
|
-
) -> Any:
|
|
502
|
-
"""What a context calls to say an item could not be dealt with."""
|
|
503
|
-
|
|
504
|
-
def collect(path: Path, reason: str) -> None:
|
|
505
|
-
errors.append({"path": str(path.relative_to(workdir)), "reason": reason})
|
|
506
|
-
|
|
507
|
-
return collect
|
|
508
|
-
|
|
509
513
|
def _fail(
|
|
510
514
|
self,
|
|
511
515
|
run_path: Path,
|
|
512
516
|
run_id: str,
|
|
513
517
|
message: str,
|
|
514
518
|
progress: dict[str, Any] | None = None,
|
|
519
|
+
data: dict[str, Any] | None = None,
|
|
515
520
|
) -> dict[str, Any]:
|
|
521
|
+
"""Says the run failed, with what it had collected until then beside the
|
|
522
|
+
error: its artifacts are on disk all the same."""
|
|
516
523
|
from wexample_wex_addon_process.const.process import (
|
|
517
524
|
RUN_KEY_DATA,
|
|
518
525
|
RUN_KEY_DATE_ENDED,
|
|
@@ -529,7 +536,7 @@ class ProcessRunner:
|
|
|
529
536
|
**(progress or {}),
|
|
530
537
|
RUN_KEY_STATE: STATE_FAILED,
|
|
531
538
|
RUN_KEY_DATE_ENDED: self._now(),
|
|
532
|
-
RUN_KEY_DATA: {"error": message},
|
|
539
|
+
RUN_KEY_DATA: {"error": message, **(data or {})},
|
|
533
540
|
},
|
|
534
541
|
)
|
|
535
542
|
|
|
@@ -587,6 +594,19 @@ class ProcessRunner:
|
|
|
587
594
|
write_record(run_path, dict(progress))
|
|
588
595
|
self._ring_now(run_id)
|
|
589
596
|
|
|
597
|
+
def _reason_collector(
|
|
598
|
+
self, workdir: Path, collected: list[dict[str, str]]
|
|
599
|
+
) -> Any:
|
|
600
|
+
"""What a context calls to say something of an item, and why — that it
|
|
601
|
+
could not be dealt with, or that it was left as it was."""
|
|
602
|
+
|
|
603
|
+
def collect(path: Path, reason: str) -> None:
|
|
604
|
+
collected.append(
|
|
605
|
+
{"path": str(path.relative_to(workdir)), "reason": reason}
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
return collect
|
|
609
|
+
|
|
590
610
|
def _ring_now(self, run_id: str) -> None:
|
|
591
611
|
import time
|
|
592
612
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _registry() -> Any:
|
|
9
|
+
"""A type that converts each item, skips those already converted, and stops
|
|
10
|
+
dead on an item named `crash`."""
|
|
11
|
+
from wexample_wex_addon_process.process_type.abstract_process_type import (
|
|
12
|
+
AbstractProcessType,
|
|
13
|
+
)
|
|
14
|
+
from wexample_wex_addon_process.process_type.family.abstract_process_type_family import (
|
|
15
|
+
AbstractProcessTypeFamily,
|
|
16
|
+
)
|
|
17
|
+
from wexample_wex_addon_process.process_type.process_type_registry import (
|
|
18
|
+
ProcessTypeRegistry,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
class ConvertType(AbstractProcessType):
|
|
22
|
+
label = "Convert"
|
|
23
|
+
name = "test:convert"
|
|
24
|
+
|
|
25
|
+
def run(self, context: Any) -> dict[str, Any]:
|
|
26
|
+
for path in context.each():
|
|
27
|
+
target = path.with_suffix(".txt")
|
|
28
|
+
if target.exists():
|
|
29
|
+
context.skip(path, "output up to date")
|
|
30
|
+
continue
|
|
31
|
+
if path.stem == "crash":
|
|
32
|
+
raise ConnectionError("docling unreachable")
|
|
33
|
+
if path.stem == "bad":
|
|
34
|
+
context.fail(path, "malformed")
|
|
35
|
+
continue
|
|
36
|
+
target.write_text(path.read_text())
|
|
37
|
+
context.artifact(target)
|
|
38
|
+
|
|
39
|
+
return {}
|
|
40
|
+
|
|
41
|
+
class ConvertFamily(AbstractProcessTypeFamily):
|
|
42
|
+
prefix = "test"
|
|
43
|
+
|
|
44
|
+
def types(self) -> list[AbstractProcessType]:
|
|
45
|
+
return [ConvertType()]
|
|
46
|
+
|
|
47
|
+
return ProcessTypeRegistry([ConvertFamily()])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _execute(app: Path) -> dict[str, Any]:
|
|
51
|
+
from wexample_prompt.common.io_manager import IoManager
|
|
52
|
+
|
|
53
|
+
from wexample_wex_addon_process.runner.process_runner import ProcessRunner
|
|
54
|
+
|
|
55
|
+
data = app / ".wex" / "data"
|
|
56
|
+
for kind, identity, values in (
|
|
57
|
+
("selection", "s", {"patterns": "docs/*.md"}),
|
|
58
|
+
("process", "p", {"title": "Convert", "type": "test:convert", "selection_id": "s"}),
|
|
59
|
+
("process_run", "r", {"process_id": "p", "state": "pending"}),
|
|
60
|
+
):
|
|
61
|
+
(data / kind).mkdir(parents=True, exist_ok=True)
|
|
62
|
+
(data / kind / f"{identity}.json").write_text(json.dumps(values))
|
|
63
|
+
|
|
64
|
+
return ProcessRunner(io=IoManager(), registry=_registry()).execute(
|
|
65
|
+
workdir=app, run_id="r"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _app(tmp_path: Path, *stems: str) -> Path:
|
|
70
|
+
app = tmp_path / "app"
|
|
71
|
+
(app / "docs").mkdir(parents=True)
|
|
72
|
+
for stem in stems:
|
|
73
|
+
(app / "docs" / f"{stem}.md").write_text(f"{stem}\n")
|
|
74
|
+
return app
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_a_skipped_item_is_listed_and_counted(tmp_path: Path) -> None:
|
|
78
|
+
app = _app(tmp_path, "a", "b")
|
|
79
|
+
(app / "docs" / "a.txt").write_text("a\n")
|
|
80
|
+
|
|
81
|
+
run = _execute(app)
|
|
82
|
+
|
|
83
|
+
assert run["state"] == "complete", run.get("data")
|
|
84
|
+
assert run["data"]["skipped"] == [
|
|
85
|
+
{"path": "docs/a.md", "reason": "output up to date"}
|
|
86
|
+
]
|
|
87
|
+
assert run["items_done"] == 2, "A skipped item counts as dealt with"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_nothing_skipped_leaves_no_key(tmp_path: Path) -> None:
|
|
91
|
+
run = _execute(_app(tmp_path, "a"))
|
|
92
|
+
|
|
93
|
+
assert run["state"] == "complete"
|
|
94
|
+
assert "skipped" not in run["data"]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_a_failed_run_keeps_what_it_collected(tmp_path: Path) -> None:
|
|
98
|
+
# Sorted: a.md is skipped, b.md converted, bad.md in error, crash.md raises.
|
|
99
|
+
app = _app(tmp_path, "a", "b", "bad", "crash")
|
|
100
|
+
(app / "docs" / "a.txt").write_text("a\n")
|
|
101
|
+
|
|
102
|
+
run = _execute(app)
|
|
103
|
+
|
|
104
|
+
assert run["state"] == "failed"
|
|
105
|
+
assert run["data"]["error"] == "ConnectionError: docling unreachable"
|
|
106
|
+
assert run["data"]["skipped"] == [
|
|
107
|
+
{"path": "docs/a.md", "reason": "output up to date"}
|
|
108
|
+
]
|
|
109
|
+
assert [a["path"] for a in run["data"]["artifacts"]] == ["docs/b.txt"]
|
|
110
|
+
assert run["data"]["errors"] == [{"path": "docs/bad.md", "reason": "malformed"}]
|
|
111
|
+
assert run["current"] == "docs/crash.md", "A failed run still says where"
|
|
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
|
|
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
|
{wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/__init__.py
RENAMED
|
File without changes
|
{wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/test_artifacts.py
RENAMED
|
File without changes
|
{wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/test_long_runs.py
RENAMED
|
File without changes
|
|
File without changes
|
{wexample_wex_addon_process-3.0.1 → wexample_wex_addon_process-4.0.0}/tests/unit/test_selection.py
RENAMED
|
File without changes
|