wexample-wex-addon-process 5.0.1__tar.gz → 5.0.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.
Files changed (63) hide show
  1. wexample_wex_addon_process-5.0.2/PKG-INFO +111 -0
  2. wexample_wex_addon_process-5.0.2/README.md +93 -0
  3. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/pyproject.toml +4 -4
  4. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/__init__.py +0 -0
  5. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/builtin/__init__.py +0 -0
  6. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/builtin/builtin_process.py +0 -0
  7. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/builtin/builtin_process_registry.py +0 -0
  8. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/__init__.py +0 -0
  9. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/process/__init__.py +0 -0
  10. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/process/create.py +0 -0
  11. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/process/remove.py +0 -0
  12. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/process/update.py +0 -0
  13. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/run/__init__.py +0 -0
  14. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/run/cancel.py +0 -0
  15. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/run/create.py +0 -0
  16. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/run/execute.py +0 -0
  17. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/selection/__init__.py +0 -0
  18. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/selection/create.py +0 -0
  19. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/selection/remove.py +0 -0
  20. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/selection/update.py +0 -0
  21. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/worker/__init__.py +0 -0
  22. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/commands/worker/start.py +0 -0
  23. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/const/__init__.py +0 -0
  24. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/const/filestate.py +0 -0
  25. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/const/process.py +0 -0
  26. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/exception/__init__.py +0 -0
  27. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/exception/record_not_found_exception.py +0 -0
  28. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/exception/run_cancelled_exception.py +0 -0
  29. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/helper/__init__.py +0 -0
  30. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_addon_manager.py +0 -0
  31. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/__init__.py +0 -0
  32. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/abstract_process_type.py +0 -0
  33. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/family/__init__.py +0 -0
  34. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/family/abstract_process_type_family.py +0 -0
  35. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/family/file_state_process_type_family.py +0 -0
  36. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/filestate/__init__.py +0 -0
  37. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/filestate/file_state_option_process_type.py +0 -0
  38. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/filestate/file_state_report_process_type.py +0 -0
  39. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/process_context.py +0 -0
  40. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/process_type_registry.py +0 -0
  41. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/process_type/selection.py +0 -0
  42. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/py.typed +0 -0
  43. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/record/__init__.py +0 -0
  44. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/record/record_file.py +0 -0
  45. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/runner/__init__.py +0 -0
  46. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/runner/process_runner.py +0 -0
  47. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/selection/__init__.py +0 -0
  48. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/selection/abstract_code_from_directory_selection.py +0 -0
  49. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/selection/abstract_process_selection.py +0 -0
  50. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/selection/git_changes_selection.py +0 -0
  51. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/selection/selection_registry.py +0 -0
  52. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/service/__init__.py +0 -0
  53. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/src/wexample_wex_addon_process/service/process_run_service.py +0 -0
  54. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/tests/.gitkeep +0 -0
  55. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/tests/unit/__init__.py +0 -0
  56. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/tests/unit/test_artifacts.py +0 -0
  57. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/tests/unit/test_long_runs.py +0 -0
  58. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/tests/unit/test_run_overrides.py +0 -0
  59. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/tests/unit/test_selection.py +0 -0
  60. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/tests/unit/test_skip_and_failure.py +0 -0
  61. {wexample_wex_addon_process-5.0.1 → wexample_wex_addon_process-5.0.2}/tests/unit/test_verdict_locations.py +0 -0
  62. wexample_wex_addon_process-5.0.1/PKG-INFO +0 -422
  63. wexample_wex_addon_process-5.0.1/README.md +0 -404
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.1
2
+ Name: wexample-wex-addon-process
3
+ Version: 5.0.2
4
+ Summary: Executes process runs defined on disk—either directly or by consuming them from a queue as a worker—against filestate selections
5
+ Author-Email: weeger <contact@wexample.com>
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.10
11
+ Requires-Dist: wexample-file>=9.2.7
12
+ Requires-Dist: wexample-queue>=2.0.6
13
+ Requires-Dist: wexample-wex-addon-app>=34.2.0
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == "dev"
16
+ Requires-Dist: pytest-cov; extra == "dev"
17
+ Description-Content-Type: text/markdown
18
+
19
+ # wex-addon-process
20
+
21
+ Version: 5.0.2
22
+
23
+ Executes process runs defined on disk—either directly or by consuming them from a queue as a worker—against filestate selections
24
+
25
+ ## Table of Contents
26
+
27
+ - [Install](#install)
28
+ - [Tests](#tests)
29
+ - [Roadmap](#roadmap)
30
+ - [Status Compatibility](#status-compatibility)
31
+ - [Useful Links](#useful-links)
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install wexample-wex-addon-process
37
+ ```
38
+
39
+ Requires Python >=3.10.
40
+
41
+ ## Tests
42
+
43
+ This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
44
+
45
+ ### Installation
46
+
47
+ First, install the required testing dependencies:
48
+ ```bash
49
+ .venv/bin/python -m pip install pytest pytest-cov
50
+ ```
51
+
52
+ ### Basic Usage
53
+
54
+ Run all tests with coverage:
55
+ ```bash
56
+ .venv/bin/python -m pytest --cov --cov-report=html
57
+ ```
58
+
59
+ ### Common Commands
60
+ ```bash
61
+ # Run tests with coverage for a specific module
62
+ .venv/bin/python -m pytest --cov=your_module
63
+
64
+ # Show which lines are not covered
65
+ .venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
66
+
67
+ # Generate an HTML coverage report
68
+ .venv/bin/python -m pytest --cov=your_module --cov-report=html
69
+
70
+ # Combine terminal and HTML reports
71
+ .venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
72
+
73
+ # Run specific test file with coverage
74
+ .venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
75
+ ```
76
+
77
+ ### Viewing HTML Reports
78
+
79
+ After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.
80
+
81
+ ### Coverage Threshold
82
+
83
+ To enforce a minimum coverage percentage:
84
+ ```bash
85
+ .venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
86
+ ```
87
+
88
+ This will cause the test suite to fail if coverage drops below 80%.
89
+
90
+ ## Known Limitations & Roadmap
91
+
92
+ Current limitations and planned features are tracked in the GitHub issues.
93
+
94
+ See the [project roadmap](https://github.com/wexample/python-wex-addon-process/issues) for upcoming features and improvements.
95
+
96
+ ## Status & Compatibility
97
+
98
+ **Maturity**: Production-ready
99
+
100
+ **Python Support**: >=3.10
101
+
102
+ **OS Support**: Linux, macOS, Windows
103
+
104
+ **Status**: Actively maintained
105
+
106
+ ## Useful Links
107
+
108
+ - **Homepage**: https://github.com/wexample/python-wex-addon-process
109
+ - **Issue Tracker**: https://github.com/wexample/python-wex-addon-process/issues
110
+ - **Discussions**: https://github.com/wexample/python-wex-addon-process/discussions
111
+ - **PyPI**: [pypi.org/project/wexample-wex-addon-process](https://pypi.org/project/wexample-wex-addon-process/)
@@ -0,0 +1,93 @@
1
+ # wex-addon-process
2
+
3
+ Version: 5.0.2
4
+
5
+ Executes process runs defined on disk—either directly or by consuming them from a queue as a worker—against filestate selections
6
+
7
+ ## Table of Contents
8
+
9
+ - [Install](#install)
10
+ - [Tests](#tests)
11
+ - [Roadmap](#roadmap)
12
+ - [Status Compatibility](#status-compatibility)
13
+ - [Useful Links](#useful-links)
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install wexample-wex-addon-process
19
+ ```
20
+
21
+ Requires Python >=3.10.
22
+
23
+ ## Tests
24
+
25
+ This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
26
+
27
+ ### Installation
28
+
29
+ First, install the required testing dependencies:
30
+ ```bash
31
+ .venv/bin/python -m pip install pytest pytest-cov
32
+ ```
33
+
34
+ ### Basic Usage
35
+
36
+ Run all tests with coverage:
37
+ ```bash
38
+ .venv/bin/python -m pytest --cov --cov-report=html
39
+ ```
40
+
41
+ ### Common Commands
42
+ ```bash
43
+ # Run tests with coverage for a specific module
44
+ .venv/bin/python -m pytest --cov=your_module
45
+
46
+ # Show which lines are not covered
47
+ .venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
48
+
49
+ # Generate an HTML coverage report
50
+ .venv/bin/python -m pytest --cov=your_module --cov-report=html
51
+
52
+ # Combine terminal and HTML reports
53
+ .venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
54
+
55
+ # Run specific test file with coverage
56
+ .venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
57
+ ```
58
+
59
+ ### Viewing HTML Reports
60
+
61
+ After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.
62
+
63
+ ### Coverage Threshold
64
+
65
+ To enforce a minimum coverage percentage:
66
+ ```bash
67
+ .venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
68
+ ```
69
+
70
+ This will cause the test suite to fail if coverage drops below 80%.
71
+
72
+ ## Known Limitations & Roadmap
73
+
74
+ Current limitations and planned features are tracked in the GitHub issues.
75
+
76
+ See the [project roadmap](https://github.com/wexample/python-wex-addon-process/issues) for upcoming features and improvements.
77
+
78
+ ## Status & Compatibility
79
+
80
+ **Maturity**: Production-ready
81
+
82
+ **Python Support**: >=3.10
83
+
84
+ **OS Support**: Linux, macOS, Windows
85
+
86
+ **Status**: Actively maintained
87
+
88
+ ## Useful Links
89
+
90
+ - **Homepage**: https://github.com/wexample/python-wex-addon-process
91
+ - **Issue Tracker**: https://github.com/wexample/python-wex-addon-process/issues
92
+ - **Discussions**: https://github.com/wexample/python-wex-addon-process/discussions
93
+ - **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 = "5.0.1"
9
+ version = "5.0.2"
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.2.0",
22
- "wexample-queue>=2.0.0",
23
- "wexample-wex-addon-app>=34.1.0",
21
+ "wexample-file>=9.2.7",
22
+ "wexample-queue>=2.0.6",
23
+ "wexample-wex-addon-app>=34.2.0",
24
24
  ]
25
25
 
26
26
  [project.readme]
@@ -1,422 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: wexample-wex-addon-process
3
- Version: 5.0.1
4
- Summary: Executes process runs defined on disk—either directly or by consuming them from a queue as a worker—against filestate selections
5
- Author-Email: weeger <contact@wexample.com>
6
- License: MIT
7
- Classifier: Programming Language :: Python :: 3
8
- Classifier: License :: OSI Approved :: MIT License
9
- Classifier: Operating System :: OS Independent
10
- Requires-Python: >=3.10
11
- Requires-Dist: wexample-file>=9.2.0
12
- Requires-Dist: wexample-queue>=2.0.0
13
- Requires-Dist: wexample-wex-addon-app>=34.1.0
14
- Provides-Extra: dev
15
- Requires-Dist: pytest; extra == "dev"
16
- Requires-Dist: pytest-cov; extra == "dev"
17
- Description-Content-Type: text/markdown
18
-
19
- # wex-addon-process
20
-
21
- Version: 5.0.1
22
-
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
-
25
- Nothing here talks to a database. A run names a process, the process names a selection and a type, the selection names files, and all four are records under the app's own `.wex/data/` — which is what lets a process run on a repository that has no board at all, and what makes a demand survive a broker that falls over.
26
-
27
- ## Running one by hand
28
-
29
- ```bash
30
- wex process::run/execute --run 8020e9fb-05b8-4da7-857d-d3947449b6f7
31
- ```
32
-
33
- The run record goes from `pending` to `running` to `complete`, gaining its dates,
34
- its counters and, at the end, whatever the type wrote in `data`. A run in any
35
- state but `pending` is left alone: a queue redelivers, and a run is done once.
36
-
37
- ## Running as a worker
38
-
39
- ```bash
40
- wex process::worker/start
41
- ```
42
-
43
- Blocks, consuming the `process_run` queue, and rings back on `process_run_event`
44
- every time a run moves — on each state change, and at most four times a second while
45
- it advances. This is the process a worker container runs, and there may be as
46
- many as the parallelism asked for.
47
-
48
- ## The types on offer
49
-
50
- A type is named by the family that answers for it. The whole of the `filestate`
51
- family is read off filestate's own options — every option that can produce an
52
- operation is a treatment a process may name:
53
-
54
- ```
55
- filestate:report says what the selection takes, changes nothing
56
- filestate:mode permissions, and ownership with them
57
- filestate:should_exist create when missing, delete when forbidden
58
- filestate:name rename to a required form
59
- filestate:content filestate:text write, trim, sort, keep unique lines
60
- filestate:yaml filestate:structured_keys
61
- filestate:should_contain_lines and its `should_not_` twin
62
- filestate:managed_blocks filestate:class filestate:on_bad_format
63
- ```
64
-
65
- Nothing is written per treatment: an option added to filestate is a type
66
- available here the same day, under the very name filestate gives it.
67
-
68
- The process's `options` object is the configuration handed to each file, and has
69
- to carry the option the type is named after:
70
-
71
- ```json
72
- // the options of a process of type filestate:mode
73
- {"mode": "600", "action": "dry_run"}
74
- ```
75
-
76
- `action` is read by the family and handed to nobody. It picks which of
77
- filestate's three verbs is asked, the same three the command line offers on an
78
- app's declared state:
79
-
80
- - `check`, the default, says what is wrong with each selected file and changes
81
- nothing. Its `data` counts the files `checked` and `failing`, and lists under
82
- `files` only those at fault, each with its `code`, `message`, `parameters` and
83
- `remedy`, the name of the operation that would fix it or null when nothing can.
84
- - `dry_run` lists the operations one pass would apply.
85
- - `fix` applies them, then lists what is still wrong, the way `check` does.
86
-
87
- A rule that only observes, such as `max_lines`, is a type like the others: it has
88
- no remedy, so a `fix` changes nothing and reports it again.
89
-
90
- ## Writing a type
91
-
92
- A type of your own is a name and a `run()`:
93
-
94
- ```python
95
- class ReviewProcessType(AbstractProcessType):
96
- name = "agent:review"
97
- label = "Review"
98
-
99
- def run(self, context: ProcessContext) -> dict[str, Any]:
100
- for path in context.each():
101
- ...
102
-
103
- return {"files": len(context.files)}
104
- ```
105
-
106
- `context.each()` hands the files over one at a time and keeps the run up to
107
- date: the file being worked on is the run's `current`, and it is counted once
108
- the loop comes back for the next one. A file whose treatment raises is never
109
- counted and stays `current`, so a failed run says where it failed.
110
-
111
- What `run()` returns goes into the run's `data`. Raising is how a type says it
112
- could not go on — the failure is written down for it, so nothing in a type has
113
- to know what a failed run looks like.
114
-
115
- A type that touches files should not be written this way: it belongs to the
116
- filestate family, where the operation, its description and its undo already
117
- exist.
118
-
119
- ## Table of Contents
120
-
121
- - [Running one by hand](#running-one-by-hand)
122
- - [Running as a worker](#running-as-a-worker)
123
- - [The types on offer](#the-types-on-offer)
124
- - [Writing a type](#writing-a-type)
125
- - [Installation](#installation)
126
- - [What a run may ask for itself](#what-a-run-may-ask-for-itself)
127
- - [Runs that last a night](#runs-that-last-a-night)
128
- - [An item that fails, a run that is cancelled](#an-item-that-fails-a-run-that-is-cancelled)
129
- - [Tests](#tests)
130
- - [Architecture](#architecture)
131
- - [Integration in the Suite](#integration-in-the-suite)
132
- - [Dependencies](#dependencies)
133
- - [Versioning & Compatibility Policy](#versioning--compatibility-policy)
134
- - [License](#license)
135
- - [About us](#about-us)
136
- - [Known Limitations & Roadmap](#known-limitations--roadmap)
137
- - [Status & Compatibility](#status--compatibility)
138
- - [Useful Links](#useful-links)
139
- - [Migration Notes](#migration-notes)
140
-
141
- ## Installation
142
-
143
- ```bash
144
- pip install wexample-wex-addon-process
145
- ```
146
-
147
- Requires Python >=3.10.
148
-
149
- A process and a selection are records, so a treatment can be declared without a board.
150
- Records are JSON, written and read by programs on both sides:
151
-
152
- ```json
153
- // .wex/data/selection/<uuid>.json
154
- {
155
- "title": "Markdowns",
156
- "patterns": "*.md\n!.wex/**"
157
- }
158
- ```
159
-
160
- ```json
161
- // .wex/data/process/<uuid>.json
162
- {
163
- "title": "What the Markdowns take",
164
- "type": "filestate:report",
165
- "selection_id": "<the selection's uuid>",
166
- "options": {}
167
- }
168
- ```
169
-
170
- Asking for a run is writing a third record:
171
-
172
- ```json
173
- // .wex/data/process_run/<uuid>.json
174
- {
175
- "process_id": "<the process's uuid>",
176
- "state": "pending"
177
- }
178
- ```
179
-
180
- Then:
181
-
182
- ```bash
183
- wex process::run/execute --run <the run's uuid>
184
- ```
185
-
186
- The same file is read back afterwards, holding what happened:
187
-
188
- ```json
189
- {
190
- "process_id": "...",
191
- "state": "complete",
192
- "items_total": 3,
193
- "items_done": 3,
194
- "data": {
195
- "files": 3,
196
- "bytes": 16,
197
- "by_extension": {".md": 3}
198
- }
199
- }
200
- ```
201
-
202
- ## What a run may ask for itself
203
-
204
- A run record may carry three keys of its own, written when it is created:
205
-
206
- - `action` — `check`, `dry_run` or `fix`, in place of the process's own, for this run.
207
- - `items` — paths relative to the app, taken in place of the selection. A path that is
208
- gone is listed under `items_missing` rather than failing the run; a path outside the
209
- app fails it.
210
- - `commit: true` — once the run completes, commit what it wrote and nothing else. The
211
- result replaces the key: `{"sha": ..., "files": [...]}`, `{"sha": null, "files": []}`
212
- when nothing changed, or `{"error": ..., "files": [...]}` when git refused — the
213
- changes stay either way.
214
-
215
- ```json
216
- {"process_id": "...", "state": "pending", "action": "fix", "items": ["docs/a.md"], "commit": true}
217
- ```
218
-
219
- What a run wrote is what its type declared through `context.touch(path)`: the operations
220
- of a filestate fix, the items an agent changed and their engrams.
221
-
222
- ## Runs that last a night
223
-
224
- A worker takes one message at a time from the queue and acknowledges it at once: the
225
- run's record, not the broker, says what became of it. The run is worked on in a thread
226
- of its own while the worker keeps the broker connection alive, so a run of several
227
- hours needs no RabbitMQ setting — neither `consumer_timeout` nor the heartbeat can cut
228
- it. A connection lost meanwhile is opened again once the run ends; the board is told of
229
- the run's moves on a best-effort basis and reads the record again anyway.
230
-
231
- A running run writes `date_alive` into its record every minute. One still `running`
232
- whose last sign of life is too old was interrupted — worker killed, machine stopped —
233
- and is said `failed`, never run again: by the next run of the same app, and by any
234
- worker that served that app, within a minute of finding its queue empty.
235
-
236
- | Variable | Default | What it sets |
237
- |---|---|---|
238
- | `PROCESS_RUN_STALE_AFTER` | `600` | Seconds without a sign of life after which a running run is taken for dead |
239
- | `RABBITMQ_HEARTBEAT` | the broker's | Seconds between two heartbeats on the worker's connection |
240
-
241
- There is no limit on how long a run may take.
242
-
243
- ## An item that fails, a run that is cancelled
244
-
245
- A type that cannot deal with one item says so with `context.fail(path, reason)` and
246
- moves on: the run still completes, and lists what it could not do under its data.
247
-
248
- ```json
249
- "data": {"errors": [{"path": "docs/a.pdf", "reason": "docling timed out"}]}
250
- ```
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
-
260
- To stop a run, write `"cancel": true` into its record. The runner checks it before every
261
- item (`context.each()`, `context.advance()`, or `context.check_cancel()` in a type's own
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
264
- starting.
265
-
266
- ## Tests
267
-
268
- This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
269
-
270
- ### Installation
271
-
272
- First, install the required testing dependencies:
273
- ```bash
274
- .venv/bin/python -m pip install pytest pytest-cov
275
- ```
276
-
277
- ### Basic Usage
278
-
279
- Run all tests with coverage:
280
- ```bash
281
- .venv/bin/python -m pytest --cov --cov-report=html
282
- ```
283
-
284
- ### Common Commands
285
- ```bash
286
- # Run tests with coverage for a specific module
287
- .venv/bin/python -m pytest --cov=your_module
288
-
289
- # Show which lines are not covered
290
- .venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
291
-
292
- # Generate an HTML coverage report
293
- .venv/bin/python -m pytest --cov=your_module --cov-report=html
294
-
295
- # Combine terminal and HTML reports
296
- .venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
297
-
298
- # Run specific test file with coverage
299
- .venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
300
- ```
301
-
302
- ### Viewing HTML Reports
303
-
304
- After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.
305
-
306
- ### Coverage Threshold
307
-
308
- To enforce a minimum coverage percentage:
309
- ```bash
310
- .venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
311
- ```
312
-
313
- This will cause the test suite to fail if coverage drops below 80%.
314
-
315
- ## Architecture
316
-
317
- The addon is one runner, a few types, and two ways to reach it.
318
-
319
- ### The runner
320
-
321
- src/wexample_wex_addon_process/runner/process_runner.py is where a run is taken from `pending` to `complete` or to `failed`. It reads the run, the process it names, the selection that process names, resolves the type, walks the files, and writes back at every state change.
322
-
323
- Three decisions are worth reading. A run in any state but `pending` is left where it is, because a queue redelivers and a run is done once. Progress — the files done and the file being worked on, `current` — is kept in memory and written down at most four times a second, each write followed by a ring: the other end reads the record when rung, and ten thousand files must not be ten thousand rewrites saying the same thing. A state change is never held back, and a failed run keeps `current` on the file it failed on. And the type never sees the run: it is handed a src/wexample_wex_addon_process/process_type/process_context.py holding the files, the options and a way to say how far it has got, so what a run is *in* is decided in one place.
324
-
325
- ### Reading a selection
326
-
327
- src/wexample_wex_addon_process/process_type/selection.py holds no list of files: a selection is a rule, and what it takes is read from disk each time it is asked for. The rule is `PathMatcher` from `wexample-file`, a line-for-line port of the PHP one the board uses; the walking is filestate's, the matcher being handed to a `ChildrenFilterOption` as its `filter`.
328
-
329
- Going through filestate to *enumerate* and not only to *change* is deliberate: a type that reads the files and a type that rewrites them are then handed the very same tree, and there is one answer to what a selection takes rather than two.
330
-
331
- The two implementations are kept honest against each other: three hundred random pattern sets, sixty paths and fifty directories were run through both and compared, with no difference.
332
-
333
- ### Types, and the families that declare them
334
-
335
- src/wexample_wex_addon_process/process_type/abstract_process_type.py is deliberately an ordinary class — no kernel, no attrs, no state. A type contributed from a bundle should be readable by whoever wrote the bundle rather than by whoever wrote wex.
336
-
337
- A type's name carries its family: `filestate:mode`, `agent:review`. The prefix is not a branch taken at run time — src/wexample_wex_addon_process/process_type/process_type_registry.py asks each family for its names once, and what a record names is found in that map or is an error. A family declares names; it decides nothing.
338
-
339
- src/wexample_wex_addon_process/process_type/family/file_state_process_type_family.py declares none of its types by hand. It walks filestate's `DefaultOptionsProvider` and keeps the options that override `create_required_operation` — the precise line between a treatment and a piece of structure, asked of the option itself rather than listed here. `mode` is a treatment, `children` is not. Thirteen types come out of it, and an option added to filestate is a fourteenth the same day.
340
-
341
- Each of those is a src/wexample_wex_addon_process/process_type/filestate/file_state_option_process_type.py, which holds no logic either: the process's `options` become the configuration asked of every selected file, and filestate turns the difference with the disk into operations that can be described, applied and undone. `action` in the options picks the verb: `check` returns verdicts per file, `dry_run` the operations a pass would apply, `fix` applies them.
342
-
343
- src/wexample_wex_addon_process/process_type/filestate/file_state_report_process_type.py is in that family and yet emits no operation, on filestate's own rule: a scope names something observable on disk, and a report changes nothing observable. An operation with no scope would be an operation nothing could ever run.
344
-
345
- ### The two entry points
346
-
347
- src/wexample_wex_addon_process/commands/run/execute.py runs one run, here and now, with no queue in sight. src/wexample_wex_addon_process/service/process_run_service.py is the same runner behind a `wexample-queue` service: it consumes `process_run`, and rings back on `process_run_event` with the same shape of message it received — `kind`, `id`, `workdir`. The runner is rebuilt for each message so that the workdir a ring names is the one the message came with: one worker serves every app.
348
-
349
- The message carries no work at all, only a pointer. A worker starting after the message was published, or reading a run somebody has since edited, sees what is true now rather than what was true when the button was pressed.
350
-
351
- ### What a record is called
352
-
353
- src/wexample_wex_addon_process/const/process.py spells every key once. The board writes these very names into the same files, so one changed here has to be changed there — this file and `ProcessRunHydrator` on the PHP side are two halves of one contract.
354
-
355
- ## Integration in the Suite
356
-
357
- This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.
358
-
359
- ### Related Packages
360
-
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
-
363
- ## Dependencies
364
-
365
- - wexample-file: >=9.2.0
366
- - wexample-queue: >=2.0.0
367
- - wexample-wex-addon-app: >=34.1.0
368
-
369
- ## Versioning & Compatibility Policy
370
-
371
- Wexample packages follow **Semantic Versioning** (SemVer):
372
-
373
- - **MAJOR**: Breaking changes
374
- - **MINOR**: New features, backward compatible
375
- - **PATCH**: Bug fixes, backward compatible
376
-
377
- We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.
378
-
379
- ## License
380
-
381
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
382
-
383
- Free to use in both personal and commercial projects.
384
-
385
- ## About us
386
-
387
- [Wexample](https://wexample.com) stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.
388
-
389
- This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.
390
-
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.
392
-
393
- Built and maintained by [Romain Weeger](https://weeger.io).
394
-
395
- ## Known Limitations & Roadmap
396
-
397
- Current limitations and planned features are tracked in the GitHub issues.
398
-
399
- See the [project roadmap](https://github.com/wexample/python-wex-addon-process/issues) for upcoming features and improvements.
400
-
401
- ## Status & Compatibility
402
-
403
- **Maturity**: Production-ready
404
-
405
- **Python Support**: >=3.10
406
-
407
- **OS Support**: Linux, macOS, Windows
408
-
409
- **Status**: Actively maintained
410
-
411
- ## Useful Links
412
-
413
- - **Homepage**: https://github.com/wexample/python-wex-addon-process
414
- - **Issue Tracker**: https://github.com/wexample/python-wex-addon-process/issues
415
- - **Discussions**: https://github.com/wexample/python-wex-addon-process/discussions
416
- - **PyPI**: [pypi.org/project/wexample-wex-addon-process](https://pypi.org/project/wexample-wex-addon-process/)
417
-
418
- ## Migration Notes
419
-
420
- When upgrading between major versions, refer to the migration guides in the documentation.
421
-
422
- Breaking changes are clearly documented with upgrade paths and examples.
@@ -1,404 +0,0 @@
1
- # wex-addon-process
2
-
3
- Version: 5.0.1
4
-
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
-
7
- Nothing here talks to a database. A run names a process, the process names a selection and a type, the selection names files, and all four are records under the app's own `.wex/data/` — which is what lets a process run on a repository that has no board at all, and what makes a demand survive a broker that falls over.
8
-
9
- ## Running one by hand
10
-
11
- ```bash
12
- wex process::run/execute --run 8020e9fb-05b8-4da7-857d-d3947449b6f7
13
- ```
14
-
15
- The run record goes from `pending` to `running` to `complete`, gaining its dates,
16
- its counters and, at the end, whatever the type wrote in `data`. A run in any
17
- state but `pending` is left alone: a queue redelivers, and a run is done once.
18
-
19
- ## Running as a worker
20
-
21
- ```bash
22
- wex process::worker/start
23
- ```
24
-
25
- Blocks, consuming the `process_run` queue, and rings back on `process_run_event`
26
- every time a run moves — on each state change, and at most four times a second while
27
- it advances. This is the process a worker container runs, and there may be as
28
- many as the parallelism asked for.
29
-
30
- ## The types on offer
31
-
32
- A type is named by the family that answers for it. The whole of the `filestate`
33
- family is read off filestate's own options — every option that can produce an
34
- operation is a treatment a process may name:
35
-
36
- ```
37
- filestate:report says what the selection takes, changes nothing
38
- filestate:mode permissions, and ownership with them
39
- filestate:should_exist create when missing, delete when forbidden
40
- filestate:name rename to a required form
41
- filestate:content filestate:text write, trim, sort, keep unique lines
42
- filestate:yaml filestate:structured_keys
43
- filestate:should_contain_lines and its `should_not_` twin
44
- filestate:managed_blocks filestate:class filestate:on_bad_format
45
- ```
46
-
47
- Nothing is written per treatment: an option added to filestate is a type
48
- available here the same day, under the very name filestate gives it.
49
-
50
- The process's `options` object is the configuration handed to each file, and has
51
- to carry the option the type is named after:
52
-
53
- ```json
54
- // the options of a process of type filestate:mode
55
- {"mode": "600", "action": "dry_run"}
56
- ```
57
-
58
- `action` is read by the family and handed to nobody. It picks which of
59
- filestate's three verbs is asked, the same three the command line offers on an
60
- app's declared state:
61
-
62
- - `check`, the default, says what is wrong with each selected file and changes
63
- nothing. Its `data` counts the files `checked` and `failing`, and lists under
64
- `files` only those at fault, each with its `code`, `message`, `parameters` and
65
- `remedy`, the name of the operation that would fix it or null when nothing can.
66
- - `dry_run` lists the operations one pass would apply.
67
- - `fix` applies them, then lists what is still wrong, the way `check` does.
68
-
69
- A rule that only observes, such as `max_lines`, is a type like the others: it has
70
- no remedy, so a `fix` changes nothing and reports it again.
71
-
72
- ## Writing a type
73
-
74
- A type of your own is a name and a `run()`:
75
-
76
- ```python
77
- class ReviewProcessType(AbstractProcessType):
78
- name = "agent:review"
79
- label = "Review"
80
-
81
- def run(self, context: ProcessContext) -> dict[str, Any]:
82
- for path in context.each():
83
- ...
84
-
85
- return {"files": len(context.files)}
86
- ```
87
-
88
- `context.each()` hands the files over one at a time and keeps the run up to
89
- date: the file being worked on is the run's `current`, and it is counted once
90
- the loop comes back for the next one. A file whose treatment raises is never
91
- counted and stays `current`, so a failed run says where it failed.
92
-
93
- What `run()` returns goes into the run's `data`. Raising is how a type says it
94
- could not go on — the failure is written down for it, so nothing in a type has
95
- to know what a failed run looks like.
96
-
97
- A type that touches files should not be written this way: it belongs to the
98
- filestate family, where the operation, its description and its undo already
99
- exist.
100
-
101
- ## Table of Contents
102
-
103
- - [Running one by hand](#running-one-by-hand)
104
- - [Running as a worker](#running-as-a-worker)
105
- - [The types on offer](#the-types-on-offer)
106
- - [Writing a type](#writing-a-type)
107
- - [Installation](#installation)
108
- - [What a run may ask for itself](#what-a-run-may-ask-for-itself)
109
- - [Runs that last a night](#runs-that-last-a-night)
110
- - [An item that fails, a run that is cancelled](#an-item-that-fails-a-run-that-is-cancelled)
111
- - [Tests](#tests)
112
- - [Architecture](#architecture)
113
- - [Integration in the Suite](#integration-in-the-suite)
114
- - [Dependencies](#dependencies)
115
- - [Versioning & Compatibility Policy](#versioning--compatibility-policy)
116
- - [License](#license)
117
- - [About us](#about-us)
118
- - [Known Limitations & Roadmap](#known-limitations--roadmap)
119
- - [Status & Compatibility](#status--compatibility)
120
- - [Useful Links](#useful-links)
121
- - [Migration Notes](#migration-notes)
122
-
123
- ## Installation
124
-
125
- ```bash
126
- pip install wexample-wex-addon-process
127
- ```
128
-
129
- Requires Python >=3.10.
130
-
131
- A process and a selection are records, so a treatment can be declared without a board.
132
- Records are JSON, written and read by programs on both sides:
133
-
134
- ```json
135
- // .wex/data/selection/<uuid>.json
136
- {
137
- "title": "Markdowns",
138
- "patterns": "*.md\n!.wex/**"
139
- }
140
- ```
141
-
142
- ```json
143
- // .wex/data/process/<uuid>.json
144
- {
145
- "title": "What the Markdowns take",
146
- "type": "filestate:report",
147
- "selection_id": "<the selection's uuid>",
148
- "options": {}
149
- }
150
- ```
151
-
152
- Asking for a run is writing a third record:
153
-
154
- ```json
155
- // .wex/data/process_run/<uuid>.json
156
- {
157
- "process_id": "<the process's uuid>",
158
- "state": "pending"
159
- }
160
- ```
161
-
162
- Then:
163
-
164
- ```bash
165
- wex process::run/execute --run <the run's uuid>
166
- ```
167
-
168
- The same file is read back afterwards, holding what happened:
169
-
170
- ```json
171
- {
172
- "process_id": "...",
173
- "state": "complete",
174
- "items_total": 3,
175
- "items_done": 3,
176
- "data": {
177
- "files": 3,
178
- "bytes": 16,
179
- "by_extension": {".md": 3}
180
- }
181
- }
182
- ```
183
-
184
- ## What a run may ask for itself
185
-
186
- A run record may carry three keys of its own, written when it is created:
187
-
188
- - `action` — `check`, `dry_run` or `fix`, in place of the process's own, for this run.
189
- - `items` — paths relative to the app, taken in place of the selection. A path that is
190
- gone is listed under `items_missing` rather than failing the run; a path outside the
191
- app fails it.
192
- - `commit: true` — once the run completes, commit what it wrote and nothing else. The
193
- result replaces the key: `{"sha": ..., "files": [...]}`, `{"sha": null, "files": []}`
194
- when nothing changed, or `{"error": ..., "files": [...]}` when git refused — the
195
- changes stay either way.
196
-
197
- ```json
198
- {"process_id": "...", "state": "pending", "action": "fix", "items": ["docs/a.md"], "commit": true}
199
- ```
200
-
201
- What a run wrote is what its type declared through `context.touch(path)`: the operations
202
- of a filestate fix, the items an agent changed and their engrams.
203
-
204
- ## Runs that last a night
205
-
206
- A worker takes one message at a time from the queue and acknowledges it at once: the
207
- run's record, not the broker, says what became of it. The run is worked on in a thread
208
- of its own while the worker keeps the broker connection alive, so a run of several
209
- hours needs no RabbitMQ setting — neither `consumer_timeout` nor the heartbeat can cut
210
- it. A connection lost meanwhile is opened again once the run ends; the board is told of
211
- the run's moves on a best-effort basis and reads the record again anyway.
212
-
213
- A running run writes `date_alive` into its record every minute. One still `running`
214
- whose last sign of life is too old was interrupted — worker killed, machine stopped —
215
- and is said `failed`, never run again: by the next run of the same app, and by any
216
- worker that served that app, within a minute of finding its queue empty.
217
-
218
- | Variable | Default | What it sets |
219
- |---|---|---|
220
- | `PROCESS_RUN_STALE_AFTER` | `600` | Seconds without a sign of life after which a running run is taken for dead |
221
- | `RABBITMQ_HEARTBEAT` | the broker's | Seconds between two heartbeats on the worker's connection |
222
-
223
- There is no limit on how long a run may take.
224
-
225
- ## An item that fails, a run that is cancelled
226
-
227
- A type that cannot deal with one item says so with `context.fail(path, reason)` and
228
- moves on: the run still completes, and lists what it could not do under its data.
229
-
230
- ```json
231
- "data": {"errors": [{"path": "docs/a.pdf", "reason": "docling timed out"}]}
232
- ```
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
-
242
- To stop a run, write `"cancel": true` into its record. The runner checks it before every
243
- item (`context.each()`, `context.advance()`, or `context.check_cancel()` in a type's own
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
246
- starting.
247
-
248
- ## Tests
249
-
250
- This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
251
-
252
- ### Installation
253
-
254
- First, install the required testing dependencies:
255
- ```bash
256
- .venv/bin/python -m pip install pytest pytest-cov
257
- ```
258
-
259
- ### Basic Usage
260
-
261
- Run all tests with coverage:
262
- ```bash
263
- .venv/bin/python -m pytest --cov --cov-report=html
264
- ```
265
-
266
- ### Common Commands
267
- ```bash
268
- # Run tests with coverage for a specific module
269
- .venv/bin/python -m pytest --cov=your_module
270
-
271
- # Show which lines are not covered
272
- .venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
273
-
274
- # Generate an HTML coverage report
275
- .venv/bin/python -m pytest --cov=your_module --cov-report=html
276
-
277
- # Combine terminal and HTML reports
278
- .venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
279
-
280
- # Run specific test file with coverage
281
- .venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
282
- ```
283
-
284
- ### Viewing HTML Reports
285
-
286
- After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.
287
-
288
- ### Coverage Threshold
289
-
290
- To enforce a minimum coverage percentage:
291
- ```bash
292
- .venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
293
- ```
294
-
295
- This will cause the test suite to fail if coverage drops below 80%.
296
-
297
- ## Architecture
298
-
299
- The addon is one runner, a few types, and two ways to reach it.
300
-
301
- ### The runner
302
-
303
- src/wexample_wex_addon_process/runner/process_runner.py is where a run is taken from `pending` to `complete` or to `failed`. It reads the run, the process it names, the selection that process names, resolves the type, walks the files, and writes back at every state change.
304
-
305
- Three decisions are worth reading. A run in any state but `pending` is left where it is, because a queue redelivers and a run is done once. Progress — the files done and the file being worked on, `current` — is kept in memory and written down at most four times a second, each write followed by a ring: the other end reads the record when rung, and ten thousand files must not be ten thousand rewrites saying the same thing. A state change is never held back, and a failed run keeps `current` on the file it failed on. And the type never sees the run: it is handed a src/wexample_wex_addon_process/process_type/process_context.py holding the files, the options and a way to say how far it has got, so what a run is *in* is decided in one place.
306
-
307
- ### Reading a selection
308
-
309
- src/wexample_wex_addon_process/process_type/selection.py holds no list of files: a selection is a rule, and what it takes is read from disk each time it is asked for. The rule is `PathMatcher` from `wexample-file`, a line-for-line port of the PHP one the board uses; the walking is filestate's, the matcher being handed to a `ChildrenFilterOption` as its `filter`.
310
-
311
- Going through filestate to *enumerate* and not only to *change* is deliberate: a type that reads the files and a type that rewrites them are then handed the very same tree, and there is one answer to what a selection takes rather than two.
312
-
313
- The two implementations are kept honest against each other: three hundred random pattern sets, sixty paths and fifty directories were run through both and compared, with no difference.
314
-
315
- ### Types, and the families that declare them
316
-
317
- src/wexample_wex_addon_process/process_type/abstract_process_type.py is deliberately an ordinary class — no kernel, no attrs, no state. A type contributed from a bundle should be readable by whoever wrote the bundle rather than by whoever wrote wex.
318
-
319
- A type's name carries its family: `filestate:mode`, `agent:review`. The prefix is not a branch taken at run time — src/wexample_wex_addon_process/process_type/process_type_registry.py asks each family for its names once, and what a record names is found in that map or is an error. A family declares names; it decides nothing.
320
-
321
- src/wexample_wex_addon_process/process_type/family/file_state_process_type_family.py declares none of its types by hand. It walks filestate's `DefaultOptionsProvider` and keeps the options that override `create_required_operation` — the precise line between a treatment and a piece of structure, asked of the option itself rather than listed here. `mode` is a treatment, `children` is not. Thirteen types come out of it, and an option added to filestate is a fourteenth the same day.
322
-
323
- Each of those is a src/wexample_wex_addon_process/process_type/filestate/file_state_option_process_type.py, which holds no logic either: the process's `options` become the configuration asked of every selected file, and filestate turns the difference with the disk into operations that can be described, applied and undone. `action` in the options picks the verb: `check` returns verdicts per file, `dry_run` the operations a pass would apply, `fix` applies them.
324
-
325
- src/wexample_wex_addon_process/process_type/filestate/file_state_report_process_type.py is in that family and yet emits no operation, on filestate's own rule: a scope names something observable on disk, and a report changes nothing observable. An operation with no scope would be an operation nothing could ever run.
326
-
327
- ### The two entry points
328
-
329
- src/wexample_wex_addon_process/commands/run/execute.py runs one run, here and now, with no queue in sight. src/wexample_wex_addon_process/service/process_run_service.py is the same runner behind a `wexample-queue` service: it consumes `process_run`, and rings back on `process_run_event` with the same shape of message it received — `kind`, `id`, `workdir`. The runner is rebuilt for each message so that the workdir a ring names is the one the message came with: one worker serves every app.
330
-
331
- The message carries no work at all, only a pointer. A worker starting after the message was published, or reading a run somebody has since edited, sees what is true now rather than what was true when the button was pressed.
332
-
333
- ### What a record is called
334
-
335
- src/wexample_wex_addon_process/const/process.py spells every key once. The board writes these very names into the same files, so one changed here has to be changed there — this file and `ProcessRunHydrator` on the PHP side are two halves of one contract.
336
-
337
- ## Integration in the Suite
338
-
339
- This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.
340
-
341
- ### Related Packages
342
-
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
-
345
- ## Dependencies
346
-
347
- - wexample-file: >=9.2.0
348
- - wexample-queue: >=2.0.0
349
- - wexample-wex-addon-app: >=34.1.0
350
-
351
- ## Versioning & Compatibility Policy
352
-
353
- Wexample packages follow **Semantic Versioning** (SemVer):
354
-
355
- - **MAJOR**: Breaking changes
356
- - **MINOR**: New features, backward compatible
357
- - **PATCH**: Bug fixes, backward compatible
358
-
359
- We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.
360
-
361
- ## License
362
-
363
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
364
-
365
- Free to use in both personal and commercial projects.
366
-
367
- ## About us
368
-
369
- [Wexample](https://wexample.com) stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.
370
-
371
- This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.
372
-
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.
374
-
375
- Built and maintained by [Romain Weeger](https://weeger.io).
376
-
377
- ## Known Limitations & Roadmap
378
-
379
- Current limitations and planned features are tracked in the GitHub issues.
380
-
381
- See the [project roadmap](https://github.com/wexample/python-wex-addon-process/issues) for upcoming features and improvements.
382
-
383
- ## Status & Compatibility
384
-
385
- **Maturity**: Production-ready
386
-
387
- **Python Support**: >=3.10
388
-
389
- **OS Support**: Linux, macOS, Windows
390
-
391
- **Status**: Actively maintained
392
-
393
- ## Useful Links
394
-
395
- - **Homepage**: https://github.com/wexample/python-wex-addon-process
396
- - **Issue Tracker**: https://github.com/wexample/python-wex-addon-process/issues
397
- - **Discussions**: https://github.com/wexample/python-wex-addon-process/discussions
398
- - **PyPI**: [pypi.org/project/wexample-wex-addon-process](https://pypi.org/project/wexample-wex-addon-process/)
399
-
400
- ## Migration Notes
401
-
402
- When upgrading between major versions, refer to the migration guides in the documentation.
403
-
404
- Breaking changes are clearly documented with upgrade paths and examples.