robotframework-pabot 5.1.0__py3-none-any.whl → 5.2.0rc1__py3-none-any.whl

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.
pabot/result_merger.py CHANGED
@@ -32,6 +32,7 @@ except ImportError:
32
32
  from robot.result.testsuite import TestSuite
33
33
 
34
34
  from robot.model import SuiteVisitor
35
+ from .writer import get_writer
35
36
 
36
37
 
37
38
  class ResultMerger(SuiteVisitor):
@@ -45,6 +46,7 @@ class ResultMerger(SuiteVisitor):
45
46
  self._out_dir = out_dir
46
47
  self.legacy_output = legacy_output
47
48
  self.timestamp_id = timestamp_id
49
+ self.writer = get_writer()
48
50
 
49
51
  self._patterns = []
50
52
  regexp_template = (
@@ -62,7 +64,10 @@ class ResultMerger(SuiteVisitor):
62
64
  if self.errors != merged.errors:
63
65
  self.errors.add(merged.errors)
64
66
  except:
65
- print("Error while merging result %s" % merged.source)
67
+ if self.writer:
68
+ self.writer.write("Error while merging result %s" % merged.source, level="error")
69
+ else:
70
+ print("Error while merging result %s" % merged.source)
66
71
  raise
67
72
 
68
73
  def _set_prefix(self, source):
@@ -214,12 +219,17 @@ def prefix(source, timestamp_id):
214
219
 
215
220
  def group_by_root(results, critical_tags, non_critical_tags, invalid_xml_callback):
216
221
  groups = {}
222
+ writer = get_writer()
217
223
  for src in results:
218
224
  try:
219
225
  res = ExecutionResult(src)
220
226
  except DataError as err:
221
- print(err.message)
222
- print("Skipping '%s' from final result" % src)
227
+ if writer:
228
+ writer.write(err.message, level="error")
229
+ writer.write("Skipping '%s' from final result" % src, level="warning")
230
+ else:
231
+ print(err.message)
232
+ print("Skipping '%s' from final result" % src)
223
233
  invalid_xml_callback()
224
234
  continue
225
235
  if ROBOT_VERSION < "4.0":
@@ -452,20 +452,28 @@ class StandardStreamInterceptor(object):
452
452
  self.output = ""
453
453
  self.origout = sys.stdout
454
454
  self.origerr = sys.stderr
455
- sys.stdout = StringIO()
456
- sys.stderr = StringIO()
455
+ self.stdout_capture = StringIO()
456
+ self.stderr_capture = StringIO()
457
+ sys.stdout = self.stdout_capture
458
+ sys.stderr = self.stderr_capture
457
459
 
458
460
  def __enter__(self):
459
461
  return self
460
462
 
461
463
  def __exit__(self, *exc_info):
462
- stdout = sys.stdout.getvalue()
463
- stderr = sys.stderr.getvalue()
464
- close = [sys.stdout, sys.stderr]
464
+ # Safely get output from captured streams, handling case where streams were restored
465
+ stdout = self._safe_getvalue(sys.stdout, self.stdout_capture)
466
+ stderr = self._safe_getvalue(sys.stderr, self.stderr_capture)
467
+
468
+ # Close only our StringIO objects
469
+ for stream in [self.stdout_capture, self.stderr_capture]:
470
+ if hasattr(stream, 'close'):
471
+ stream.close()
472
+
473
+ # Restore original streams
465
474
  sys.stdout = self.origout
466
475
  sys.stderr = self.origerr
467
- for stream in close:
468
- stream.close()
476
+
469
477
  if stdout and stderr:
470
478
  if not stderr.startswith(
471
479
  ("*TRACE*", "*DEBUG*", "*INFO*", "*HTML*", "*WARN*", "*ERROR*")
@@ -474,6 +482,18 @@ class StandardStreamInterceptor(object):
474
482
  if not stdout.endswith("\n"):
475
483
  stdout += "\n"
476
484
  self.output = stdout + stderr
485
+
486
+ def _safe_getvalue(self, current_stream, original_capture):
487
+ """Safely get value from stream, handling case where stream was restored."""
488
+ # If current stream is still our StringIO, get value from it
489
+ if hasattr(current_stream, 'getvalue') and current_stream is original_capture:
490
+ return current_stream.getvalue()
491
+ # If current stream was restored but our capture still has data
492
+ print("*WARN* Stream capture was interrupted", file=sys.stderr)
493
+ if hasattr(original_capture, 'getvalue'):
494
+ return original_capture.getvalue()
495
+ else:
496
+ return ""
477
497
 
478
498
 
479
499
  class KeywordResult(object):
pabot/writer.py ADDED
@@ -0,0 +1,235 @@
1
+ import threading
2
+ import queue
3
+ import sys
4
+ import os
5
+ import time
6
+
7
+ class Color:
8
+ RED = "\033[91m"
9
+ GREEN = "\033[92m"
10
+ YELLOW = "\033[93m"
11
+ ENDC = "\033[0m"
12
+ SUPPORTED_OSES = {"posix"} # Only Unix terminals support ANSI colors
13
+
14
+
15
+ class DottedConsole:
16
+ def __init__(self):
17
+ self._on_line = False
18
+
19
+ def dot(self, char):
20
+ print(char, end="", flush=True)
21
+ self._on_line = True
22
+
23
+ def newline(self):
24
+ if self._on_line:
25
+ print()
26
+ self._on_line = False
27
+
28
+
29
+ class BufferingWriter:
30
+ """
31
+ Buffers partial writes until a newline is encountered.
32
+ Useful for handling output that comes in fragments (e.g., from stderr).
33
+ """
34
+ def __init__(self, writer, level="info", original_stderr_name=None):
35
+ self._writer = writer
36
+ self._level = level
37
+ self.original_stderr_name = original_stderr_name
38
+ self._buffer = ""
39
+ self._lock = threading.Lock()
40
+
41
+ def write(self, msg):
42
+ with self._lock:
43
+ if not msg:
44
+ return
45
+
46
+ self._buffer += msg
47
+
48
+ # Check if buffer contains newline(s)
49
+ while "\n" in self._buffer:
50
+ line, self._buffer = self._buffer.split("\n", 1)
51
+ if line: # Only write non-empty lines
52
+ if self.original_stderr_name:
53
+ line = f"From {self.original_stderr_name}: {line}"
54
+ self._writer.write(line, level=self._level)
55
+
56
+ # If buffer ends with partial content (no newline), keep it buffered
57
+
58
+ def flush(self):
59
+ with self._lock:
60
+ if self._buffer:
61
+ self._writer.write(self._buffer, level=self._level)
62
+ self._buffer = ""
63
+
64
+
65
+ class ThreadSafeWriter:
66
+ def __init__(self, writer, level="info"):
67
+ self._writer = writer
68
+ self._lock = threading.Lock()
69
+ self._level = level # Default level for this writer instance
70
+
71
+ def write(self, msg, level=None):
72
+ # Use provided level or fall back to instance default
73
+ msg_level = level if level is not None else self._level
74
+ with self._lock:
75
+ self._writer.write(msg, level=msg_level)
76
+
77
+ def flush(self):
78
+ with self._lock:
79
+ self._writer.flush()
80
+
81
+
82
+ class MessageWriter:
83
+ def __init__(self, log_file=None, console_type="verbose"):
84
+ self.queue = queue.Queue()
85
+ self.log_file = log_file
86
+ self.console_type = console_type
87
+ self.console = DottedConsole() if console_type == "dotted" else None
88
+ if log_file:
89
+ os.makedirs(os.path.dirname(log_file), exist_ok=True)
90
+ self._stop_event = threading.Event()
91
+ self.thread = threading.Thread(target=self._writer)
92
+ self.thread.daemon = True
93
+ self.thread.start()
94
+
95
+ def _is_output_coloring_supported(self):
96
+ return sys.stdout.isatty() and os.name in Color.SUPPORTED_OSES
97
+
98
+ def _wrap_with(self, color, message):
99
+ if self._is_output_coloring_supported() and color:
100
+ return f"{color}{message}{Color.ENDC}"
101
+ return message
102
+
103
+
104
+ def _should_print_to_console(self, console_type=None, level="debug"):
105
+ """
106
+ Determine if message should be printed to console based on console_type and level.
107
+ Always write to log file.
108
+
109
+ Args:
110
+ console_type: The console type mode. If None, uses instance default.
111
+ level: Message level (debug, info, warning, error, and spesial results infos: info_passed, info_failed, info_skipped, info_ignored). Defaults to debug.
112
+ """
113
+ ct = console_type if console_type is not None else self.console_type
114
+
115
+ # Map levels to importance: debug < info_passed/info_ignored/info_skipped < info_failed < info < warning < error
116
+ level_map = {"debug": 0, "info_passed": 1, "info_ignored": 1, "info_skipped": 1, "info_failed": 2, "info": 3, "warning": 4, "error": 5}
117
+ message_level = level_map.get(level, 0) # default to debug
118
+
119
+ if ct == "none":
120
+ return False
121
+ elif ct == "quiet":
122
+ # In quiet mode, show only warning and error level messages
123
+ return message_level >= 3
124
+ elif ct == "dotted":
125
+ # In dotted mode, show test result indicators (info_passed/failed/skipped/ignored) and warnings/errors
126
+ return message_level >= 1
127
+ # verbose mode - print everything
128
+ return True
129
+
130
+ def _writer(self):
131
+ while not self._stop_event.is_set():
132
+ try:
133
+ message, color, level = self.queue.get(timeout=0.1)
134
+ except queue.Empty:
135
+ continue
136
+ if message is None:
137
+ self.queue.task_done()
138
+ break
139
+
140
+ message = message.rstrip("\n")
141
+ # Always write to log file
142
+ if self.log_file:
143
+ with open(self.log_file, "a", encoding="utf-8") as f:
144
+ lvl_msg = f"[{level.split('_')[0].upper()}]".ljust(9)
145
+ f.write(f"{lvl_msg} {message}\n")
146
+
147
+ # Print to console based on level
148
+ if self._should_print_to_console(level=level):
149
+ if self.console is not None:
150
+ # In dotted mode, only print single character messages directly
151
+ if level == "info_passed":
152
+ self.console.dot(self._wrap_with(color, "."))
153
+ elif level == "info_failed":
154
+ self.console.dot(self._wrap_with(color, "F"))
155
+ elif level in ("info_ignored", "info_skipped"):
156
+ self.console.dot(self._wrap_with(color, "s"))
157
+ else:
158
+ self.console.newline()
159
+ print(self._wrap_with(color, message), flush=True)
160
+ else:
161
+ print(self._wrap_with(color, message), flush=True)
162
+
163
+ self.queue.task_done()
164
+
165
+ def write(self, message, color=None, level="info"):
166
+ self.queue.put((f"{message}", color, level))
167
+
168
+ def flush(self, timeout=5):
169
+ """
170
+ Wait until all queued messages have been written.
171
+
172
+ :param timeout: Optional timeout in seconds. If None, wait indefinitely.
173
+ :return: True if queue drained before timeout (or no timeout), False if timed out.
174
+ """
175
+ start = time.time()
176
+ try:
177
+ # Loop until Queue reports no unfinished tasks
178
+ while True:
179
+ # If writer thread died, break to avoid infinite loop
180
+ if not self.thread.is_alive():
181
+ # Give one last moment for potential in-flight task_done()
182
+ time.sleep(0.01)
183
+ # If still unfinished, we can't do more
184
+ return getattr(self.queue, "unfinished_tasks", 0) == 0
185
+
186
+ unfinished = getattr(self.queue, "unfinished_tasks", None)
187
+ if unfinished is None:
188
+ # Fallback: call join once and return
189
+ try:
190
+ self.queue.join()
191
+ return True
192
+ except Exception:
193
+ return False
194
+
195
+ if unfinished == 0:
196
+ return True
197
+
198
+ if timeout is not None and (time.time() - start) > timeout:
199
+ return False
200
+
201
+ time.sleep(0.05)
202
+ except KeyboardInterrupt:
203
+ # Allow tests/cli to interrupt flushing
204
+ return False
205
+
206
+ def stop(self):
207
+ """
208
+ Gracefully stop the writer thread and flush remaining messages.
209
+ """
210
+ self.flush()
211
+ self._stop_event.set()
212
+ self.queue.put((None, None, None)) # sentinel to break thread loop
213
+ self.thread.join(timeout=1.0)
214
+
215
+
216
+ _writer_instance = None
217
+
218
+ def get_writer(log_dir=None, console_type="verbose"):
219
+ global _writer_instance
220
+ if _writer_instance is None:
221
+ if log_dir:
222
+ os.makedirs(log_dir, exist_ok=True)
223
+ log_file = os.path.join(log_dir or ".", "pabot_manager.log")
224
+ _writer_instance = MessageWriter(log_file=log_file, console_type=console_type)
225
+ return _writer_instance
226
+
227
+ def get_stdout_writer(log_dir=None, console_type="verbose"):
228
+ """Get a writer configured for stdout with 'info' level"""
229
+ return ThreadSafeWriter(get_writer(log_dir, console_type), level="info")
230
+
231
+ def get_stderr_writer(log_dir=None, console_type="verbose", original_stderr_name: str = None):
232
+ """Get a writer configured for stderr with 'error' level, buffered to handle partial writes"""
233
+ # Use BufferingWriter to combine fragments that come without newlines
234
+ buffering_writer = BufferingWriter(get_writer(log_dir, console_type), level="error", original_stderr_name=original_stderr_name)
235
+ return buffering_writer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robotframework-pabot
3
- Version: 5.1.0
3
+ Version: 5.2.0rc1
4
4
  Summary: Parallel test runner for Robot Framework
5
5
  Home-page: https://pabot.org
6
6
  Download-URL: https://pypi.python.org/pypi/robotframework-pabot
@@ -12,17 +12,14 @@ Classifier: Intended Audience :: Developers
12
12
  Classifier: Natural Language :: English
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Topic :: Software Development :: Testing
15
- Classifier: License :: OSI Approved :: Apache Software License
16
15
  Classifier: Development Status :: 5 - Production/Stable
17
16
  Classifier: Framework :: Robot Framework
18
17
  Requires-Python: >=3.6
19
18
  Description-Content-Type: text/markdown
20
- License-File: LICENSE.txt
21
19
  Requires-Dist: robotframework>=3.2
22
20
  Requires-Dist: robotframework-stacktrace>=0.4.1
23
21
  Requires-Dist: natsort>=8.2.0
24
22
  Dynamic: download-url
25
- Dynamic: license-file
26
23
 
27
24
  # Pabot
28
25
 
@@ -47,7 +44,7 @@ A parallel executor for [Robot Framework](http://www.robotframework.org) tests.
47
44
  - [Contributing](#contributing-to-the-project)
48
45
  - [Command-line options](#command-line-options)
49
46
  - [PabotLib](#pabotlib)
50
- - [Controlling execution order](#controlling-execution-order-and-level-of-parallelism)
47
+ - [Controlling execution order, mode and level of parallelism](#controlling-execution-order-mode-and-level-of-parallelism)
51
48
  - [Programmatic use](#programmatic-use)
52
49
  - [Global variables](#global-variables)
53
50
  - [Output Files Generated by Pabot](#output-files-generated-by-pabot)
@@ -98,6 +95,12 @@ There are several ways you can help in improving this tool:
98
95
  - Report an issue or an improvement idea to the [issue tracker](https://github.com/mkorpela/pabot/issues)
99
96
  - Contribute by programming and making a pull request (easiest way is to work on an issue from the issue tracker)
100
97
 
98
+ Before contributing, please read our detailed contributing guidelines:
99
+
100
+ - [Contributing Guide](CONTRIBUTING.md)
101
+ - [Code of Conduct](CODE_OF_CONDUCT.md)
102
+ - [Security Policy](SECURITY.md)
103
+
101
104
  ## Command-line options
102
105
  <!-- NOTE:
103
106
  The sections inside these docstring markers are also used in Pabot's --help output.
@@ -114,17 +117,19 @@ pabot [--verbose|--testlevelsplit|--command .. --end-command|
114
117
  --processtimeout num|
115
118
  --shard i/n|
116
119
  --artifacts extensions|--artifactsinsubfolders|
117
- --resourcefile file|--argumentfile[num] file|--suitesfrom file|--ordering file|
120
+ --resourcefile file|--argumentfile[num] file|--suitesfrom file
121
+ --ordering file [static|dynamic] [skip|run_all]|
118
122
  --chunk|
119
123
  --pabotprerunmodifier modifier|
120
124
  --no-rebot|
125
+ --pabotconsole [verbose|dotted|quiet|none]|
121
126
  --help|--version]
122
127
  [robot options] [path ...]
123
128
  ```
124
129
 
125
130
  PabotLib remote server is started by default to enable locking and resource distribution between parallel test executions.
126
131
 
127
- Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#all-command-line-options) and also following pabot options:
132
+ Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#command-line-options) and also following pabot options:
128
133
 
129
134
  **--verbose**
130
135
  More output from the parallel execution.
@@ -202,8 +207,10 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
202
207
  Optionally read suites from output.xml file. Failed suites will run first and longer running ones will be executed
203
208
  before shorter ones.
204
209
 
205
- **--ordering [FILE PATH]**
206
- Optionally give execution order from a file.
210
+ **--ordering [FILEPATH] [MODE] [FAILURE POLICY]**
211
+ Optionally give execution order from a file. See README.md section: [Controlling execution order, mode and level of parallelism](#controlling-execution-order-mode-and-level-of-parallelism)
212
+ - MODE (optional): [ static (default) | dynamic ]
213
+ - FAILURE POLICY (optional, only in dynamic mode): [ skip | run_all (default) ]
207
214
 
208
215
  **--chunk**
209
216
  Optionally chunk tests to PROCESSES number of robot runs. This can save time because all the suites will share the same
@@ -220,6 +227,28 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
220
227
  for scenarios where Rebot should be run later due to large log files, ensuring better memory and resource availability.
221
228
  Subprocess results are stored in the pabot_results folder.
222
229
 
230
+ **--pabotconsole [MODE]**
231
+ The --pabotconsole option controls how much output is printed to the console.
232
+ Note that all Pabot’s own messages are always logged to pabot_manager.log, regardless of the selected console mode.
233
+
234
+ The available options are:
235
+ - verbose (default):
236
+ Prints all messages to the console, corresponding closely to what is written to the log file.
237
+ - dotted:
238
+ Prints important messages, warnings, and errors to the console, along with execution progress using the following notation:
239
+
240
+ - PASS = .
241
+ - FAIL = F
242
+ - SKIP = s
243
+
244
+ Note that each Robot Framework process is represented by a single character.
245
+ Depending on the execution parameters, individual tests may not have their own status character;
246
+ instead, the status may represent an entire suite or a group of tests.
247
+ - quiet:
248
+ Similar to dotted, but suppresses execution progress output.
249
+ - none:
250
+ Produces no console output at all.
251
+
223
252
  **--help**
224
253
  Print usage instructions.
225
254
 
@@ -245,6 +274,9 @@ These can be helpful when you must ensure that only one of the processes uses so
245
274
 
246
275
  PabotLib Docs are located at https://pabot.org/PabotLib.html.
247
276
 
277
+ Note that PabotLib uses the XML-RPC protocol, which does not support all possible object types.
278
+ These limitations are described in the Robot Framework documentation in chapter [Supported argument and return value types](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#supported-argument-and-return-value-types).
279
+
248
280
  ### PabotLib example:
249
281
 
250
282
  test.robot
@@ -290,18 +322,28 @@ pabot call using resources from valueset.dat
290
322
 
291
323
  pabot --pabotlib --resourcefile valueset.dat test.robot
292
324
 
293
- ### Controlling execution order and level of parallelism
325
+ ### Controlling execution order, mode, and level of parallelism
294
326
 
295
327
  .pabotsuitenames file contains the list of suites that will be executed.
296
- File is created during pabot execution if not already there.
297
- The file is a cache that pabot uses when re-executing same tests to speed up processing.
298
- This file can be partially manually edited but easier option is to use ```--ordering FILENAME```.
299
- First 4 rows contain information that should not be edited - pabot will edit these when something changes.
300
- After this come the suite names.
328
+ This file is created during pabot execution if it does not already exist. It acts as a cache to speed up processing when re-executing the same tests.
329
+ The file can be manually edited partially, but a simpler and more controlled approach is to use:
330
+
331
+ ```bash
332
+ --ordering <FILENAME> [static|dynamic] [skip|run_all]
333
+ ```
334
+
335
+ - **FILENAME** – path to the ordering file.
336
+ - **mode** – optional execution mode, either `static` (default) or `dynamic`.
337
+ - `static` executes suites in predefined stages.
338
+ - `dynamic` executes tests as soon as all their dependencies are satisfied, allowing more optimal parallel execution.
339
+ - **failure_policy** – determines behavior when dependencies fail. Used only in dynamic mode. Optional:
340
+ - `skip` – dependent tests are skipped if a dependency fails.
341
+ - `run_all` – all tests run regardless of failures (default).
301
342
 
302
- With ```--ordering FILENAME``` you can have a list that controls order also. The syntax is same as .pabotsuitenames file syntax but does not contain 4 hash rows that are present in .pabotsuitenames.
343
+ The ordering file syntax is similar to `.pabotsuitenames` but does not include the first 4 hash rows used by pabot. The ordering file defines the **execution order and dependencies** of suites and tests.
344
+ The actual selection of what to run must still be done using options like `--test`, `--suite`, `--include`, or `--exclude`.
303
345
 
304
- Note: The `--ordering` file is intended only for defining the execution order of suites and tests. The actual selection of what to run must still be done using options like `--test`, `--suite`, `--include`, or `--exclude`.
346
+ #### Controlling execution order
305
347
 
306
348
  There different possibilities to influence the execution:
307
349
 
@@ -313,7 +355,7 @@ There different possibilities to influence the execution:
313
355
  --suite Top Suite
314
356
  ```
315
357
 
316
- * If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-the-name) you can use either the new or old full test path. For example:
358
+ * If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-suite-name) you can use either the new or old full test path. For example:
317
359
 
318
360
  ```
319
361
  --test New Suite Name.Sub Suite.Test 1
@@ -323,8 +365,14 @@ OR
323
365
 
324
366
  * You can add a line with text `#WAIT` to force executor to wait until all previous suites have been executed.
325
367
  * You can group suites and tests together to same executor process by adding line `{` before the group and `}` after. Note that `#WAIT` cannot be used inside a group.
326
- * You can introduce dependencies using the word `#DEPENDS` after a test declaration. This keyword can be used several times if it is necessary to refer to several different tests. The ordering algorithm is designed to preserve the exact user-defined order as closely as possible. However, if a test's execution dependencies are not yet satisfied, the test is postponed and moved to the earliest possible stage where all its dependencies are fulfilled. Please take care that in case of circular dependencies an exception will be thrown. Note that each `#WAIT` splits suites into separate execution blocks, and it's not possible to define dependencies for suites or tests that are inside another `#WAIT` block or inside another `{}` braces.
327
- * Note: Within a group `{}`, neither execution order nor the `#DEPENDS` keyword currently works. This is due to limitations in Robot Framework, which is invoked within Pabot subprocesses. These limitations may be addressed in a future release of Robot Framework. For now, tests or suites within a group will be executed in the order Robot Framework discovers them typically in alphabetical order.
368
+ * You can introduce dependencies using the word `#DEPENDS` after a test declaration. This keyword can be used several times if it is necessary to refer to several different tests.
369
+ * The ordering algorithm is designed to preserve the exact user-defined order as closely as possible. However, if a test's execution dependencies are not yet satisfied, the test is postponed and moved to the earliest possible stage where all its dependencies are fulfilled.
370
+ * Please take care that in case of circular dependencies an exception will be thrown.
371
+ * Note that each `#WAIT` splits suites into separate execution blocks, and it's not possible to define dependencies for suites or tests that are inside another `#WAIT` block or inside another `{}` braces.
372
+ * Ordering mode effect to execution:
373
+ * **Dynamic mode** will schedule dependent tests as soon as all their dependencies are satisfied. Note that in dynamic mode `#WAIT` is ignored, but you can achieve same results with using only `#DEPENDS` keywords.
374
+ * **Static mode** preserves stage barriers and executes the next stage only after all tests in the previous stage finish.
375
+ * Note: Within a group `{}`, neither execution order nor the `#DEPENDS` keyword currently works. This is due to limitations in Robot Framework, which is invoked within Pabot subprocesses. These limitations may be addressed in a future release of Robot Framework. For now, tests or suites within a group will be executed in the order Robot Framework discovers them — typically in alphabetical order.
328
376
  * An example could be:
329
377
 
330
378
  ```
@@ -378,7 +426,7 @@ where order.txt is:
378
426
  #SLEEP 8
379
427
  ```
380
428
 
381
- prints something like this:
429
+ Possible output could be:
382
430
 
383
431
  ```
384
432
  2025-02-15 19:15:00.408321 [0] [ID:1] SLEEPING 6 SECONDS BEFORE STARTING Data 1.suite C
@@ -466,6 +514,7 @@ pabot_results/
466
514
  │ ├── robot_stdout.out
467
515
  │ ├── robot_stderr.out
468
516
  │ └── artifacts...
517
+ └── pabot_manager.log # Pabot's own main log.
469
518
  ```
470
519
 
471
520
  Each `PABOTQUEUEINDEX` folder contains as default:
@@ -0,0 +1,23 @@
1
+ pabot/ProcessManager.py,sha256=Y4SUOLJ-AmQCc1Y49IYjZS34uqRUnlDt-G2AGymAdHg,13627
2
+ pabot/SharedLibrary.py,sha256=mIipGs3ZhKYEakKprcbrMI4P_Un6qI8gE7086xpHaLY,2552
3
+ pabot/__init__.py,sha256=3MzL6YP6ocsJT8YWQWOMv3XsyC6HvKBZkA07ZtDHD2s,203
4
+ pabot/arguments.py,sha256=IBxkqa63hz5RvdnSZxhLjykkuMs91q8AF30QOzmRl_U,12034
5
+ pabot/clientwrapper.py,sha256=yz7battGs0exysnDeLDWJuzpb2Q-qSjitwxZMO2TlJw,231
6
+ pabot/coordinatorwrapper.py,sha256=nQQ7IowD6c246y8y9nsx0HZbt8vS2XODhPVDjm-lyi0,195
7
+ pabot/execution_items.py,sha256=zDVGW0AAeVbM-scC3Yui2TxvIPx1wYyFKHTPU2BkJkY,13329
8
+ pabot/pabot.py,sha256=rF20VvPfcsPO2_d9FyFGwmoRRqfpoS2EFuATMblOHsc,97383
9
+ pabot/pabotlib.py,sha256=vHbqV7L7mIvDzXBh9UcdULrwhBHNn70EDXF_31MNFO4,22320
10
+ pabot/result_merger.py,sha256=rRRSkQa6bdallwT4w9-jHJXvv7X866C1NwD0jdWdSaE,10177
11
+ pabot/robotremoteserver.py,sha256=BdeIni9Q4LJKVDBUlG2uJ9tiyAjrPXwU_YsPq1THWoo,23296
12
+ pabot/workerwrapper.py,sha256=BdELUVDs5BmEkdNBcYTlnP22Cj0tUpZEunYQMAKyKWU,185
13
+ pabot/writer.py,sha256=tRlPI1jH9NWIYy-VkDsb2odDxZwem7ZgccRBOXZvy4w,8861
14
+ pabot/py3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ pabot/py3/client.py,sha256=Od9L4vZ0sozMHq_W_ITQHBBt8kAej40DG58wnxmbHGM,1434
16
+ pabot/py3/coordinator.py,sha256=kBshCzA_1QX_f0WNk42QBJyDYSwSlNM-UEBxOReOj6E,2313
17
+ pabot/py3/messages.py,sha256=7mFr4_0x1JHm5sW8TvKq28Xs_JoeIGku2bX7AyO0kng,2557
18
+ pabot/py3/worker.py,sha256=5rfp4ZiW6gf8GRz6eC0-KUkfx847A91lVtRYpLAv2sg,1612
19
+ robotframework_pabot-5.2.0rc1.dist-info/METADATA,sha256=qQ3V9OzAxIwAZGpUtL9Gi2cetHLXDsTzCPR2Wt1huVw,24792
20
+ robotframework_pabot-5.2.0rc1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
21
+ robotframework_pabot-5.2.0rc1.dist-info/entry_points.txt,sha256=JpAIFADTeFOQWdwmn56KpAil8V3-41ZC5ICXCYm3Ng0,43
22
+ robotframework_pabot-5.2.0rc1.dist-info/top_level.txt,sha256=t3OwfEAsSxyxrhjy_GCJYHKbV_X6AIsgeLhYeHvObG4,6
23
+ robotframework_pabot-5.2.0rc1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,22 +0,0 @@
1
- pabot/SharedLibrary.py,sha256=mIipGs3ZhKYEakKprcbrMI4P_Un6qI8gE7086xpHaLY,2552
2
- pabot/__init__.py,sha256=0g7UY0dKCwXzo3sH_STKWVLBEVtZnj96gmuak8fdlf0,200
3
- pabot/arguments.py,sha256=M1T2QAA0v2BO1bbryLC82RIA0VZZaEGfXnQiXfNcHOU,9577
4
- pabot/clientwrapper.py,sha256=yz7battGs0exysnDeLDWJuzpb2Q-qSjitwxZMO2TlJw,231
5
- pabot/coordinatorwrapper.py,sha256=nQQ7IowD6c246y8y9nsx0HZbt8vS2XODhPVDjm-lyi0,195
6
- pabot/execution_items.py,sha256=zDVGW0AAeVbM-scC3Yui2TxvIPx1wYyFKHTPU2BkJkY,13329
7
- pabot/pabot.py,sha256=wxkCGUzvibj7Jtdqhuyzo7F5k5xOPgVXICWZXbt7cn8,81246
8
- pabot/pabotlib.py,sha256=vHbqV7L7mIvDzXBh9UcdULrwhBHNn70EDXF_31MNFO4,22320
9
- pabot/result_merger.py,sha256=g4mm-BhhMK57Z6j6dpvfL5El1g5onOtfV4RByNrO8g0,9744
10
- pabot/robotremoteserver.py,sha256=L3O2QRKSGSE4ux5M1ip5XJMaelqaxQWJxd9wLLdtpzM,22272
11
- pabot/workerwrapper.py,sha256=BdELUVDs5BmEkdNBcYTlnP22Cj0tUpZEunYQMAKyKWU,185
12
- pabot/py3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- pabot/py3/client.py,sha256=Od9L4vZ0sozMHq_W_ITQHBBt8kAej40DG58wnxmbHGM,1434
14
- pabot/py3/coordinator.py,sha256=kBshCzA_1QX_f0WNk42QBJyDYSwSlNM-UEBxOReOj6E,2313
15
- pabot/py3/messages.py,sha256=7mFr4_0x1JHm5sW8TvKq28Xs_JoeIGku2bX7AyO0kng,2557
16
- pabot/py3/worker.py,sha256=5rfp4ZiW6gf8GRz6eC0-KUkfx847A91lVtRYpLAv2sg,1612
17
- robotframework_pabot-5.1.0.dist-info/licenses/LICENSE.txt,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
18
- robotframework_pabot-5.1.0.dist-info/METADATA,sha256=-3nvXfoJrNCoqf6XVZccsiGhqiU8quQWI3cGk_RV0hY,22070
19
- robotframework_pabot-5.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
- robotframework_pabot-5.1.0.dist-info/entry_points.txt,sha256=JpAIFADTeFOQWdwmn56KpAil8V3-41ZC5ICXCYm3Ng0,43
21
- robotframework_pabot-5.1.0.dist-info/top_level.txt,sha256=t3OwfEAsSxyxrhjy_GCJYHKbV_X6AIsgeLhYeHvObG4,6
22
- robotframework_pabot-5.1.0.dist-info/RECORD,,