wexample-wex-addon-process 4.1.4__tar.gz → 5.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.
Files changed (62) hide show
  1. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/PKG-INFO +6 -7
  2. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/README.md +4 -5
  3. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/pyproject.toml +2 -2
  4. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/builtin/builtin_process.py +32 -0
  5. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/builtin/builtin_process_registry.py +29 -0
  6. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/process/create.py +108 -0
  7. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/process/remove.py +46 -0
  8. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/process/update.py +107 -0
  9. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/run/cancel.py +54 -0
  10. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/run/create.py +144 -0
  11. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/selection/create.py +69 -0
  12. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/selection/remove.py +46 -0
  13. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/selection/update.py +66 -0
  14. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/const/process.py +6 -0
  15. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/exception/record_not_found_exception.py +24 -0
  16. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_addon_manager.py +7 -0
  17. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_type/process_context.py +5 -0
  18. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/runner/process_runner.py +70 -12
  19. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/selection/git_changes_selection.py +36 -0
  20. wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/service/__init__.py +0 -0
  21. wexample_wex_addon_process-5.0.0/tests/.gitkeep +0 -0
  22. wexample_wex_addon_process-5.0.0/tests/unit/__init__.py +0 -0
  23. wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/helper/git_commit.py +0 -83
  24. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/__init__.py +0 -0
  25. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/commands → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/builtin}/__init__.py +0 -0
  26. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/commands/run → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands}/__init__.py +0 -0
  27. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/commands/worker → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/process}/__init__.py +0 -0
  28. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/const → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/run}/__init__.py +0 -0
  29. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/commands/run/execute.py +0 -0
  30. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/exception → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/selection}/__init__.py +0 -0
  31. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/helper → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/commands/worker}/__init__.py +0 -0
  32. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/commands/worker/start.py +0 -0
  33. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/process_type → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/const}/__init__.py +0 -0
  34. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/const/filestate.py +0 -0
  35. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/process_type/family → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/exception}/__init__.py +0 -0
  36. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/exception/run_cancelled_exception.py +0 -0
  37. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/process_type/filestate → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/helper}/__init__.py +0 -0
  38. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/record → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/process_type}/__init__.py +0 -0
  39. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_type/abstract_process_type.py +0 -0
  40. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/runner → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/process_type/family}/__init__.py +0 -0
  41. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_type/family/abstract_process_type_family.py +0 -0
  42. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_type/family/file_state_process_type_family.py +0 -0
  43. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/selection → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/process_type/filestate}/__init__.py +0 -0
  44. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_type/filestate/file_state_option_process_type.py +0 -0
  45. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_type/filestate/file_state_report_process_type.py +0 -0
  46. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_type/process_type_registry.py +0 -0
  47. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/process_type/selection.py +0 -0
  48. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/py.typed +0 -0
  49. {wexample_wex_addon_process-4.1.4/src/wexample_wex_addon_process/service → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/record}/__init__.py +0 -0
  50. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/record/record_file.py +0 -0
  51. {wexample_wex_addon_process-4.1.4/tests/unit → wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/runner}/__init__.py +0 -0
  52. /wexample_wex_addon_process-4.1.4/tests/.gitkeep → /wexample_wex_addon_process-5.0.0/src/wexample_wex_addon_process/selection/__init__.py +0 -0
  53. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/selection/abstract_code_from_directory_selection.py +0 -0
  54. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/selection/abstract_process_selection.py +0 -0
  55. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/selection/selection_registry.py +0 -0
  56. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/src/wexample_wex_addon_process/service/process_run_service.py +0 -0
  57. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/tests/unit/test_artifacts.py +0 -0
  58. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/tests/unit/test_long_runs.py +0 -0
  59. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/tests/unit/test_run_overrides.py +0 -0
  60. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/tests/unit/test_selection.py +0 -0
  61. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/tests/unit/test_skip_and_failure.py +0 -0
  62. {wexample_wex_addon_process-4.1.4 → wexample_wex_addon_process-5.0.0}/tests/unit/test_verdict_locations.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wexample-wex-addon-process
3
- Version: 4.1.4
3
+ Version: 5.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>=33.3.0
13
+ Requires-Dist: wexample-wex-addon-app>=34.0.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: 4.1.4
21
+ Version: 5.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
 
@@ -360,13 +360,11 @@ This package is part of the Wexample Suite — a collection of high-quality, mod
360
360
 
361
361
  The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.
362
362
 
363
- Visit the [Wexample Suite documentation](https://docs.wexample.com) for the complete package ecosystem.
364
-
365
363
  ## Dependencies
366
364
 
367
365
  - wexample-file: >=9.2.0
368
366
  - wexample-queue: >=2.0.0
369
- - wexample-wex-addon-app: >=33.3.0
367
+ - wexample-wex-addon-app: >=34.0.0
370
368
 
371
369
  ## Versioning & Compatibility Policy
372
370
 
@@ -392,6 +390,8 @@ This packages suite embodies this spirit. Trusted by professionals and enthusias
392
390
 
393
391
  Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.
394
392
 
393
+ Built and maintained by [Romain Weeger](https://weeger.io).
394
+
395
395
  ## Known Limitations & Roadmap
396
396
 
397
397
  Current limitations and planned features are tracked in the GitHub issues.
@@ -411,7 +411,6 @@ See the [project roadmap](https://github.com/wexample/python-wex-addon-process/i
411
411
  ## Useful Links
412
412
 
413
413
  - **Homepage**: https://github.com/wexample/python-wex-addon-process
414
- - **Documentation**: [docs.wexample.com](https://docs.wexample.com)
415
414
  - **Issue Tracker**: https://github.com/wexample/python-wex-addon-process/issues
416
415
  - **Discussions**: https://github.com/wexample/python-wex-addon-process/discussions
417
416
  - **PyPI**: [pypi.org/project/wexample-wex-addon-process](https://pypi.org/project/wexample-wex-addon-process/)
@@ -1,6 +1,6 @@
1
1
  # wex-addon-process
2
2
 
3
- Version: 4.1.4
3
+ Version: 5.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
 
@@ -342,13 +342,11 @@ This package is part of the Wexample Suite — a collection of high-quality, mod
342
342
 
343
343
  The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.
344
344
 
345
- Visit the [Wexample Suite documentation](https://docs.wexample.com) for the complete package ecosystem.
346
-
347
345
  ## Dependencies
348
346
 
349
347
  - wexample-file: >=9.2.0
350
348
  - wexample-queue: >=2.0.0
351
- - wexample-wex-addon-app: >=33.3.0
349
+ - wexample-wex-addon-app: >=34.0.0
352
350
 
353
351
  ## Versioning & Compatibility Policy
354
352
 
@@ -374,6 +372,8 @@ This packages suite embodies this spirit. Trusted by professionals and enthusias
374
372
 
375
373
  Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.
376
374
 
375
+ Built and maintained by [Romain Weeger](https://weeger.io).
376
+
377
377
  ## Known Limitations & Roadmap
378
378
 
379
379
  Current limitations and planned features are tracked in the GitHub issues.
@@ -393,7 +393,6 @@ See the [project roadmap](https://github.com/wexample/python-wex-addon-process/i
393
393
  ## Useful Links
394
394
 
395
395
  - **Homepage**: https://github.com/wexample/python-wex-addon-process
396
- - **Documentation**: [docs.wexample.com](https://docs.wexample.com)
397
396
  - **Issue Tracker**: https://github.com/wexample/python-wex-addon-process/issues
398
397
  - **Discussions**: https://github.com/wexample/python-wex-addon-process/discussions
399
398
  - **PyPI**: [pypi.org/project/wexample-wex-addon-process](https://pypi.org/project/wexample-wex-addon-process/)
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "wexample-wex-addon-process"
9
- version = "4.1.4"
9
+ version = "5.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>=33.3.0",
23
+ "wexample-wex-addon-app>=34.0.0",
24
24
  ]
25
25
 
26
26
  [project.readme]
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class BuiltinProcess:
9
+ """A process an addon ships: the same in every app, declared by no record.
10
+
11
+ What a process record says, said in code — a type, the options it is handed, and
12
+ the selection it runs on, a selection computed in code named by its provider, since
13
+ there is no selection record to point at either. A run names it by its identity, as
14
+ it names a process an app holds; nothing in an app can change it.
15
+ """
16
+
17
+ name: str
18
+ selection: str
19
+ title: str
20
+ type: str
21
+
22
+ options: dict[str, Any] = field(default_factory=dict)
23
+ severity: str | None = None
24
+
25
+ @property
26
+ def identity(self) -> str:
27
+ """The uuid a run and a consumer know it by, following from its name."""
28
+ from wexample_wex_core.helper.data_record import record_identity
29
+
30
+ from wexample_wex_addon_process.const.process import KIND_PROCESS
31
+
32
+ return record_identity(KIND_PROCESS, self.name)
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ if TYPE_CHECKING:
6
+ from wexample_wex_addon_process.builtin.builtin_process import BuiltinProcess
7
+
8
+
9
+ class BuiltinProcessRegistry:
10
+ """The processes the loaded addons ship, by the identity a run names them with."""
11
+
12
+ def __init__(self, processes: list[BuiltinProcess]) -> None:
13
+ self._processes = {process.identity: process for process in processes}
14
+
15
+ @classmethod
16
+ def from_kernel(cls, kernel: Any) -> BuiltinProcessRegistry:
17
+ return cls(
18
+ [
19
+ process
20
+ for addon in kernel.get_addons().values()
21
+ for process in addon.get_builtin_processes()
22
+ ]
23
+ )
24
+
25
+ def all(self) -> list[BuiltinProcess]:
26
+ return sorted(self._processes.values(), key=lambda process: process.name)
27
+
28
+ def get(self, identity: str) -> BuiltinProcess | None:
29
+ return self._processes.get(identity)
@@ -0,0 +1,108 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from wexample_cli.const.tags import EffectTag, ScopeTag
6
+ from wexample_cli.decorator.api_command import api_command
7
+ from wexample_cli.decorator.command import command
8
+ from wexample_cli.decorator.middleware import middleware
9
+ from wexample_cli.decorator.option import option
10
+ from wexample_wex_addon_app.middleware.app_middleware import AppMiddleware
11
+ from wexample_wex_core.const.globals import COMMAND_TYPE_ADDON
12
+
13
+ if TYPE_CHECKING:
14
+ from wexample_app.response.abstract_response import AbstractResponse
15
+ from wexample_cli.context.execution_context import ExecutionContext
16
+ from wexample_wex_addon_app.workdir.managed_workdir import ManagedWorkdir
17
+
18
+
19
+ @option(name="title", type=str, required=True, description="What the process is called.")
20
+ @option(
21
+ name="type",
22
+ short_name="ty",
23
+ type=str,
24
+ required=True,
25
+ description="The process type it runs, by its registry name.",
26
+ )
27
+ @option(
28
+ name="selection",
29
+ type=str,
30
+ required=True,
31
+ description="Uuid of the selection it runs on, a record under `.wex/data/selection/`.",
32
+ )
33
+ @option(
34
+ name="options",
35
+ type=str,
36
+ required=False,
37
+ default="{}",
38
+ description="Its options, a JSON object, handed to the type as they are.",
39
+ )
40
+ @option(
41
+ name="severity",
42
+ short_name="sv",
43
+ type=str,
44
+ required=False,
45
+ description="How serious what it finds is (`warning`, `error`…); left out, the reader's default.",
46
+ )
47
+ @api_command(commit_subject="process creation")
48
+ @middleware(middleware=AppMiddleware)
49
+ @command(
50
+ type=COMMAND_TYPE_ADDON,
51
+ description="Create a process of the app, its record under `.wex/data/process/`. The selection it names must be one the app holds.",
52
+ tags=[
53
+ EffectTag.CREATE,
54
+ ScopeTag.APP,
55
+ ScopeTag.LOCAL,
56
+ ],
57
+ )
58
+ def process__process__create(
59
+ context: ExecutionContext,
60
+ app_workdir: ManagedWorkdir,
61
+ title: str,
62
+ type: str,
63
+ selection: str,
64
+ options: str = "{}",
65
+ severity: str | None = None,
66
+ ) -> AbstractResponse:
67
+ import json
68
+ from datetime import UTC, datetime
69
+
70
+ from wexample_app.response.dict_response import DictResponse
71
+ from wexample_wex_core.helper.data_record import data_kind_dir, new_record_path
72
+
73
+ from wexample_wex_addon_process.const.process import (
74
+ KIND_PROCESS,
75
+ KIND_SELECTION,
76
+ PROCESS_KEY_DATE_CREATED,
77
+ PROCESS_KEY_OPTIONS,
78
+ PROCESS_KEY_SELECTION_ID,
79
+ PROCESS_KEY_SEVERITY,
80
+ PROCESS_KEY_TITLE,
81
+ PROCESS_KEY_TYPE,
82
+ )
83
+ from wexample_wex_addon_process.record.record_file import record_path, write_record
84
+
85
+ app_path = app_workdir.get_path()
86
+ if not record_path(app_path, KIND_SELECTION, selection).is_file():
87
+ from wexample_wex_addon_process.exception.record_not_found_exception import (
88
+ RecordNotFoundException,
89
+ )
90
+
91
+ raise RecordNotFoundException(kind=KIND_SELECTION, record_id=selection)
92
+
93
+ record = new_record_path(data_kind_dir(app_path, KIND_PROCESS))
94
+ values = {
95
+ PROCESS_KEY_TITLE: title,
96
+ PROCESS_KEY_TYPE: type,
97
+ PROCESS_KEY_SELECTION_ID: selection,
98
+ PROCESS_KEY_OPTIONS: json.loads(options),
99
+ PROCESS_KEY_DATE_CREATED: datetime.now(UTC).isoformat(timespec="seconds"),
100
+ }
101
+ if severity:
102
+ values[PROCESS_KEY_SEVERITY] = severity
103
+ write_record(record, values)
104
+
105
+ return DictResponse(
106
+ kernel=context.kernel,
107
+ content={"id": record.stem, "path": str(record), "written": [str(record)]},
108
+ )
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from wexample_cli.const.tags import EffectTag, ScopeTag
6
+ from wexample_cli.decorator.api_command import api_command
7
+ from wexample_cli.decorator.command import command
8
+ from wexample_cli.decorator.middleware import middleware
9
+ from wexample_cli.decorator.option import option
10
+ from wexample_wex_addon_app.middleware.app_middleware import AppMiddleware
11
+ from wexample_wex_core.const.globals import COMMAND_TYPE_ADDON
12
+
13
+ if TYPE_CHECKING:
14
+ from wexample_app.response.abstract_response import AbstractResponse
15
+ from wexample_cli.context.execution_context import ExecutionContext
16
+ from wexample_wex_addon_app.workdir.managed_workdir import ManagedWorkdir
17
+
18
+
19
+ @option(name="id", type=str, required=True, description="Uuid of the process to take away.")
20
+ @api_command(commit_subject="process removal")
21
+ @middleware(middleware=AppMiddleware)
22
+ @command(
23
+ type=COMMAND_TYPE_ADDON,
24
+ description="Take a process of the app away, its record and the directory beside it. Its runs stay: they are the history of what it did.",
25
+ tags=[
26
+ EffectTag.DELETE,
27
+ ScopeTag.APP,
28
+ ScopeTag.LOCAL,
29
+ ],
30
+ )
31
+ def process__process__remove(
32
+ context: ExecutionContext,
33
+ app_workdir: ManagedWorkdir,
34
+ id: str,
35
+ ) -> AbstractResponse:
36
+ from wexample_app.response.dict_response import DictResponse
37
+ from wexample_wex_core.helper.data_record import record_remove
38
+
39
+ from wexample_wex_addon_process.const.process import KIND_PROCESS
40
+ from wexample_wex_addon_process.record.record_file import record_path
41
+
42
+ removed = record_remove(record_path(app_workdir.get_path(), KIND_PROCESS, id))
43
+ return DictResponse(
44
+ kernel=context.kernel,
45
+ content={"id": id, "written": [str(path) for path in removed]},
46
+ )
@@ -0,0 +1,107 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from wexample_cli.const.tags import EffectTag, ScopeTag
6
+ from wexample_cli.decorator.api_command import api_command
7
+ from wexample_cli.decorator.command import command
8
+ from wexample_cli.decorator.middleware import middleware
9
+ from wexample_cli.decorator.option import option
10
+ from wexample_wex_addon_app.middleware.app_middleware import AppMiddleware
11
+ from wexample_wex_core.const.globals import COMMAND_TYPE_ADDON
12
+
13
+ if TYPE_CHECKING:
14
+ from wexample_app.response.abstract_response import AbstractResponse
15
+ from wexample_cli.context.execution_context import ExecutionContext
16
+ from wexample_wex_addon_app.workdir.managed_workdir import ManagedWorkdir
17
+
18
+
19
+ @option(name="id", type=str, required=True, description="Uuid of the process to rewrite.")
20
+ @option(name="title", type=str, required=True, description="What the process is called.")
21
+ @option(
22
+ name="type",
23
+ short_name="ty",
24
+ type=str,
25
+ required=True,
26
+ description="The process type it runs, by its registry name.",
27
+ )
28
+ @option(
29
+ name="selection",
30
+ type=str,
31
+ required=False,
32
+ description="Uuid of the selection it runs on, a record under `.wex/data/selection/`; left out, it keeps its own.",
33
+ )
34
+ @option(
35
+ name="options",
36
+ type=str,
37
+ required=False,
38
+ default="{}",
39
+ description="Its options, a JSON object, handed to the type as they are.",
40
+ )
41
+ @option(
42
+ name="severity",
43
+ short_name="sv",
44
+ type=str,
45
+ required=False,
46
+ description="How serious what it finds is (`warning`, `error`…); left out, the reader's default.",
47
+ )
48
+ @api_command(commit_subject="process edition")
49
+ @middleware(middleware=AppMiddleware)
50
+ @command(
51
+ type=COMMAND_TYPE_ADDON,
52
+ description="Rewrite a process of the app from the fields a form holds. Fields of the record it does not take stay as they were.",
53
+ tags=[
54
+ EffectTag.WRITE,
55
+ ScopeTag.APP,
56
+ ScopeTag.LOCAL,
57
+ ],
58
+ )
59
+ def process__process__update(
60
+ context: ExecutionContext,
61
+ app_workdir: ManagedWorkdir,
62
+ id: str,
63
+ title: str,
64
+ type: str,
65
+ selection: str | None = None,
66
+ options: str = "{}",
67
+ severity: str | None = None,
68
+ ) -> AbstractResponse:
69
+ import json
70
+
71
+ from wexample_app.response.dict_response import DictResponse
72
+
73
+ from wexample_wex_addon_process.const.process import (
74
+ KIND_PROCESS,
75
+ KIND_SELECTION,
76
+ PROCESS_KEY_OPTIONS,
77
+ PROCESS_KEY_SELECTION_ID,
78
+ PROCESS_KEY_SEVERITY,
79
+ PROCESS_KEY_TITLE,
80
+ PROCESS_KEY_TYPE,
81
+ )
82
+ from wexample_wex_addon_process.exception.record_not_found_exception import (
83
+ RecordNotFoundException,
84
+ )
85
+ from wexample_wex_addon_process.record.record_file import record_path, write_record
86
+
87
+ app_path = app_workdir.get_path()
88
+ record = record_path(app_path, KIND_PROCESS, id)
89
+ if not record.is_file():
90
+ raise RecordNotFoundException(kind=KIND_PROCESS, record_id=id)
91
+ values = {
92
+ PROCESS_KEY_TITLE: title,
93
+ PROCESS_KEY_TYPE: type,
94
+ PROCESS_KEY_OPTIONS: json.loads(options),
95
+ }
96
+ if selection:
97
+ if not record_path(app_path, KIND_SELECTION, selection).is_file():
98
+ raise RecordNotFoundException(kind=KIND_SELECTION, record_id=selection)
99
+ values[PROCESS_KEY_SELECTION_ID] = selection
100
+ if severity:
101
+ values[PROCESS_KEY_SEVERITY] = severity
102
+ write_record(record, values)
103
+
104
+ return DictResponse(
105
+ kernel=context.kernel,
106
+ content={"id": id, "path": str(record), "written": [str(record)]},
107
+ )
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from wexample_cli.const.tags import EffectTag, ScopeTag
6
+ from wexample_cli.decorator.api_command import api_command
7
+ from wexample_cli.decorator.command import command
8
+ from wexample_cli.decorator.middleware import middleware
9
+ from wexample_cli.decorator.option import option
10
+ from wexample_wex_addon_app.middleware.app_middleware import AppMiddleware
11
+ from wexample_wex_core.const.globals import COMMAND_TYPE_ADDON
12
+
13
+ if TYPE_CHECKING:
14
+ from wexample_app.response.abstract_response import AbstractResponse
15
+ from wexample_cli.context.execution_context import ExecutionContext
16
+ from wexample_wex_addon_app.workdir.managed_workdir import ManagedWorkdir
17
+
18
+
19
+ @option(name="id", type=str, required=True, description="Uuid of the run to stop.")
20
+ @api_command(commit_subject="process run cancel")
21
+ @middleware(middleware=AppMiddleware)
22
+ @command(
23
+ type=COMMAND_TYPE_ADDON,
24
+ description="Ask a run to stop. Whoever executes it reads the request between two items, so the file under way is finished first.",
25
+ tags=[
26
+ EffectTag.WRITE,
27
+ ScopeTag.APP,
28
+ ScopeTag.LOCAL,
29
+ ],
30
+ )
31
+ def process__run__cancel(
32
+ context: ExecutionContext,
33
+ app_workdir: ManagedWorkdir,
34
+ id: str,
35
+ ) -> AbstractResponse:
36
+ from wexample_app.response.dict_response import DictResponse
37
+
38
+ from wexample_wex_addon_process.const.process import (
39
+ KIND_PROCESS_RUN,
40
+ RUN_KEY_CANCEL,
41
+ )
42
+ from wexample_wex_addon_process.record.record_file import record_path, write_record
43
+
44
+ path = record_path(app_workdir.get_path(), KIND_PROCESS_RUN, id)
45
+ if not path.is_file():
46
+ from wexample_wex_addon_process.exception.record_not_found_exception import (
47
+ RecordNotFoundException,
48
+ )
49
+
50
+ raise RecordNotFoundException(kind=KIND_PROCESS_RUN, record_id=id)
51
+ write_record(path, {RUN_KEY_CANCEL: True})
52
+
53
+ # Nothing to commit: runs stay out of the project's history.
54
+ return DictResponse(kernel=context.kernel, content={"id": id, "path": str(path), "written": []})
@@ -0,0 +1,144 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from wexample_cli.const.tags import EffectTag, ScopeTag
6
+ from wexample_cli.decorator.api_command import api_command
7
+ from wexample_cli.decorator.command import command
8
+ from wexample_cli.decorator.middleware import middleware
9
+ from wexample_cli.decorator.option import option
10
+ from wexample_wex_addon_app.middleware.app_middleware import AppMiddleware
11
+ from wexample_wex_core.const.globals import COMMAND_TYPE_ADDON
12
+
13
+ if TYPE_CHECKING:
14
+ from wexample_app.response.abstract_response import AbstractResponse
15
+ from wexample_cli.context.execution_context import ExecutionContext
16
+ from wexample_wex_addon_app.workdir.managed_workdir import ManagedWorkdir
17
+
18
+
19
+ @option(
20
+ name="process",
21
+ type=str,
22
+ required=True,
23
+ description="Uuid of the process to run: a record under `.wex/data/process/`, or one an addon ships (`/data/process` lists them).",
24
+ )
25
+ @option(
26
+ name="options",
27
+ short_name="o",
28
+ type=str,
29
+ required=False,
30
+ description="Options of the run's own, a JSON object laid over the process's.",
31
+ )
32
+ @option(
33
+ name="action",
34
+ type=str,
35
+ required=False,
36
+ description="What the run does instead of the process's own action.",
37
+ )
38
+ @option(
39
+ name="items",
40
+ type=str,
41
+ required=False,
42
+ description="What it goes through instead of the selection, a JSON list of paths.",
43
+ )
44
+ @option(
45
+ name="commit_work",
46
+ short_name="cw",
47
+ type=bool,
48
+ is_flag=True,
49
+ default=False,
50
+ description="Have the run commit the paths it declares it wrote, once it is done.",
51
+ )
52
+ @option(
53
+ name="work_author",
54
+ short_name="wa",
55
+ type=str,
56
+ required=False,
57
+ description="`Name <email>` the run's commit is signed by.",
58
+ )
59
+ @api_command(commit_subject="process run")
60
+ @middleware(middleware=AppMiddleware)
61
+ @command(
62
+ type=COMMAND_TYPE_ADDON,
63
+ description="Open a run of a process, pending, its record under `.wex/data/process_run/` — what a worker picks up, or `process::run/execute` runs by hand. Runs stay out of the project's history: their directory ignores them.",
64
+ tags=[
65
+ EffectTag.CREATE,
66
+ ScopeTag.APP,
67
+ ScopeTag.LOCAL,
68
+ ],
69
+ )
70
+ def process__run__create(
71
+ context: ExecutionContext,
72
+ app_workdir: ManagedWorkdir,
73
+ process: str,
74
+ action: str | None = None,
75
+ items: str | None = None,
76
+ options: str | None = None,
77
+ commit_work: bool = False,
78
+ work_author: str | None = None,
79
+ ) -> AbstractResponse:
80
+ import json
81
+ from datetime import UTC, datetime
82
+
83
+ from wexample_app.response.dict_response import DictResponse
84
+ from wexample_wex_core.helper.data_record import data_kind_dir, new_record_path
85
+
86
+ from wexample_wex_addon_process.builtin.builtin_process_registry import (
87
+ BuiltinProcessRegistry,
88
+ )
89
+ from wexample_wex_addon_process.const.process import (
90
+ KIND_PROCESS,
91
+ KIND_PROCESS_RUN,
92
+ RUN_KEY_ACTION,
93
+ RUN_KEY_AUTHOR,
94
+ RUN_KEY_COMMIT,
95
+ RUN_KEY_DATE_CREATED,
96
+ RUN_KEY_ITEMS,
97
+ RUN_KEY_OPTIONS,
98
+ RUN_KEY_PROCESS_ID,
99
+ RUN_KEY_STATE,
100
+ STATE_PENDING,
101
+ )
102
+ from wexample_wex_addon_process.record.record_file import record_path, write_record
103
+
104
+ app_path = app_workdir.get_path()
105
+ if (
106
+ not record_path(app_path, KIND_PROCESS, process).is_file()
107
+ and BuiltinProcessRegistry.from_kernel(context.kernel).get(process) is None
108
+ ):
109
+ from wexample_wex_addon_process.exception.record_not_found_exception import (
110
+ RecordNotFoundException,
111
+ )
112
+
113
+ raise RecordNotFoundException(kind=KIND_PROCESS, record_id=process)
114
+
115
+ directory = data_kind_dir(app_path, KIND_PROCESS_RUN)
116
+ directory.mkdir(parents=True, exist_ok=True)
117
+ # A process is a declaration worth committing and a run is a dated fact that is
118
+ # not, so the directory holding them says so itself rather than relying on every
119
+ # project to have thought of it.
120
+ ignore = directory / ".gitignore"
121
+ written = []
122
+ if not ignore.is_file():
123
+ ignore.write_text("*\n!.gitignore\n", encoding="utf-8")
124
+ written.append(str(ignore))
125
+
126
+ # The overrides only when there are some: a run that names none is the process as
127
+ # declared, and its record says nothing more.
128
+ values = {
129
+ RUN_KEY_PROCESS_ID: process,
130
+ RUN_KEY_STATE: STATE_PENDING,
131
+ RUN_KEY_ACTION: action,
132
+ RUN_KEY_ITEMS: json.loads(items) if items else None,
133
+ RUN_KEY_OPTIONS: json.loads(options) if options else None,
134
+ RUN_KEY_COMMIT: commit_work,
135
+ RUN_KEY_AUTHOR: work_author if commit_work else None,
136
+ RUN_KEY_DATE_CREATED: datetime.now(UTC).isoformat(timespec="seconds"),
137
+ }
138
+ record = new_record_path(directory)
139
+ write_record(record, {key: value for key, value in values.items() if value is not None})
140
+
141
+ return DictResponse(
142
+ kernel=context.kernel,
143
+ content={"id": record.stem, "path": str(record), "written": written},
144
+ )