robotframework-parallel-requests 0.1.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_parallel_requests-0.1.0/LICENSE +21 -0
- robotframework_parallel_requests-0.1.0/PKG-INFO +524 -0
- robotframework_parallel_requests-0.1.0/README.md +490 -0
- robotframework_parallel_requests-0.1.0/pyproject.toml +51 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/__init__.py +24 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/library.py +513 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/metrics.py +95 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/rate_limiter.py +76 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/response_store.py +33 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/retry.py +106 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/session.py +27 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/tasks.py +18 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/transport/__init__.py +3 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/transport/base.py +9 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/transport/httpx_async_future.py +82 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/transport/httpx_sync.py +53 -0
- robotframework_parallel_requests-0.1.0/robot_parallel_requests/worker.py +136 -0
- robotframework_parallel_requests-0.1.0/robotframework_parallel_requests.egg-info/PKG-INFO +524 -0
- robotframework_parallel_requests-0.1.0/robotframework_parallel_requests.egg-info/SOURCES.txt +25 -0
- robotframework_parallel_requests-0.1.0/robotframework_parallel_requests.egg-info/dependency_links.txt +1 -0
- robotframework_parallel_requests-0.1.0/robotframework_parallel_requests.egg-info/requires.txt +12 -0
- robotframework_parallel_requests-0.1.0/robotframework_parallel_requests.egg-info/top_level.txt +1 -0
- robotframework_parallel_requests-0.1.0/setup.cfg +4 -0
- robotframework_parallel_requests-0.1.0/setup.py +9 -0
- robotframework_parallel_requests-0.1.0/tests/test_advanced.py +411 -0
- robotframework_parallel_requests-0.1.0/tests/test_core.py +116 -0
- robotframework_parallel_requests-0.1.0/tests/test_optimizations.py +180 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tallin Carlson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robotframework-parallel-requests
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Parallelized HTTP Request library for Robot Framework (httpx + ThreadPool MVP)
|
|
5
|
+
Author: Tallin Carlson
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tallin32/robotframework-parallel-requests
|
|
8
|
+
Project-URL: Repository, https://github.com/tallin32/robotframework-parallel-requests
|
|
9
|
+
Project-URL: Issues, https://github.com/tallin32/robotframework-parallel-requests/issues
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Framework :: Robot Framework
|
|
12
|
+
Classifier: Framework :: Robot Framework :: Library
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: httpx>=0.23.0
|
|
24
|
+
Requires-Dist: robotframework>=4.0
|
|
25
|
+
Provides-Extra: http2
|
|
26
|
+
Requires-Dist: h2>=3.0.0; extra == "http2"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: respx>=0.20.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
30
|
+
Requires-Dist: build; extra == "dev"
|
|
31
|
+
Requires-Dist: twine; extra == "dev"
|
|
32
|
+
Requires-Dist: h2>=3.0.0; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# robotframework-parallel-requests
|
|
36
|
+
|
|
37
|
+
[](https://badge.fury.io/py/robotframework-parallel-requests)
|
|
38
|
+
[](https://github.com/tallin32/robotframework-parallel-requests/actions/workflows/tests.yml)
|
|
39
|
+
[](https://tallin32.github.io/robotframework-parallel-requests/)
|
|
40
|
+
[](https://pypi.org/project/robotframework-parallel-requests/)
|
|
41
|
+
[](https://opensource.org/licenses/MIT)
|
|
42
|
+
|
|
43
|
+
A Robot Framework library for parallelized HTTP requests using `httpx` and ThreadPool.
|
|
44
|
+
|
|
45
|
+
**Queue multiple HTTP requests and run them in parallel**, then retrieve responses by ID or await all responses. Designed for testing scenarios like rate limiting, bulk API operations, and performance validation.
|
|
46
|
+
|
|
47
|
+
## Status
|
|
48
|
+
|
|
49
|
+
⚠️ **Beta** — Core functionality stable; API may evolve.
|
|
50
|
+
|
|
51
|
+
Current production scope uses synchronous `httpx` transport with ThreadPool concurrency
|
|
52
|
+
(connection pool sized to worker count; optional HTTP/2). Native async transport is planned
|
|
53
|
+
for a later release.
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- **Parallelized requests**: Queue up multiple HTTP requests and execute them concurrently using ThreadPoolExecutor.
|
|
58
|
+
- **RequestsLibrary-like API**: Keywords mirror RequestsLibrary for familiarity (with `Parallel ` prefix).
|
|
59
|
+
- **Direct response access**: Retrieve the underlying `httpx.Response` object for advanced assertions.
|
|
60
|
+
- **Simple session management**: Create named sessions with base URLs and default headers.
|
|
61
|
+
|
|
62
|
+
## Table of Contents
|
|
63
|
+
|
|
64
|
+
- [Installation](#installation)
|
|
65
|
+
- [Quick Start](#quick-start)
|
|
66
|
+
- [Comparison with RequestsLibrary](#comparison-with-requestslibrary)
|
|
67
|
+
- [Keywords Reference](#keywords)
|
|
68
|
+
- [Library Initialization](#library-initialization)
|
|
69
|
+
- [Use Cases](#use-cases)
|
|
70
|
+
- [Error Handling](#error-handling)
|
|
71
|
+
- [Best Practices](#best-practices)
|
|
72
|
+
- [Architecture](#architecture)
|
|
73
|
+
- [Testing](#testing)
|
|
74
|
+
- [Future Enhancements](#future-enhancements)
|
|
75
|
+
- [Contributing](#contributing)
|
|
76
|
+
- [Release Process](#release-process)
|
|
77
|
+
- [License](#license)
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
**From PyPI:**
|
|
82
|
+
```bash
|
|
83
|
+
pip install robotframework-parallel-requests
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Optional HTTP/2 support:**
|
|
87
|
+
```bash
|
|
88
|
+
pip install robotframework-parallel-requests[http2]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Development/Local Installation:**
|
|
92
|
+
```bash
|
|
93
|
+
pip install -r requirements.txt
|
|
94
|
+
pip install -e .
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Prerequisites:**
|
|
98
|
+
- Python 3.8+
|
|
99
|
+
- Robot Framework 4.0+
|
|
100
|
+
|
|
101
|
+
**Dependencies:**
|
|
102
|
+
- `httpx>=0.23.0` - HTTP client
|
|
103
|
+
- `robotframework>=4.0` - Robot Framework core
|
|
104
|
+
- `pytest>=7.0` - Testing (dev only)
|
|
105
|
+
- `respx>=0.20.0` - httpx mocking (dev only)
|
|
106
|
+
|
|
107
|
+
## Quick Start
|
|
108
|
+
|
|
109
|
+
### Basic Usage
|
|
110
|
+
|
|
111
|
+
```robot
|
|
112
|
+
*** Settings ***
|
|
113
|
+
Library robot_parallel_requests
|
|
114
|
+
|
|
115
|
+
*** Test Cases ***
|
|
116
|
+
Queue And Wait For Responses
|
|
117
|
+
Parallel Create Session alias=default base_url=https://api.example.com
|
|
118
|
+
${id1}= Parallel Queue Request GET /users/1
|
|
119
|
+
${id2}= Parallel Queue Request GET /users/2
|
|
120
|
+
${id3}= Parallel Queue Request GET /users/3
|
|
121
|
+
|
|
122
|
+
Parallel Wait For All Requests timeout=30
|
|
123
|
+
|
|
124
|
+
${status1}= Parallel Get Response Status ${id1}
|
|
125
|
+
Should Be Equal ${status1} 200
|
|
126
|
+
|
|
127
|
+
${body}= Parallel Get Response Body ${id1}
|
|
128
|
+
Log ${body}
|
|
129
|
+
|
|
130
|
+
Parallel Shutdown
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Using Response Objects Directly
|
|
134
|
+
|
|
135
|
+
```robot
|
|
136
|
+
*** Test Cases ***
|
|
137
|
+
Access Response Object
|
|
138
|
+
Parallel Create Session
|
|
139
|
+
${id}= Parallel Queue Request GET https://httpbin.org/json
|
|
140
|
+
Parallel Wait For All Requests timeout=30
|
|
141
|
+
|
|
142
|
+
${response}= Parallel Get Response Object ${id}
|
|
143
|
+
# Now you have the underlying httpx.Response object
|
|
144
|
+
Should Be Equal ${response.status_code} 200
|
|
145
|
+
${json_data}= Parallel Get Response JSON ${id}
|
|
146
|
+
Log ${json_data}
|
|
147
|
+
|
|
148
|
+
Parallel Shutdown
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Comparison with RequestsLibrary
|
|
152
|
+
|
|
153
|
+
| Feature | robot_parallel_requests | RequestsLibrary |
|
|
154
|
+
|---------|------------------------|-----------------|
|
|
155
|
+
| **Parallel/Async Requests** | ✅ Native ThreadPoolExecutor | ❌ Sequential only |
|
|
156
|
+
| **Queue Multiple Requests** | ✅ Yes, with ID-based retrieval | ❌ No |
|
|
157
|
+
| **Bulk Operations** | ✅ Optimized | ❌ Requires loops + waits |
|
|
158
|
+
| **Rate Limiting Tests** | ✅ Token bucket with per-send throttling | ⚠️ Difficult/slow |
|
|
159
|
+
| **API Compatibility** | Similar keywords (with `Parallel ` prefix) | — |
|
|
160
|
+
| **Direct Response Objects** | ✅ `httpx.Response` access | ✅ `requests.Response` access |
|
|
161
|
+
| **Session Management** | ✅ Named sessions | ✅ Named sessions |
|
|
162
|
+
|
|
163
|
+
**When to use robot_parallel_requests:**
|
|
164
|
+
- Testing APIs with rate limits, quotas, or concurrency requirements
|
|
165
|
+
- Bulk operations (e.g., creating 100 records in parallel)
|
|
166
|
+
- Performance/load testing within Robot Framework
|
|
167
|
+
- Simulating real-world parallel client behavior
|
|
168
|
+
- Optimizing tests that make many requests in succession
|
|
169
|
+
|
|
170
|
+
**When to stick with RequestsLibrary:**
|
|
171
|
+
- Simple sequential API testing
|
|
172
|
+
- Lightweight HTTP assertions
|
|
173
|
+
- No parallel workload requirements
|
|
174
|
+
- But feel free to use both—we're good with that
|
|
175
|
+
|
|
176
|
+
## Error Handling
|
|
177
|
+
|
|
178
|
+
When a request fails (network error, timeout, invalid URL), the exception is stored in the response store:
|
|
179
|
+
|
|
180
|
+
```robot
|
|
181
|
+
*** Test Cases ***
|
|
182
|
+
Handle Request Failures
|
|
183
|
+
Parallel Create Session
|
|
184
|
+
${id1}= Parallel Queue Request GET https://httpbin.org/delay/2 timeout=1
|
|
185
|
+
${id2}= Parallel Queue Request GET https://invalid-domain-12345.com
|
|
186
|
+
|
|
187
|
+
Parallel Wait For All Requests timeout=10
|
|
188
|
+
|
|
189
|
+
# Check if response is an exception
|
|
190
|
+
${resp}= Parallel Get Response Object ${id1}
|
|
191
|
+
Run Keyword If '${type(resp).__name__}' == 'ReadTimeout' Log Request timed out
|
|
192
|
+
|
|
193
|
+
# For a regular response, status code is safe
|
|
194
|
+
${resp2}= Parallel Get Response Object ${id2}
|
|
195
|
+
Run Keyword If '${type(resp2).__name__}' == 'ConnectError' Log Request failed: ${resp2}
|
|
196
|
+
|
|
197
|
+
Parallel Shutdown
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Safe Patterns:**
|
|
201
|
+
- Always call `Parallel Wait For All Requests` before retrieving responses
|
|
202
|
+
- Use `Parallel Get Response Object` and check the exception type if needed
|
|
203
|
+
- Use `Run Keyword If` with type checks for conditional error handling
|
|
204
|
+
|
|
205
|
+
## Best Practices
|
|
206
|
+
|
|
207
|
+
**Library Scope:**
|
|
208
|
+
- This library sets `ROBOT_LIBRARY_SCOPE = "TEST"` so each test gets a fresh instance and resources are always isolated.
|
|
209
|
+
- This is the most idiomatic and robust approach for libraries managing connections, pools, or other stateful resources.
|
|
210
|
+
|
|
211
|
+
**Shutdown Handling:**
|
|
212
|
+
- Shutdown runs automatically at end of each test via the library listener.
|
|
213
|
+
- Explicit `Parallel Shutdown` is still safe (idempotent) and useful mid-test.
|
|
214
|
+
- ❌ **Bad:** Calling `Parallel Shutdown` mid-test and then queueing more requests without `Parallel Set Worker Count` to recreate the pool.
|
|
215
|
+
|
|
216
|
+
**Worker Count:**
|
|
217
|
+
- Default `worker_count=5` is suitable for most scenarios.
|
|
218
|
+
- For high-throughput tests, increase to 10-20 (connection pool limits scale with workers).
|
|
219
|
+
- For I/O-heavy operations, ThreadPoolExecutor can handle 50+ safely.
|
|
220
|
+
- Enable `http2=True` (optional `h2` extra) if you want multiplexing to a single origin.
|
|
221
|
+
|
|
222
|
+
**Timeout Handling:**
|
|
223
|
+
- Always set explicit timeouts in `Parallel Wait For All Requests` to prevent hanging tests.
|
|
224
|
+
- Individual request timeouts (via `timeout=` parameter in `Queue Request`) affect only that request.
|
|
225
|
+
- By default, if the wait timeout expires, the keyword logs a warning. Use `fail_on_timeout=${True}` (keyword or library init) to fail the test instead.
|
|
226
|
+
- Not-yet-started futures are cancelled on timeout; in-flight HTTP calls may still finish in the background.
|
|
227
|
+
|
|
228
|
+
**Sessions:**
|
|
229
|
+
- Omitting `session=` automatically uses the `default` session when one was created.
|
|
230
|
+
- Pass `session=<alias>` for non-default sessions.
|
|
231
|
+
|
|
232
|
+
**Request IDs:**
|
|
233
|
+
- Each queued request must use a unique `id` when you provide a custom value. Reusing an ID raises `ValueError`.
|
|
234
|
+
|
|
235
|
+
**Batch waits:**
|
|
236
|
+
- `Parallel Wait For All And Get Responses` returns only the requests queued since the previous wait in the same test.
|
|
237
|
+
|
|
238
|
+
**Rate limiting:**
|
|
239
|
+
- Throttling is enforced when requests are sent, not when they are queued.
|
|
240
|
+
- Default burst size is `requests + 1` (e.g. `105 per="minute"` → burst `106`).
|
|
241
|
+
- Retries consume rate-limit tokens on each HTTP attempt.
|
|
242
|
+
|
|
243
|
+
**If you override library scope:**
|
|
244
|
+
- Use `Suite Teardown` to call shutdown/cleanup keywords.
|
|
245
|
+
- Avoid calling shutdown in individual tests unless you fully understand the implications.
|
|
246
|
+
|
|
247
|
+
## Keywords
|
|
248
|
+
|
|
249
|
+
### Session Management
|
|
250
|
+
|
|
251
|
+
**Parallel Create Session**
|
|
252
|
+
- **Arguments:** `alias` (str, default `default`), `base_url` (str, optional), `headers` (dict, optional)
|
|
253
|
+
- **Description:** Create a named session with optional base URL and default headers. The `default` session is applied automatically when queue keywords omit `session=`.
|
|
254
|
+
|
|
255
|
+
**Parallel Shutdown**
|
|
256
|
+
- **Description:** Shutdown worker pool and close transport (also runs automatically at end of each test).
|
|
257
|
+
|
|
258
|
+
### Request Queuing
|
|
259
|
+
|
|
260
|
+
**Parallel Queue Request**
|
|
261
|
+
- **Arguments:** `method` (str), `url` (str), `session` (str, optional), `id` (str, optional), `**kwargs` (headers, json, data, params, etc.)
|
|
262
|
+
- **Returns:** Response ID (string)
|
|
263
|
+
- **Description:** Queue a request to be processed by the worker pool. Returns a response ID for later retrieval. Custom `id` values must be unique within the test instance.
|
|
264
|
+
|
|
265
|
+
**Parallel Queue Many**
|
|
266
|
+
- **Arguments:** `requests` (list of dicts or `[method, url]` pairs), `session` (str, optional)
|
|
267
|
+
- **Returns:** List of response IDs
|
|
268
|
+
- **Description:** Bulk-enqueue many requests and return their IDs in order.
|
|
269
|
+
|
|
270
|
+
**Parallel Start Workers**
|
|
271
|
+
- **Description:** Start worker pool (workers are ready on init; this is a no-op in MVP).
|
|
272
|
+
|
|
273
|
+
**Parallel Wait For All Requests**
|
|
274
|
+
- **Arguments:** `timeout` (float, optional, seconds), `fail_on_timeout` (bool, optional)
|
|
275
|
+
- **Description:** Block until all requests queued since the last wait complete or the timeout expires. Logs a warning (or raises `TimeoutError` when fail-on-timeout is enabled) if any requests remain incomplete.
|
|
276
|
+
|
|
277
|
+
**Parallel Wait For All And Get Responses**
|
|
278
|
+
- **Arguments:** `timeout` (float, optional, seconds), `fail_on_timeout` (bool, optional)
|
|
279
|
+
- **Returns:** List of `httpx.Response` or Exception objects in submission order for the current pending batch only.
|
|
280
|
+
|
|
281
|
+
### Response Retrieval
|
|
282
|
+
|
|
283
|
+
**Parallel Get Response Object**
|
|
284
|
+
- **Arguments:** `id` (str)
|
|
285
|
+
- **Returns:** `httpx.Response` object (or Exception if request failed)
|
|
286
|
+
- **Description:** Retrieve the underlying response object for direct assertions.
|
|
287
|
+
|
|
288
|
+
**Parallel Get Response Status**
|
|
289
|
+
- **Arguments:** `id` (str)
|
|
290
|
+
- **Returns:** Status code (int)
|
|
291
|
+
|
|
292
|
+
**Parallel Get Response Body**
|
|
293
|
+
- **Arguments:** `id` (str)
|
|
294
|
+
- **Returns:** Response body as string
|
|
295
|
+
|
|
296
|
+
**Parallel Get Response JSON**
|
|
297
|
+
- **Arguments:** `id` (str)
|
|
298
|
+
- **Returns:** Parsed JSON (dict/list)
|
|
299
|
+
|
|
300
|
+
### Configuration
|
|
301
|
+
|
|
302
|
+
**Parallel Set Worker Count**
|
|
303
|
+
- **Arguments:** `count` (int)
|
|
304
|
+
- **Description:** Adjust the number of concurrent worker threads.
|
|
305
|
+
|
|
306
|
+
**Parallel Set Rate Limit**
|
|
307
|
+
- **Arguments:** `requests` (float), `per` (str, default `"second"`), `burst_size` (int, optional)
|
|
308
|
+
- **Description:** Configure client-side token-bucket rate limiting. Default burst size is `requests + 1`.
|
|
309
|
+
|
|
310
|
+
**Parallel Set Retry Policy**
|
|
311
|
+
- **Arguments:** `max_retries`, `backoff_factor`, `retry_statuses`, `jitter`
|
|
312
|
+
- **Description:** Configure exponential backoff retries for selected HTTP status codes and transport errors (timeouts/network). Jitter is applied to backoff waits.
|
|
313
|
+
|
|
314
|
+
**Parallel Get Metrics**
|
|
315
|
+
- **Description:** Return aggregated request metrics including retry counts and requests per second.
|
|
316
|
+
|
|
317
|
+
## Library Initialization
|
|
318
|
+
|
|
319
|
+
### Arguments
|
|
320
|
+
|
|
321
|
+
- `worker_count` (int, default: `5`): Number of worker threads in the pool (connection limits follow).
|
|
322
|
+
- `fail_on_timeout` (bool, default: `False`): Make wait keywords raise `TimeoutError` when incomplete.
|
|
323
|
+
- `http2` (bool, default: `False`): Enable HTTP/2 (requires optional `h2` extra).
|
|
324
|
+
- `cancel_pending_on_timeout` (bool, default: `True`): Cancel not-yet-started futures when a wait times out.
|
|
325
|
+
|
|
326
|
+
### Examples
|
|
327
|
+
|
|
328
|
+
```robot
|
|
329
|
+
# Default: 5 worker threads
|
|
330
|
+
Library robot_parallel_requests
|
|
331
|
+
|
|
332
|
+
# High concurrency: 20 worker threads, fail waits on timeout
|
|
333
|
+
Library robot_parallel_requests worker_count=20 fail_on_timeout=${True}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Use Cases
|
|
337
|
+
|
|
338
|
+
### 1. Rate Limiting Testing
|
|
339
|
+
Queue 101 requests (where the 101st should fail) to test error handling and rate limits:
|
|
340
|
+
|
|
341
|
+
```robot
|
|
342
|
+
*** Test Cases ***
|
|
343
|
+
Test Rate Limit With Bulk Requests
|
|
344
|
+
Parallel Create Session
|
|
345
|
+
FOR ${i} IN RANGE 101
|
|
346
|
+
${id}= Parallel Queue Request POST /api/favorite-restaurants json={"name": "Restaurant ${i}"}
|
|
347
|
+
END
|
|
348
|
+
|
|
349
|
+
Parallel Wait For All Requests timeout=60
|
|
350
|
+
|
|
351
|
+
# Verify 100 succeeded, 1 failed
|
|
352
|
+
# ... retrieve and check statuses
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 2. Parallel API Calls
|
|
356
|
+
Fetch multiple user profiles in parallel:
|
|
357
|
+
|
|
358
|
+
```robot
|
|
359
|
+
*** Test Cases ***
|
|
360
|
+
Fetch Multiple User Profiles
|
|
361
|
+
Parallel Create Session base_url=https://api.example.com
|
|
362
|
+
@{user_ids}= Create List 1 2 3 4 5
|
|
363
|
+
@{response_ids}= Create List
|
|
364
|
+
|
|
365
|
+
FOR ${user_id} IN @{user_ids}
|
|
366
|
+
${id}= Parallel Queue Request GET /users/${user_id}
|
|
367
|
+
Append To List @{response_ids} ${id}
|
|
368
|
+
END
|
|
369
|
+
|
|
370
|
+
Parallel Wait For All Requests timeout=30
|
|
371
|
+
|
|
372
|
+
FOR ${id} IN @{response_ids}
|
|
373
|
+
${resp}= Parallel Get Response Object ${id}
|
|
374
|
+
Should Be Equal ${resp.status_code} 200
|
|
375
|
+
END
|
|
376
|
+
|
|
377
|
+
Parallel Shutdown
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 3. Direct Response Assertions
|
|
381
|
+
Use the raw response object for complex assertions:
|
|
382
|
+
|
|
383
|
+
```robot
|
|
384
|
+
*** Test Cases ***
|
|
385
|
+
Advanced Response Assertions
|
|
386
|
+
Parallel Create Session
|
|
387
|
+
${id}= Parallel Queue Request GET https://httpbin.org/headers
|
|
388
|
+
Parallel Wait For All Requests
|
|
389
|
+
|
|
390
|
+
${resp}= Parallel Get Response Object ${id}
|
|
391
|
+
Should Contain ${resp.headers['user-agent']} python-httpx
|
|
392
|
+
Should Be Equal As Numbers ${resp.elapsed.total_seconds()} ${0} delta=5
|
|
393
|
+
|
|
394
|
+
Parallel Shutdown
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Testing
|
|
398
|
+
|
|
399
|
+
Run unit tests:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
pytest tests/ -v
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Run example Robot tests (requires Robot Framework installed in venv):
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
robot examples/parallel_requests.robot
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Architecture
|
|
412
|
+
|
|
413
|
+
- **`tasks.py`**: `RequestTask` dataclass for queued requests.
|
|
414
|
+
- **`response_store.py`**: In-memory storage for responses by ID.
|
|
415
|
+
- **`transport/base.py`**: Abstract transport interface.
|
|
416
|
+
- **`transport/httpx_sync.py`**: Synchronous httpx-based transport.
|
|
417
|
+
- **`worker.py`**: ThreadPoolExecutor-based worker pool.
|
|
418
|
+
- **`library.py`**: Robot Framework library with keywords.
|
|
419
|
+
|
|
420
|
+
## Future Enhancements
|
|
421
|
+
|
|
422
|
+
- **Async transport** (`transport/httpx_async.py`) using `httpx.AsyncClient` for very high concurrency.
|
|
423
|
+
- **Circuit breaker / adaptive backoff** to complement existing retry policy.
|
|
424
|
+
- **Structured logging / tracing hooks** (OpenTelemetry integration).
|
|
425
|
+
- **Per-session prioritized queues** for differentiated QoS.
|
|
426
|
+
- **Optional persistent response cache** (configurable TTL).
|
|
427
|
+
|
|
428
|
+
## Contributing
|
|
429
|
+
|
|
430
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup,
|
|
431
|
+
branching, and pull request expectations.
|
|
432
|
+
|
|
433
|
+
## License
|
|
434
|
+
|
|
435
|
+
MIT — see [LICENSE](LICENSE).
|
|
436
|
+
|
|
437
|
+
## Release Process
|
|
438
|
+
|
|
439
|
+
Automated releases are driven by Git tags and a GitHub Actions workflow (`publish.yml`).
|
|
440
|
+
|
|
441
|
+
### Version Tags (PEP 440)
|
|
442
|
+
|
|
443
|
+
| Type | Example | Notes |
|
|
444
|
+
|------|---------|-------|
|
|
445
|
+
| Final | `v0.1.0` | Stable release consumers get by default |
|
|
446
|
+
| Release Candidate | `v0.1.0rc1` | Treated as prerelease; published to PyPI & TestPyPI |
|
|
447
|
+
| Beta / Alpha | `v0.1.0b1`, `v0.1.0a1` | Published to TestPyPI only |
|
|
448
|
+
| Dev Snapshot | `v0.1.0.dev2` | Iterative build, TestPyPI only |
|
|
449
|
+
|
|
450
|
+
### Publishing Matrix
|
|
451
|
+
|
|
452
|
+
| Tag Type | TestPyPI | PyPI | GitHub Release | Prerelease Flag |
|
|
453
|
+
|----------|----------|-----|----------------|-----------------|
|
|
454
|
+
| Final (`vX.Y.Z`) | No | Yes | Yes | No |
|
|
455
|
+
| RC (`vX.Y.ZrcN`) | Yes | Yes | Yes | Yes |
|
|
456
|
+
| Beta/Alpha (`vX.Y.ZbN/aN`) | Yes | No | Yes | Yes |
|
|
457
|
+
| Dev (`vX.Y.Z.devN`) | Yes | No | Yes | Yes |
|
|
458
|
+
|
|
459
|
+
### Changelog Enforcement
|
|
460
|
+
|
|
461
|
+
Final and RC tags must have a `## [X.Y.Z]` section in `CHANGELOG.md`. Missing sections cause the workflow to fail.
|
|
462
|
+
|
|
463
|
+
### Prerelease Notes Generation
|
|
464
|
+
|
|
465
|
+
For dev/alpha/beta tags, release notes are generated from the commit diff between the new tag and the previous `v*` tag:
|
|
466
|
+
|
|
467
|
+
```
|
|
468
|
+
### v0.1.0b1
|
|
469
|
+
Changes since v0.1.0:
|
|
470
|
+
- abc123 Short commit message (Author)
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Trusted Publishing
|
|
474
|
+
|
|
475
|
+
Configure trusted publishers with workflow file **`publish.yml`**.
|
|
476
|
+
|
|
477
|
+
| Tag type | GitHub environment |
|
|
478
|
+
|----------|--------------------|
|
|
479
|
+
| Dev / alpha / beta | `testpypi` |
|
|
480
|
+
| RC | `testpypi` and `pypi` |
|
|
481
|
+
| Final | `pypi` |
|
|
482
|
+
|
|
483
|
+
### Release Steps
|
|
484
|
+
|
|
485
|
+
1. Update `CHANGELOG.md` (for final/RC).
|
|
486
|
+
2. Run tests: `pytest -v` and `robot examples/parallel_requests.robot`.
|
|
487
|
+
3. Ensure the matching GitHub environment(s) and PyPI/TestPyPI trusted publishers exist.
|
|
488
|
+
4. Tag and push:
|
|
489
|
+
```bash
|
|
490
|
+
git tag v0.1.0rc1
|
|
491
|
+
git push origin v0.1.0rc1
|
|
492
|
+
# Final:
|
|
493
|
+
git tag v0.1.0
|
|
494
|
+
git push origin v0.1.0
|
|
495
|
+
```
|
|
496
|
+
5. Workflow builds, uploads, generates notes, creates GitHub Release.
|
|
497
|
+
|
|
498
|
+
### Installing Pre-Releases
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
pip install --pre robotframework-parallel-requests
|
|
502
|
+
# Or pin a specific build
|
|
503
|
+
pip install robotframework-parallel-requests==0.1.0rc1
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### TestPyPI Validation
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
pip install -i https://test.pypi.org/simple robotframework-parallel-requests==0.1.0.dev2 --extra-index-url https://pypi.org/simple
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Patch / Hotfix
|
|
513
|
+
|
|
514
|
+
Add a new section in CHANGELOG, tag, push:
|
|
515
|
+
```bash
|
|
516
|
+
git tag v0.1.1
|
|
517
|
+
git push origin v0.1.1
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Yanks & Post Releases
|
|
521
|
+
|
|
522
|
+
If a bad release ships, yank on PyPI and publish `X.Y.Z.post1` with the fix.
|
|
523
|
+
|
|
524
|
+
|