pytest-loco 1.3.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 (39) hide show
  1. pytest_loco-1.3.0/LICENSE +24 -0
  2. pytest_loco-1.3.0/PKG-INFO +329 -0
  3. pytest_loco-1.3.0/README.md +299 -0
  4. pytest_loco-1.3.0/pyproject.toml +190 -0
  5. pytest_loco-1.3.0/src/pytest_loco/__init__.py +14 -0
  6. pytest_loco-1.3.0/src/pytest_loco/__main__.py +144 -0
  7. pytest_loco-1.3.0/src/pytest_loco/builtins/__init__.py +1 -0
  8. pytest_loco-1.3.0/src/pytest_loco/builtins/actors.py +35 -0
  9. pytest_loco-1.3.0/src/pytest_loco/builtins/checkers.py +337 -0
  10. pytest_loco-1.3.0/src/pytest_loco/builtins/instructions.py +409 -0
  11. pytest_loco-1.3.0/src/pytest_loco/builtins/lookups.py +181 -0
  12. pytest_loco-1.3.0/src/pytest_loco/context.py +51 -0
  13. pytest_loco-1.3.0/src/pytest_loco/core/__init__.py +22 -0
  14. pytest_loco-1.3.0/src/pytest_loco/core/builder.py +188 -0
  15. pytest_loco-1.3.0/src/pytest_loco/core/loader.py +269 -0
  16. pytest_loco-1.3.0/src/pytest_loco/core/parser.py +278 -0
  17. pytest_loco-1.3.0/src/pytest_loco/errors.py +546 -0
  18. pytest_loco-1.3.0/src/pytest_loco/extensions/__init__.py +112 -0
  19. pytest_loco-1.3.0/src/pytest_loco/extensions/actors.py +125 -0
  20. pytest_loco-1.3.0/src/pytest_loco/extensions/checkers.py +111 -0
  21. pytest_loco-1.3.0/src/pytest_loco/extensions/contents.py +386 -0
  22. pytest_loco-1.3.0/src/pytest_loco/extensions/instructions.py +60 -0
  23. pytest_loco-1.3.0/src/pytest_loco/extensions/parameters.py +224 -0
  24. pytest_loco-1.3.0/src/pytest_loco/jsonschema.py +107 -0
  25. pytest_loco-1.3.0/src/pytest_loco/models.py +192 -0
  26. pytest_loco-1.3.0/src/pytest_loco/names.py +70 -0
  27. pytest_loco-1.3.0/src/pytest_loco/plugin/__init__.py +110 -0
  28. pytest_loco-1.3.0/src/pytest_loco/plugin/case.py +340 -0
  29. pytest_loco-1.3.0/src/pytest_loco/plugin/spec.py +107 -0
  30. pytest_loco-1.3.0/src/pytest_loco/py.typed +0 -0
  31. pytest_loco-1.3.0/src/pytest_loco/schema/__init__.py +27 -0
  32. pytest_loco-1.3.0/src/pytest_loco/schema/actions.py +166 -0
  33. pytest_loco-1.3.0/src/pytest_loco/schema/cases.py +141 -0
  34. pytest_loco-1.3.0/src/pytest_loco/schema/checks.py +76 -0
  35. pytest_loco-1.3.0/src/pytest_loco/schema/contents.py +85 -0
  36. pytest_loco-1.3.0/src/pytest_loco/schema/contexts.py +72 -0
  37. pytest_loco-1.3.0/src/pytest_loco/schema/inputs.py +329 -0
  38. pytest_loco-1.3.0/src/pytest_loco/schema/instructions.py +49 -0
  39. pytest_loco-1.3.0/src/pytest_loco/values.py +103 -0
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2025, Mikhalev Oleg and others
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytest-loco
3
+ Version: 1.3.0
4
+ Summary: Another one YAML-based DSL for testing
5
+ License-Expression: BSD-2-Clause
6
+ License-File: LICENSE
7
+ Author: Mikhalev Oleg
8
+ Author-email: mhalairt@gmail.com
9
+ Requires-Python: >3.13,<4
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Plugins
12
+ Classifier: Framework :: Pytest
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Classifier: Topic :: Utilities
21
+ Requires-Dist: click (>=8.3.1,<9.0.0)
22
+ Requires-Dist: pydantic (>=2.12.5,<3.0.0)
23
+ Requires-Dist: pydantic-settings (>=2.12.0,<3.0.0)
24
+ Requires-Dist: pytest (>=9.0.2,<10.0.0)
25
+ Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
26
+ Project-URL: Issues, https://github.com/pytest-loco/pytest-loco/issues
27
+ Project-URL: Source Code, https://github.com/pytest-loco/pytest-loco
28
+ Description-Content-Type: text/markdown
29
+
30
+ # pytest-loco
31
+
32
+ Declarative DSL for structured, extensible test scenarios in pytest.
33
+
34
+ `pytest-loco` introduces a YAML-based domain-specific language (DSL) for
35
+ describing test workflows in a declarative and composable way.
36
+ It is designed to support structured validation, data-driven execution,
37
+ and pluggable extensions such as HTTP, JSON, and custom domain logic.
38
+
39
+ ## Install
40
+
41
+ ```sh
42
+ > pip install pytest-loco
43
+ ```
44
+
45
+ Requirements:
46
+ - Python 3.13 or higher
47
+
48
+ ## Concepts
49
+
50
+ ### Definitions
51
+
52
+ A test file is a sequence of YAML documents, each document has a `spec`
53
+ that defines its role.
54
+
55
+ Document represents a `step` by default, but the first document in a file may define:
56
+
57
+ - a `case` (runnable scenario), or
58
+ - a `template` (reusable, non-runnable definition)
59
+
60
+ Subsequent documents define executable steps.
61
+
62
+ #### Case
63
+
64
+ A `case` represents a runnable test scenario.
65
+
66
+ ```yaml
67
+ spec: case
68
+ title: Short human-readable case title
69
+ description: >
70
+ Detailed human-readable description of the scenario.
71
+ May span multiple lines and is intended for documentation
72
+ and reporting purposes
73
+ metadata:
74
+ tags:
75
+ - engine
76
+ - example
77
+ vars:
78
+ baseUrl: https://httpbin.org
79
+ ```
80
+
81
+ Only documents under a `case` are executed.
82
+
83
+ #### Template
84
+
85
+ A `template` defines reusable logic that can be applied to multiple cases.
86
+
87
+ ```yaml
88
+ spec: template
89
+ title: Short human-readable template title
90
+ description: >
91
+ Detailed human-readable description of the scenario template.
92
+ May span multiple lines and is intended for documentation
93
+ and reporting purposes
94
+ params:
95
+ - name: baseUrl
96
+ type: string
97
+ default: https://httpbin.org
98
+ ```
99
+
100
+ Templates allow:
101
+
102
+ - Reuse of common workflows
103
+ - Parameterized execution
104
+ - Elimination of duplication
105
+ - Standardization of expectations
106
+
107
+ Templates are resolved at parse time and merged into the execution graph.
108
+
109
+ ```yaml
110
+ ...
111
+
112
+ ---
113
+ title: Example of invoking the template from a case
114
+ description: >
115
+ This step demonstrates how a template can be invoked from
116
+ within a case using the `include` action. The caller may pass
117
+ variables into the action context via `vars`. These variables
118
+ are treated as parameters of the invoked template. The template
119
+ execution context is isolated: only values explicitly declared
120
+ as template parameters are transferred and shared.
121
+ action: include
122
+ vars:
123
+ argument: OK
124
+ file: echo.yaml
125
+ export:
126
+ value: !var result.value
127
+ expect:
128
+ - title: Value exists in include result
129
+ value: !var value
130
+ match: OK
131
+
132
+ ...
133
+ ```
134
+
135
+ ### Steps
136
+
137
+ #### Actions
138
+
139
+ Actions are the executable units of a case, each step document
140
+ describes a single action invocation with expectations block.
141
+
142
+ The core itself does not implement domain-specific logic.
143
+ The plugin executes the action implementation. Action resolution
144
+ working with `action` field as a symbolic identifier (for example,
145
+ `http.get`).
146
+
147
+ At runtime:
148
+ - The core locates the plugin that registered this action
149
+ - The action schema is validated
150
+ - Arguments are parsed and compiled (including deferred expressions)
151
+ - The plugin executes the action implementation
152
+
153
+ The complex example with `pytest-loco-json` and `pytest-loco-http` extensions:
154
+
155
+ ```yaml
156
+ ...
157
+
158
+ ---
159
+ title: Test HTTP GET-method
160
+ description: >
161
+ Detailed human-readable description of the action.
162
+ May span multiple lines and is intended for documentation
163
+ and reporting purposes
164
+ action: http.get
165
+ url: !urljoin baseUrl /get
166
+ params: test: 'true'
167
+ headers:
168
+ accept: application/json
169
+ output: response
170
+ export:
171
+ responseJson: !load
172
+ source: !var response.text
173
+ format: json
174
+ expect:
175
+ - title: Status is 200
176
+ value: !var response.status
177
+ match: 200
178
+ - title: Response contains arguments
179
+ value: !var responseJson.args.test
180
+ match: 'true'
181
+
182
+ ...
183
+ ```
184
+
185
+ After execution, the action produces a result object.
186
+
187
+ By convention, this result is stored in the case context under
188
+ `result`. But user can redefine output variable name by `output` field.
189
+
190
+ ```yaml
191
+ ...
192
+
193
+ output: response
194
+ value: !var response.status
195
+
196
+ ...
197
+ ```
198
+
199
+ The structure of result is defined by the plugin.
200
+
201
+
202
+ Actions may define an export block:
203
+
204
+ ```yaml
205
+ ...
206
+
207
+ export:
208
+ token: !var result.token
209
+ ...
210
+
211
+ ```
212
+
213
+ Export:
214
+ - Evaluates expressions after action execution
215
+ - Stores values in case context
216
+ - Makes them available to subsequent steps
217
+
218
+ Exports are explicit data flow. Nothing is implicitly persisted
219
+ across steps except what is exported.
220
+
221
+ #### Expectations
222
+
223
+ Actions may define expectations.
224
+
225
+ ```yaml
226
+ ...
227
+
228
+ expect:
229
+ - title: Status is 200
230
+ value: !var result.status
231
+ match: 200
232
+ ...
233
+
234
+ ```
235
+
236
+ Expectations:
237
+ - Are evaluated after export
238
+ - Operate on fully resolved values
239
+ - Produce structured assertion results
240
+ - Are reported individually
241
+
242
+ Failure of an expectation fails the step and the case.
243
+
244
+ By default available:
245
+ - `match` (aliased `eq`, `equal`)
246
+ - `not_match` (aliased `notMatch`, `ne`, `notEqual`)
247
+ - `less_than` (aliased `lt`, `lessThan`)
248
+ - `greater_than` (aliased `gt`, `greaterThan`)
249
+ - `less_than_or_equal` (aliased `lte`, `lessThanOrEqual`)
250
+ - `greater_than_or_equal` (aliased `gte`, `greaterThanOrEqual`)
251
+ - `regex` (aliased `reMatch`, `regexMatch`)
252
+
253
+ Expectations can be extended by plugins.
254
+
255
+ ### Context
256
+
257
+ The context is the runtime state container for a case.
258
+
259
+ It holds:
260
+ - Case-level variables (defined by `vars` in a case)
261
+ - Case parameters (defined by `params` in a case)
262
+ - Template parameters (defined by `params` in a case)
263
+ - Environment variables (defined by `envs` in a case or a template)
264
+ - Step-level variables (defined by `vars` in a step)
265
+ - Step results (stored in `result` by default)
266
+ - Exported values
267
+ - Deferred expressions
268
+
269
+ Each case has its own isolated context.
270
+
271
+ Instructions such as:
272
+
273
+ ```yaml
274
+ ...
275
+
276
+ value: !var result.status
277
+ ...
278
+ ```
279
+
280
+ are compiled at parse time but executed at runtime.
281
+
282
+ Deferred evaluation allows:
283
+ - Accessing results of previous steps
284
+ - Chained transformations
285
+ - Late binding of values
286
+ - Context-aware resolution
287
+
288
+ Evaluation order is deterministic and follows step order.
289
+
290
+ ### Errors
291
+
292
+ Parse Errors (before execution begins):
293
+ - Invalid YAML structure
294
+ - Invalid schema
295
+ - Unknown action or expectation
296
+ - Unknown instruction
297
+ - Invalid parameter declarations
298
+
299
+ In this case, the test case does not start execution.
300
+
301
+ Runtime Errors (during execution)
302
+ - Exception raised inside an action or an expectation
303
+ - Deferred evaluation failure
304
+ - Type validation failure
305
+ - Data transformation errors
306
+
307
+ Any failures must be deterministic and predictable, so
308
+ deliberate simplifications of semantics and behavior have
309
+ been introduced into the core:
310
+ - No implicit continuation after failure
311
+ - No silent exception suppression
312
+ - No partially committed state
313
+ - No automatic retries unless implemented by a plugin
314
+
315
+ ## VSCode integration
316
+
317
+ Recommended extension:
318
+ [YAML Language Support by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml)
319
+
320
+ DSL schema is available for validation and hints with this extension.
321
+
322
+ Just run:
323
+
324
+ ```sh
325
+ > pytest-loco vscode-configure
326
+ ```
327
+
328
+ at the root of a project.
329
+
@@ -0,0 +1,299 @@
1
+ # pytest-loco
2
+
3
+ Declarative DSL for structured, extensible test scenarios in pytest.
4
+
5
+ `pytest-loco` introduces a YAML-based domain-specific language (DSL) for
6
+ describing test workflows in a declarative and composable way.
7
+ It is designed to support structured validation, data-driven execution,
8
+ and pluggable extensions such as HTTP, JSON, and custom domain logic.
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ > pip install pytest-loco
14
+ ```
15
+
16
+ Requirements:
17
+ - Python 3.13 or higher
18
+
19
+ ## Concepts
20
+
21
+ ### Definitions
22
+
23
+ A test file is a sequence of YAML documents, each document has a `spec`
24
+ that defines its role.
25
+
26
+ Document represents a `step` by default, but the first document in a file may define:
27
+
28
+ - a `case` (runnable scenario), or
29
+ - a `template` (reusable, non-runnable definition)
30
+
31
+ Subsequent documents define executable steps.
32
+
33
+ #### Case
34
+
35
+ A `case` represents a runnable test scenario.
36
+
37
+ ```yaml
38
+ spec: case
39
+ title: Short human-readable case title
40
+ description: >
41
+ Detailed human-readable description of the scenario.
42
+ May span multiple lines and is intended for documentation
43
+ and reporting purposes
44
+ metadata:
45
+ tags:
46
+ - engine
47
+ - example
48
+ vars:
49
+ baseUrl: https://httpbin.org
50
+ ```
51
+
52
+ Only documents under a `case` are executed.
53
+
54
+ #### Template
55
+
56
+ A `template` defines reusable logic that can be applied to multiple cases.
57
+
58
+ ```yaml
59
+ spec: template
60
+ title: Short human-readable template title
61
+ description: >
62
+ Detailed human-readable description of the scenario template.
63
+ May span multiple lines and is intended for documentation
64
+ and reporting purposes
65
+ params:
66
+ - name: baseUrl
67
+ type: string
68
+ default: https://httpbin.org
69
+ ```
70
+
71
+ Templates allow:
72
+
73
+ - Reuse of common workflows
74
+ - Parameterized execution
75
+ - Elimination of duplication
76
+ - Standardization of expectations
77
+
78
+ Templates are resolved at parse time and merged into the execution graph.
79
+
80
+ ```yaml
81
+ ...
82
+
83
+ ---
84
+ title: Example of invoking the template from a case
85
+ description: >
86
+ This step demonstrates how a template can be invoked from
87
+ within a case using the `include` action. The caller may pass
88
+ variables into the action context via `vars`. These variables
89
+ are treated as parameters of the invoked template. The template
90
+ execution context is isolated: only values explicitly declared
91
+ as template parameters are transferred and shared.
92
+ action: include
93
+ vars:
94
+ argument: OK
95
+ file: echo.yaml
96
+ export:
97
+ value: !var result.value
98
+ expect:
99
+ - title: Value exists in include result
100
+ value: !var value
101
+ match: OK
102
+
103
+ ...
104
+ ```
105
+
106
+ ### Steps
107
+
108
+ #### Actions
109
+
110
+ Actions are the executable units of a case, each step document
111
+ describes a single action invocation with expectations block.
112
+
113
+ The core itself does not implement domain-specific logic.
114
+ The plugin executes the action implementation. Action resolution
115
+ working with `action` field as a symbolic identifier (for example,
116
+ `http.get`).
117
+
118
+ At runtime:
119
+ - The core locates the plugin that registered this action
120
+ - The action schema is validated
121
+ - Arguments are parsed and compiled (including deferred expressions)
122
+ - The plugin executes the action implementation
123
+
124
+ The complex example with `pytest-loco-json` and `pytest-loco-http` extensions:
125
+
126
+ ```yaml
127
+ ...
128
+
129
+ ---
130
+ title: Test HTTP GET-method
131
+ description: >
132
+ Detailed human-readable description of the action.
133
+ May span multiple lines and is intended for documentation
134
+ and reporting purposes
135
+ action: http.get
136
+ url: !urljoin baseUrl /get
137
+ params: test: 'true'
138
+ headers:
139
+ accept: application/json
140
+ output: response
141
+ export:
142
+ responseJson: !load
143
+ source: !var response.text
144
+ format: json
145
+ expect:
146
+ - title: Status is 200
147
+ value: !var response.status
148
+ match: 200
149
+ - title: Response contains arguments
150
+ value: !var responseJson.args.test
151
+ match: 'true'
152
+
153
+ ...
154
+ ```
155
+
156
+ After execution, the action produces a result object.
157
+
158
+ By convention, this result is stored in the case context under
159
+ `result`. But user can redefine output variable name by `output` field.
160
+
161
+ ```yaml
162
+ ...
163
+
164
+ output: response
165
+ value: !var response.status
166
+
167
+ ...
168
+ ```
169
+
170
+ The structure of result is defined by the plugin.
171
+
172
+
173
+ Actions may define an export block:
174
+
175
+ ```yaml
176
+ ...
177
+
178
+ export:
179
+ token: !var result.token
180
+ ...
181
+
182
+ ```
183
+
184
+ Export:
185
+ - Evaluates expressions after action execution
186
+ - Stores values in case context
187
+ - Makes them available to subsequent steps
188
+
189
+ Exports are explicit data flow. Nothing is implicitly persisted
190
+ across steps except what is exported.
191
+
192
+ #### Expectations
193
+
194
+ Actions may define expectations.
195
+
196
+ ```yaml
197
+ ...
198
+
199
+ expect:
200
+ - title: Status is 200
201
+ value: !var result.status
202
+ match: 200
203
+ ...
204
+
205
+ ```
206
+
207
+ Expectations:
208
+ - Are evaluated after export
209
+ - Operate on fully resolved values
210
+ - Produce structured assertion results
211
+ - Are reported individually
212
+
213
+ Failure of an expectation fails the step and the case.
214
+
215
+ By default available:
216
+ - `match` (aliased `eq`, `equal`)
217
+ - `not_match` (aliased `notMatch`, `ne`, `notEqual`)
218
+ - `less_than` (aliased `lt`, `lessThan`)
219
+ - `greater_than` (aliased `gt`, `greaterThan`)
220
+ - `less_than_or_equal` (aliased `lte`, `lessThanOrEqual`)
221
+ - `greater_than_or_equal` (aliased `gte`, `greaterThanOrEqual`)
222
+ - `regex` (aliased `reMatch`, `regexMatch`)
223
+
224
+ Expectations can be extended by plugins.
225
+
226
+ ### Context
227
+
228
+ The context is the runtime state container for a case.
229
+
230
+ It holds:
231
+ - Case-level variables (defined by `vars` in a case)
232
+ - Case parameters (defined by `params` in a case)
233
+ - Template parameters (defined by `params` in a case)
234
+ - Environment variables (defined by `envs` in a case or a template)
235
+ - Step-level variables (defined by `vars` in a step)
236
+ - Step results (stored in `result` by default)
237
+ - Exported values
238
+ - Deferred expressions
239
+
240
+ Each case has its own isolated context.
241
+
242
+ Instructions such as:
243
+
244
+ ```yaml
245
+ ...
246
+
247
+ value: !var result.status
248
+ ...
249
+ ```
250
+
251
+ are compiled at parse time but executed at runtime.
252
+
253
+ Deferred evaluation allows:
254
+ - Accessing results of previous steps
255
+ - Chained transformations
256
+ - Late binding of values
257
+ - Context-aware resolution
258
+
259
+ Evaluation order is deterministic and follows step order.
260
+
261
+ ### Errors
262
+
263
+ Parse Errors (before execution begins):
264
+ - Invalid YAML structure
265
+ - Invalid schema
266
+ - Unknown action or expectation
267
+ - Unknown instruction
268
+ - Invalid parameter declarations
269
+
270
+ In this case, the test case does not start execution.
271
+
272
+ Runtime Errors (during execution)
273
+ - Exception raised inside an action or an expectation
274
+ - Deferred evaluation failure
275
+ - Type validation failure
276
+ - Data transformation errors
277
+
278
+ Any failures must be deterministic and predictable, so
279
+ deliberate simplifications of semantics and behavior have
280
+ been introduced into the core:
281
+ - No implicit continuation after failure
282
+ - No silent exception suppression
283
+ - No partially committed state
284
+ - No automatic retries unless implemented by a plugin
285
+
286
+ ## VSCode integration
287
+
288
+ Recommended extension:
289
+ [YAML Language Support by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml)
290
+
291
+ DSL schema is available for validation and hints with this extension.
292
+
293
+ Just run:
294
+
295
+ ```sh
296
+ > pytest-loco vscode-configure
297
+ ```
298
+
299
+ at the root of a project.