robotframework-parallelrunner 0.2.1__tar.gz → 0.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.
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/PKG-INFO +6 -2
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/README.md +5 -1
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/pyproject.toml +1 -1
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/src/ParallelRunner/parallel_library.py +42 -12
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/LICENSE +0 -0
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/src/ParallelRunner/__init__.py +0 -0
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/src/ParallelRunner/py.typed +0 -0
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/src/ParallelRunner/version.py +0 -0
- {robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/src/parallelrunner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotframework-parallelrunner
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Run a keyword in parallel inside a single Robot Framework test case, with one clean, ordered log.html.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -118,6 +118,10 @@ threading, buffering, and replay logic stay out of your `.robot` files.
|
|
|
118
118
|
8 workers (see [Using It Together With pabot](#using-it-together-with-pabot)).
|
|
119
119
|
- **Integrity.** Your `log.html` remains the single source of truth — no
|
|
120
120
|
broken XML tags from concurrent writes.
|
|
121
|
+
- **No hidden failures.** If any call fails, the keyword fails too (after all
|
|
122
|
+
calls finish and their logs are replayed), with a message listing every
|
|
123
|
+
failed item. Pass `fail_on_error=False` to get a `PASS`/`FAIL` status per
|
|
124
|
+
item and check them yourself.
|
|
121
125
|
- **Flexibility.** Pass `repeat=10` to stress-test a single endpoint, or
|
|
122
126
|
`for_loop_iterable=${items}` for batch validation of a whole list.
|
|
123
127
|
- **Ergonomics.** Results come back in call order (not completion order), so
|
|
@@ -179,7 +183,7 @@ pabot --processes 4 --pythonpath . atest/
|
|
|
179
183
|
pabot --processes 4 --testlevelsplit --pythonpath . atest/
|
|
180
184
|
```
|
|
181
185
|
|
|
182
|
-
Both commands pass the full acceptance suite of this repository (
|
|
186
|
+
Both commands pass the full acceptance suite of this repository (52 tests;
|
|
183
187
|
checked with pabot 5.2.2).
|
|
184
188
|
|
|
185
189
|
How much time each one saves, from the offline
|
|
@@ -84,6 +84,10 @@ threading, buffering, and replay logic stay out of your `.robot` files.
|
|
|
84
84
|
8 workers (see [Using It Together With pabot](#using-it-together-with-pabot)).
|
|
85
85
|
- **Integrity.** Your `log.html` remains the single source of truth — no
|
|
86
86
|
broken XML tags from concurrent writes.
|
|
87
|
+
- **No hidden failures.** If any call fails, the keyword fails too (after all
|
|
88
|
+
calls finish and their logs are replayed), with a message listing every
|
|
89
|
+
failed item. Pass `fail_on_error=False` to get a `PASS`/`FAIL` status per
|
|
90
|
+
item and check them yourself.
|
|
87
91
|
- **Flexibility.** Pass `repeat=10` to stress-test a single endpoint, or
|
|
88
92
|
`for_loop_iterable=${items}` for batch validation of a whole list.
|
|
89
93
|
- **Ergonomics.** Results come back in call order (not completion order), so
|
|
@@ -145,7 +149,7 @@ pabot --processes 4 --pythonpath . atest/
|
|
|
145
149
|
pabot --processes 4 --testlevelsplit --pythonpath . atest/
|
|
146
150
|
```
|
|
147
151
|
|
|
148
|
-
Both commands pass the full acceptance suite of this repository (
|
|
152
|
+
Both commands pass the full acceptance suite of this repository (52 tests;
|
|
149
153
|
checked with pabot 5.2.2).
|
|
150
154
|
|
|
151
155
|
How much time each one saves, from the offline
|
|
@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
6
6
|
name = "robotframework-parallelrunner"
|
|
7
7
|
# Single source of truth for the version: bump with `poetry version patch|minor|major`.
|
|
8
8
|
# ParallelRunner.__version__ / ROBOT_LIBRARY_VERSION read it back from the installed metadata.
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.3.0"
|
|
10
10
|
description = "Run a keyword in parallel inside a single Robot Framework test case, with one clean, ordered log.html."
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
license = "MIT"
|
|
@@ -10,13 +10,14 @@ from .version import __version__
|
|
|
10
10
|
|
|
11
11
|
class ParallelTaskError(RuntimeError):
|
|
12
12
|
"""
|
|
13
|
-
Raised when at least one parallel task failed
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
Raised when at least one parallel task failed: by ``run_parallel_scenarios``
|
|
14
|
+
(unless called with ``fail_on_error=False``), and by ``get_result_values``,
|
|
15
|
+
which has no meaningful return value to hand back for a failed task.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this is raised instead
|
|
17
|
+
Returning normally after a failed call would let a test keep going (and
|
|
18
|
+
pass) with bad or missing data instead of failing for the right reason,
|
|
19
|
+
so this is raised instead - after every task has finished and all logs
|
|
20
|
+
have been replayed.
|
|
20
21
|
|
|
21
22
|
Attributes:
|
|
22
23
|
failures: the subset of result dicts with ``status == "FAIL"``,
|
|
@@ -91,9 +92,24 @@ class ParallelRunner:
|
|
|
91
92
|
| result | The return value of the call (only when ``status`` is ``PASS``). |
|
|
92
93
|
| error | The error message (only when ``status`` is ``FAIL``). |
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
= Failures =
|
|
96
|
+
|
|
97
|
+
If any call raises, `Run Parallel Scenarios` still waits for every other
|
|
98
|
+
call, replays all logs, and then *fails* with a ``ParallelTaskError``
|
|
99
|
+
that lists each failed item and its error, e.g.
|
|
100
|
+
``2 of 5 parallel task(s) failed: item='4': ...; item='5': ...``.
|
|
101
|
+
One failed call is never hidden behind a passing keyword.
|
|
102
|
+
|
|
103
|
+
To inspect failures yourself instead, pass ``fail_on_error=False``: the
|
|
104
|
+
keyword then always returns the result list, failed calls get
|
|
105
|
+
``status`` ``FAIL`` (their error is also logged at ``ERROR`` level), and
|
|
106
|
+
it is up to you to assert on them:
|
|
107
|
+
|
|
108
|
+
| ${results}= | Run Parallel Scenarios | keyword=Verify Agent Data | library=MyLibrary | for_loop_iterable=${agents} | fail_on_error=False |
|
|
109
|
+
| Should Be Equal | ${results}[1][status] | PASS |
|
|
110
|
+
|
|
111
|
+
``return_values_only=True`` and `Get Result Values` always fail if any
|
|
112
|
+
call failed, because a failed call has no return value.
|
|
97
113
|
|
|
98
114
|
= Custom logger mappers =
|
|
99
115
|
|
|
@@ -146,6 +162,7 @@ class ParallelRunner:
|
|
|
146
162
|
thread_log_level: str = "INFO",
|
|
147
163
|
logger_mapper: Optional[Any] = None,
|
|
148
164
|
return_values_only: bool = False,
|
|
165
|
+
fail_on_error: bool = True,
|
|
149
166
|
**kwargs
|
|
150
167
|
) -> Union[List[Dict[str, Any]], Tuple[Any, ...]]:
|
|
151
168
|
"""Runs ``keyword`` from ``library`` concurrently, once per item or N times.
|
|
@@ -178,17 +195,23 @@ class ParallelRunner:
|
|
|
178
195
|
return value (in call order) instead of the result dictionaries.
|
|
179
196
|
Fails with ``ParallelTaskError`` if any call failed. Same as calling
|
|
180
197
|
`Get Result Values` on the default return value.
|
|
198
|
+
- ``fail_on_error``: If true (default), fail with ``ParallelTaskError``
|
|
199
|
+
when any call failed, after all calls have finished and their logs
|
|
200
|
+
have been replayed. If false, return the results anyway and leave
|
|
201
|
+
checking ``status`` to the caller. See `Failures`.
|
|
181
202
|
- ``**kwargs``: Any other named argument is passed to every call.
|
|
182
203
|
|
|
183
204
|
Returns a list of result dictionaries (see `Result format`), in call
|
|
184
205
|
order: entry ``i`` belongs to ``for_loop_iterable[i]`` / repeat
|
|
185
|
-
index ``i``, regardless of which thread finished first.
|
|
206
|
+
index ``i``, regardless of which thread finished first. With the
|
|
207
|
+
default ``fail_on_error=True`` it only returns when every call passed.
|
|
186
208
|
|
|
187
209
|
Examples:
|
|
188
210
|
| ${agents}= | Create List | 1 | 2 | 3 |
|
|
189
211
|
| ${results}= | Run Parallel Scenarios | keyword=Verify Agent Data | library=MyLibrary | for_loop_iterable=${agents} |
|
|
190
212
|
| ${results}= | Run Parallel Scenarios | keyword=Check Health | library=MyLibrary | repeat=8 | agent_id=1 |
|
|
191
213
|
| ${id1} ${id2} ${id3}= | Run Parallel Scenarios | keyword=Seed Record | library=MyLibrary | repeat=3 | return_values_only=True |
|
|
214
|
+
| ${results}= | Run Parallel Scenarios | keyword=Verify Agent Data | library=MyLibrary | for_loop_iterable=${agents} | fail_on_error=False |
|
|
192
215
|
"""
|
|
193
216
|
# Determine items to process. NOTE: `repeat or 1` would be wrong here -
|
|
194
217
|
# 0 is falsy in Python, so an explicit repeat=0 would silently fall
|
|
@@ -228,6 +251,8 @@ class ParallelRunner:
|
|
|
228
251
|
|
|
229
252
|
if return_values_only:
|
|
230
253
|
return self.get_result_values(results)
|
|
254
|
+
if fail_on_error:
|
|
255
|
+
self._raise_if_any_failed(results)
|
|
231
256
|
return results
|
|
232
257
|
|
|
233
258
|
def get_result_values(self, results: List[Dict[str, Any]]) -> Tuple[Any, ...]:
|
|
@@ -238,7 +263,9 @@ class ParallelRunner:
|
|
|
238
263
|
|
|
239
264
|
Passing ``return_values_only=True`` to `Run Parallel Scenarios` does
|
|
240
265
|
the same in one step; use this keyword when you want to inspect the
|
|
241
|
-
full results first (e.g. assert on ``status`` or ``logs``).
|
|
266
|
+
full results first (e.g. assert on ``status`` or ``logs``). Such a
|
|
267
|
+
result list may only contain failed calls if it came from a run with
|
|
268
|
+
``fail_on_error=False``.
|
|
242
269
|
|
|
243
270
|
Fails with ``ParallelTaskError`` if any entry has status ``FAIL`` -
|
|
244
271
|
a failed call has no return value to put in its slot.
|
|
@@ -247,10 +274,13 @@ class ParallelRunner:
|
|
|
247
274
|
| ${results}= | Run Parallel Scenarios | keyword=Seed Record | library=MyLibrary | repeat=3 |
|
|
248
275
|
| ${id1} ${id2} ${id3}= | Get Result Values | ${results} |
|
|
249
276
|
"""
|
|
277
|
+
self._raise_if_any_failed(results)
|
|
278
|
+
return tuple(entry["result"] for entry in results)
|
|
279
|
+
|
|
280
|
+
def _raise_if_any_failed(self, results: List[Dict[str, Any]]) -> None:
|
|
250
281
|
failures = [entry for entry in results if entry.get("status") == "FAIL"]
|
|
251
282
|
if failures:
|
|
252
283
|
raise ParallelTaskError(failures, len(results))
|
|
253
|
-
return tuple(entry["result"] for entry in results)
|
|
254
284
|
|
|
255
285
|
def _resolve_mapper(self, mapper: Optional[Any], source: str = "logger_mapper") -> Optional[Any]:
|
|
256
286
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{robotframework_parallelrunner-0.2.1 → robotframework_parallelrunner-0.3.0}/src/parallelrunner.py
RENAMED
|
File without changes
|