wool 0.1rc13__tar.gz → 0.1rc15__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.
Potentially problematic release.
This version of wool might be problematic. Click here for more details.
- {wool-0.1rc13 → wool-0.1rc15}/PKG-INFO +153 -41
- wool-0.1rc15/README.md +232 -0
- {wool-0.1rc13 → wool-0.1rc15}/pyproject.toml +1 -1
- {wool-0.1rc13 → wool-0.1rc15}/wool/__init__.py +1 -26
- wool-0.1rc15/wool/_connection.py +247 -0
- wool-0.1rc15/wool/_context.py +29 -0
- wool-0.1rc15/wool/_loadbalancer.py +213 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/task_pb2_grpc.py +2 -2
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/worker_pb2.py +6 -6
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/worker_pb2.pyi +4 -4
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/worker_pb2_grpc.py +2 -2
- {wool-0.1rc13 → wool-0.1rc15}/wool/_resource_pool.py +3 -3
- wool-0.1rc15/wool/_undefined.py +11 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_work.py +5 -4
- wool-0.1rc15/wool/_worker.py +601 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_worker_discovery.py +24 -36
- {wool-0.1rc13 → wool-0.1rc15}/wool/_worker_pool.py +46 -31
- {wool-0.1rc13 → wool-0.1rc15}/wool/_worker_proxy.py +54 -186
- wool-0.1rc15/wool/_worker_service.py +243 -0
- wool-0.1rc13/README.md +0 -120
- wool-0.1rc13/wool/_worker.py +0 -912
- {wool-0.1rc13 → wool-0.1rc15}/.gitignore +0 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/__init__.py +0 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/exception.py +0 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/task.py +0 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/task_pb2.py +0 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/task_pb2.pyi +0 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_protobuf/worker.py +0 -0
- {wool-0.1rc13 → wool-0.1rc15}/wool/_typing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wool
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1rc15
|
|
4
4
|
Summary: A Python framework for distributed multiprocessing.
|
|
5
5
|
Author-email: Conrad Bzura <conrad@wool.io>
|
|
6
6
|
Maintainer-email: maintainers@wool.io
|
|
@@ -222,14 +222,14 @@ Requires-Dist: hypothesis; extra == 'dev'
|
|
|
222
222
|
Requires-Dist: pytest; extra == 'dev'
|
|
223
223
|
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
224
224
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
225
|
-
Requires-Dist: pytest-grpc-aio~=0.
|
|
225
|
+
Requires-Dist: pytest-grpc-aio~=0.3.0; extra == 'dev'
|
|
226
226
|
Requires-Dist: pytest-mock; extra == 'dev'
|
|
227
227
|
Requires-Dist: ruff; extra == 'dev'
|
|
228
228
|
Description-Content-Type: text/markdown
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+

|
|
231
231
|
|
|
232
|
-
Wool is a native Python package for transparently executing tasks in a horizontally scalable, distributed network of agnostic worker processes. Any picklable async function or method can be converted into a task with a simple decorator and a client connection.
|
|
232
|
+
**Wool** is a native Python package for transparently executing tasks in a horizontally scalable, distributed network of agnostic worker processes. Any picklable async function or method can be converted into a task with a simple decorator and a client connection.
|
|
233
233
|
|
|
234
234
|
## Installation
|
|
235
235
|
|
|
@@ -253,19 +253,73 @@ cd wool
|
|
|
253
253
|
|
|
254
254
|
## Usage
|
|
255
255
|
|
|
256
|
-
###
|
|
256
|
+
### Declaring tasks
|
|
257
257
|
|
|
258
|
-
Wool
|
|
258
|
+
Wool tasks are coroutine functions that are executed in a remote `asyncio` event loop within a worker process. To declare a task, use the `@wool.task` decorator:
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
```python
|
|
261
|
+
import wool
|
|
261
262
|
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
@wool.task
|
|
264
|
+
async def sample_task(x, y):
|
|
265
|
+
return x + y
|
|
264
266
|
```
|
|
265
267
|
|
|
266
|
-
|
|
268
|
+
Tasks must be picklable, stateless, and idempotent. Avoid passing unpicklable objects as arguments or return values.
|
|
269
|
+
|
|
270
|
+
### Worker pools
|
|
271
|
+
|
|
272
|
+
Worker pools are responsible for executing tasks. Wool provides two types of pools:
|
|
273
|
+
|
|
274
|
+
#### Ephemeral pools
|
|
275
|
+
|
|
276
|
+
Ephemeral pools are created and destroyed within the scope of a context manager. Use `wool.pool` to declare an ephemeral pool:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
import asyncio, wool
|
|
280
|
+
|
|
281
|
+
@wool.task
|
|
282
|
+
async def sample_task(x, y):
|
|
283
|
+
return x + y
|
|
284
|
+
|
|
285
|
+
async def main():
|
|
286
|
+
with wool.pool():
|
|
287
|
+
result = await sample_task(1, 2)
|
|
288
|
+
print(f"Result: {result}")
|
|
289
|
+
|
|
290
|
+
asyncio.run(main())
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
#### Durable pools
|
|
294
|
+
|
|
295
|
+
Durable pools are started independently and persist beyond the scope of a single application. Use the `wool` CLI to manage durable pools:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
wool pool up --port 5050 --authkey deadbeef --module tasks
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Connect to a durable pool using `wool.session`:
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
import asyncio, wool
|
|
305
|
+
|
|
306
|
+
@wool.task
|
|
307
|
+
async def sample_task(x, y):
|
|
308
|
+
return x + y
|
|
309
|
+
|
|
310
|
+
async def main():
|
|
311
|
+
with wool.session(port=5050, authkey=b"deadbeef"):
|
|
312
|
+
result = await sample_task(1, 2)
|
|
313
|
+
print(f"Result: {result}")
|
|
314
|
+
|
|
315
|
+
asyncio.run(main())
|
|
316
|
+
```
|
|
267
317
|
|
|
268
|
-
|
|
318
|
+
### CLI commands
|
|
319
|
+
|
|
320
|
+
Wool provides a command-line interface (CLI) for managing worker pools.
|
|
321
|
+
|
|
322
|
+
#### Start the worker pool
|
|
269
323
|
|
|
270
324
|
```sh
|
|
271
325
|
wool pool up --host <host> --port <port> --authkey <authkey> --breadth <breadth> --module <module>
|
|
@@ -275,11 +329,9 @@ wool pool up --host <host> --port <port> --authkey <authkey> --breadth <breadth>
|
|
|
275
329
|
- `--port`: The port number (default: `0`).
|
|
276
330
|
- `--authkey`: The authentication key (default: `b""`).
|
|
277
331
|
- `--breadth`: The number of worker processes (default: number of CPU cores).
|
|
278
|
-
- `--module`: Python module containing Wool task definitions
|
|
279
|
-
|
|
280
|
-
#### Stop the Worker Pool
|
|
332
|
+
- `--module`: Python module containing Wool task definitions (optional, can be specified multiple times).
|
|
281
333
|
|
|
282
|
-
|
|
334
|
+
#### Stop the worker pool
|
|
283
335
|
|
|
284
336
|
```sh
|
|
285
337
|
wool pool down --host <host> --port <port> --authkey <authkey> --wait
|
|
@@ -290,9 +342,7 @@ wool pool down --host <host> --port <port> --authkey <authkey> --wait
|
|
|
290
342
|
- `--authkey`: The authentication key (default: `b""`).
|
|
291
343
|
- `--wait`: Wait for in-flight tasks to complete before shutting down.
|
|
292
344
|
|
|
293
|
-
#### Ping the
|
|
294
|
-
|
|
295
|
-
To ping the worker pool, use the `ping` command:
|
|
345
|
+
#### Ping the worker pool
|
|
296
346
|
|
|
297
347
|
```sh
|
|
298
348
|
wool ping --host <host> --port <port> --authkey <authkey>
|
|
@@ -302,46 +352,108 @@ wool ping --host <host> --port <port> --authkey <authkey>
|
|
|
302
352
|
- `--port`: The port number (required).
|
|
303
353
|
- `--authkey`: The authentication key (default: `b""`).
|
|
304
354
|
|
|
305
|
-
###
|
|
355
|
+
### Advanced usage
|
|
356
|
+
|
|
357
|
+
#### Nested pools and sessions
|
|
306
358
|
|
|
307
|
-
|
|
359
|
+
Wool supports nesting pools and sessions to achieve complex workflows. Tasks can be dispatched to specific pools by nesting contexts:
|
|
308
360
|
|
|
309
|
-
Module defining remote tasks:
|
|
310
|
-
`tasks.py`
|
|
311
361
|
```python
|
|
312
362
|
import asyncio, wool
|
|
313
363
|
|
|
314
|
-
# Decorate an async function using the `task` decorator
|
|
315
364
|
@wool.task
|
|
316
|
-
async def
|
|
365
|
+
async def task_a():
|
|
317
366
|
await asyncio.sleep(1)
|
|
318
|
-
|
|
367
|
+
|
|
368
|
+
@wool.task
|
|
369
|
+
async def task_b():
|
|
370
|
+
with wool.pool(port=5051):
|
|
371
|
+
await task_a()
|
|
372
|
+
|
|
373
|
+
async def main():
|
|
374
|
+
with wool.pool(port=5050):
|
|
375
|
+
await task_a()
|
|
376
|
+
await task_b()
|
|
377
|
+
|
|
378
|
+
asyncio.run(main())
|
|
319
379
|
```
|
|
320
380
|
|
|
321
|
-
|
|
322
|
-
|
|
381
|
+
In this example, `task_a` is executed by two different pools, while `task_b` is executed by the pool on port 5050.
|
|
382
|
+
|
|
383
|
+
### Best practices
|
|
384
|
+
|
|
385
|
+
#### Sizing worker pools
|
|
386
|
+
|
|
387
|
+
When configuring worker pools, it is important to balance the number of processes with the available system resources:
|
|
388
|
+
|
|
389
|
+
- **CPU-bound tasks**: Size the worker pool to match the number of CPU cores. This is the default behavior when spawning a pool.
|
|
390
|
+
- **I/O-bound tasks**: For workloads involving significant I/O, consider oversizing the pool slightly to maximize the system's I/O capacity utilization.
|
|
391
|
+
- **Mixed workloads**: Monitor memory usage and system load to avoid oversubscription, especially for memory-intensive tasks. Use profiling tools to determine the optimal pool size.
|
|
392
|
+
|
|
393
|
+
#### Defining tasks
|
|
394
|
+
|
|
395
|
+
Wool tasks are coroutine functions that execute asynchronously in a remote `asyncio` event loop. To ensure smooth execution and scalability, prioritize:
|
|
396
|
+
|
|
397
|
+
- **Picklability**: Ensure all task arguments and return values are picklable. Avoid passing unpicklable objects such as open file handles, database connections, or lambda functions.
|
|
398
|
+
- **Statelessness and idempotency**: Design tasks to be stateless and idempotent. Avoid relying on global variables or shared mutable state. This ensures predictable behavior and safe retries.
|
|
399
|
+
- **Non-blocking operations**: To achieve higher concurrency, avoid blocking calls within tasks. Use `asyncio`-compatible libraries for I/O operations.
|
|
400
|
+
- **Inter-process synchronization**: Use Wool's synchronization primitives (e.g., `wool.locking`) for inter-worker and inter-pool coordination. Standard `asyncio` primitives will not behave as expected in a multi-process environment.
|
|
401
|
+
|
|
402
|
+
#### Debugging and logging
|
|
403
|
+
|
|
404
|
+
- Enable detailed logging during development to trace task execution and worker pool behavior:
|
|
405
|
+
```python
|
|
406
|
+
import logging
|
|
407
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
408
|
+
```
|
|
409
|
+
- Use Wool's built-in logging configuration to capture worker-specific logs.
|
|
410
|
+
|
|
411
|
+
#### Nested pools and sessions
|
|
412
|
+
|
|
413
|
+
Wool supports nesting pools and sessions to achieve complex workflows. Tasks can be dispatched to specific pools by nesting contexts. This is useful for workflows requiring task segregation or resource isolation.
|
|
414
|
+
|
|
415
|
+
Example:
|
|
323
416
|
```python
|
|
324
417
|
import asyncio, wool
|
|
325
|
-
from tasks import sample_task
|
|
326
418
|
|
|
327
|
-
|
|
419
|
+
@wool.task
|
|
420
|
+
async def task_a():
|
|
421
|
+
await asyncio.sleep(1)
|
|
422
|
+
|
|
423
|
+
@wool.task
|
|
424
|
+
async def task_b():
|
|
425
|
+
with wool.pool(port=5051):
|
|
426
|
+
await task_a()
|
|
427
|
+
|
|
328
428
|
async def main():
|
|
329
|
-
with wool.
|
|
330
|
-
|
|
331
|
-
|
|
429
|
+
with wool.pool(port=5050):
|
|
430
|
+
await task_a()
|
|
431
|
+
await task_b()
|
|
332
432
|
|
|
333
|
-
asyncio.
|
|
433
|
+
asyncio.run(main())
|
|
334
434
|
```
|
|
335
435
|
|
|
336
|
-
|
|
337
|
-
```bash
|
|
338
|
-
wool pool up --port 5050 --authkey deadbeef --breadth 1 --module tasks
|
|
339
|
-
```
|
|
436
|
+
#### Performance optimization
|
|
340
437
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
438
|
+
- Minimize the size of arguments and return values to reduce serialization overhead.
|
|
439
|
+
- For large datasets, consider using shared memory or passing references (e.g., file paths) instead of transferring the entire data.
|
|
440
|
+
- Profile tasks to identify and optimize performance bottlenecks.
|
|
441
|
+
|
|
442
|
+
#### Task cancellation
|
|
443
|
+
|
|
444
|
+
- Handle task cancellations gracefully by cleaning up resources and rolling back partial changes.
|
|
445
|
+
- Use `asyncio.CancelledError` to detect and respond to cancellations.
|
|
446
|
+
|
|
447
|
+
#### Error propagation
|
|
448
|
+
|
|
449
|
+
- Wool propagates exceptions raised within tasks to the caller. Use this feature to handle errors centrally in your application.
|
|
450
|
+
|
|
451
|
+
Example:
|
|
452
|
+
```python
|
|
453
|
+
try:
|
|
454
|
+
result = await some_task()
|
|
455
|
+
except Exception as e:
|
|
456
|
+
print(f"Task failed with error: {e}")
|
|
345
457
|
```
|
|
346
458
|
|
|
347
459
|
## License
|
wool-0.1rc15/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
**Wool** is a native Python package for transparently executing tasks in a horizontally scalable, distributed network of agnostic worker processes. Any picklable async function or method can be converted into a task with a simple decorator and a client connection.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Using pip
|
|
8
|
+
|
|
9
|
+
To install the package using pip, run the following command:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
[uv] pip install --pre wool
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Cloning from GitHub
|
|
16
|
+
|
|
17
|
+
To install the package by cloning from GitHub, run the following commands:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
git clone https://github.com/wool-labs/wool.git
|
|
21
|
+
cd wool
|
|
22
|
+
[uv] pip install ./wool
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Declaring tasks
|
|
28
|
+
|
|
29
|
+
Wool tasks are coroutine functions that are executed in a remote `asyncio` event loop within a worker process. To declare a task, use the `@wool.task` decorator:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import wool
|
|
33
|
+
|
|
34
|
+
@wool.task
|
|
35
|
+
async def sample_task(x, y):
|
|
36
|
+
return x + y
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Tasks must be picklable, stateless, and idempotent. Avoid passing unpicklable objects as arguments or return values.
|
|
40
|
+
|
|
41
|
+
### Worker pools
|
|
42
|
+
|
|
43
|
+
Worker pools are responsible for executing tasks. Wool provides two types of pools:
|
|
44
|
+
|
|
45
|
+
#### Ephemeral pools
|
|
46
|
+
|
|
47
|
+
Ephemeral pools are created and destroyed within the scope of a context manager. Use `wool.pool` to declare an ephemeral pool:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import asyncio, wool
|
|
51
|
+
|
|
52
|
+
@wool.task
|
|
53
|
+
async def sample_task(x, y):
|
|
54
|
+
return x + y
|
|
55
|
+
|
|
56
|
+
async def main():
|
|
57
|
+
with wool.pool():
|
|
58
|
+
result = await sample_task(1, 2)
|
|
59
|
+
print(f"Result: {result}")
|
|
60
|
+
|
|
61
|
+
asyncio.run(main())
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Durable pools
|
|
65
|
+
|
|
66
|
+
Durable pools are started independently and persist beyond the scope of a single application. Use the `wool` CLI to manage durable pools:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
wool pool up --port 5050 --authkey deadbeef --module tasks
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Connect to a durable pool using `wool.session`:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import asyncio, wool
|
|
76
|
+
|
|
77
|
+
@wool.task
|
|
78
|
+
async def sample_task(x, y):
|
|
79
|
+
return x + y
|
|
80
|
+
|
|
81
|
+
async def main():
|
|
82
|
+
with wool.session(port=5050, authkey=b"deadbeef"):
|
|
83
|
+
result = await sample_task(1, 2)
|
|
84
|
+
print(f"Result: {result}")
|
|
85
|
+
|
|
86
|
+
asyncio.run(main())
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### CLI commands
|
|
90
|
+
|
|
91
|
+
Wool provides a command-line interface (CLI) for managing worker pools.
|
|
92
|
+
|
|
93
|
+
#### Start the worker pool
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
wool pool up --host <host> --port <port> --authkey <authkey> --breadth <breadth> --module <module>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- `--host`: The host address (default: `localhost`).
|
|
100
|
+
- `--port`: The port number (default: `0`).
|
|
101
|
+
- `--authkey`: The authentication key (default: `b""`).
|
|
102
|
+
- `--breadth`: The number of worker processes (default: number of CPU cores).
|
|
103
|
+
- `--module`: Python module containing Wool task definitions (optional, can be specified multiple times).
|
|
104
|
+
|
|
105
|
+
#### Stop the worker pool
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
wool pool down --host <host> --port <port> --authkey <authkey> --wait
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- `--host`: The host address (default: `localhost`).
|
|
112
|
+
- `--port`: The port number (required).
|
|
113
|
+
- `--authkey`: The authentication key (default: `b""`).
|
|
114
|
+
- `--wait`: Wait for in-flight tasks to complete before shutting down.
|
|
115
|
+
|
|
116
|
+
#### Ping the worker pool
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
wool ping --host <host> --port <port> --authkey <authkey>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- `--host`: The host address (default: `localhost`).
|
|
123
|
+
- `--port`: The port number (required).
|
|
124
|
+
- `--authkey`: The authentication key (default: `b""`).
|
|
125
|
+
|
|
126
|
+
### Advanced usage
|
|
127
|
+
|
|
128
|
+
#### Nested pools and sessions
|
|
129
|
+
|
|
130
|
+
Wool supports nesting pools and sessions to achieve complex workflows. Tasks can be dispatched to specific pools by nesting contexts:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import asyncio, wool
|
|
134
|
+
|
|
135
|
+
@wool.task
|
|
136
|
+
async def task_a():
|
|
137
|
+
await asyncio.sleep(1)
|
|
138
|
+
|
|
139
|
+
@wool.task
|
|
140
|
+
async def task_b():
|
|
141
|
+
with wool.pool(port=5051):
|
|
142
|
+
await task_a()
|
|
143
|
+
|
|
144
|
+
async def main():
|
|
145
|
+
with wool.pool(port=5050):
|
|
146
|
+
await task_a()
|
|
147
|
+
await task_b()
|
|
148
|
+
|
|
149
|
+
asyncio.run(main())
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
In this example, `task_a` is executed by two different pools, while `task_b` is executed by the pool on port 5050.
|
|
153
|
+
|
|
154
|
+
### Best practices
|
|
155
|
+
|
|
156
|
+
#### Sizing worker pools
|
|
157
|
+
|
|
158
|
+
When configuring worker pools, it is important to balance the number of processes with the available system resources:
|
|
159
|
+
|
|
160
|
+
- **CPU-bound tasks**: Size the worker pool to match the number of CPU cores. This is the default behavior when spawning a pool.
|
|
161
|
+
- **I/O-bound tasks**: For workloads involving significant I/O, consider oversizing the pool slightly to maximize the system's I/O capacity utilization.
|
|
162
|
+
- **Mixed workloads**: Monitor memory usage and system load to avoid oversubscription, especially for memory-intensive tasks. Use profiling tools to determine the optimal pool size.
|
|
163
|
+
|
|
164
|
+
#### Defining tasks
|
|
165
|
+
|
|
166
|
+
Wool tasks are coroutine functions that execute asynchronously in a remote `asyncio` event loop. To ensure smooth execution and scalability, prioritize:
|
|
167
|
+
|
|
168
|
+
- **Picklability**: Ensure all task arguments and return values are picklable. Avoid passing unpicklable objects such as open file handles, database connections, or lambda functions.
|
|
169
|
+
- **Statelessness and idempotency**: Design tasks to be stateless and idempotent. Avoid relying on global variables or shared mutable state. This ensures predictable behavior and safe retries.
|
|
170
|
+
- **Non-blocking operations**: To achieve higher concurrency, avoid blocking calls within tasks. Use `asyncio`-compatible libraries for I/O operations.
|
|
171
|
+
- **Inter-process synchronization**: Use Wool's synchronization primitives (e.g., `wool.locking`) for inter-worker and inter-pool coordination. Standard `asyncio` primitives will not behave as expected in a multi-process environment.
|
|
172
|
+
|
|
173
|
+
#### Debugging and logging
|
|
174
|
+
|
|
175
|
+
- Enable detailed logging during development to trace task execution and worker pool behavior:
|
|
176
|
+
```python
|
|
177
|
+
import logging
|
|
178
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
179
|
+
```
|
|
180
|
+
- Use Wool's built-in logging configuration to capture worker-specific logs.
|
|
181
|
+
|
|
182
|
+
#### Nested pools and sessions
|
|
183
|
+
|
|
184
|
+
Wool supports nesting pools and sessions to achieve complex workflows. Tasks can be dispatched to specific pools by nesting contexts. This is useful for workflows requiring task segregation or resource isolation.
|
|
185
|
+
|
|
186
|
+
Example:
|
|
187
|
+
```python
|
|
188
|
+
import asyncio, wool
|
|
189
|
+
|
|
190
|
+
@wool.task
|
|
191
|
+
async def task_a():
|
|
192
|
+
await asyncio.sleep(1)
|
|
193
|
+
|
|
194
|
+
@wool.task
|
|
195
|
+
async def task_b():
|
|
196
|
+
with wool.pool(port=5051):
|
|
197
|
+
await task_a()
|
|
198
|
+
|
|
199
|
+
async def main():
|
|
200
|
+
with wool.pool(port=5050):
|
|
201
|
+
await task_a()
|
|
202
|
+
await task_b()
|
|
203
|
+
|
|
204
|
+
asyncio.run(main())
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### Performance optimization
|
|
208
|
+
|
|
209
|
+
- Minimize the size of arguments and return values to reduce serialization overhead.
|
|
210
|
+
- For large datasets, consider using shared memory or passing references (e.g., file paths) instead of transferring the entire data.
|
|
211
|
+
- Profile tasks to identify and optimize performance bottlenecks.
|
|
212
|
+
|
|
213
|
+
#### Task cancellation
|
|
214
|
+
|
|
215
|
+
- Handle task cancellations gracefully by cleaning up resources and rolling back partial changes.
|
|
216
|
+
- Use `asyncio.CancelledError` to detect and respond to cancellations.
|
|
217
|
+
|
|
218
|
+
#### Error propagation
|
|
219
|
+
|
|
220
|
+
- Wool propagates exceptions raised within tasks to the caller. Use this feature to handle errors centrally in your application.
|
|
221
|
+
|
|
222
|
+
Example:
|
|
223
|
+
```python
|
|
224
|
+
try:
|
|
225
|
+
result = await some_task()
|
|
226
|
+
except Exception as e:
|
|
227
|
+
print(f"Task failed with error: {e}")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
This project is licensed under the Apache License Version 2.0.
|
|
@@ -2,9 +2,6 @@ from contextvars import ContextVar
|
|
|
2
2
|
from importlib.metadata import PackageNotFoundError
|
|
3
3
|
from importlib.metadata import version
|
|
4
4
|
from typing import Final
|
|
5
|
-
from typing import Generic
|
|
6
|
-
from typing import TypeVar
|
|
7
|
-
from typing import cast
|
|
8
5
|
|
|
9
6
|
from tblib import pickling_support
|
|
10
7
|
|
|
@@ -29,34 +26,12 @@ from wool._worker_proxy import WorkerProxy
|
|
|
29
26
|
pickling_support.install()
|
|
30
27
|
|
|
31
28
|
|
|
32
|
-
SENTINEL = object()
|
|
33
|
-
|
|
34
|
-
T = TypeVar("T")
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class GlobalVar(Generic[T]):
|
|
38
|
-
def __init__(self, default: T | None = None) -> None:
|
|
39
|
-
self._default = default
|
|
40
|
-
self._value = SENTINEL
|
|
41
|
-
|
|
42
|
-
def get(self) -> T | None:
|
|
43
|
-
if self._value is SENTINEL:
|
|
44
|
-
return self._default
|
|
45
|
-
else:
|
|
46
|
-
return cast(T, self._value)
|
|
47
|
-
|
|
48
|
-
def set(self, value: T):
|
|
49
|
-
self._value = value
|
|
50
|
-
|
|
51
|
-
|
|
52
29
|
try:
|
|
53
30
|
__version__ = version("wool")
|
|
54
31
|
except PackageNotFoundError:
|
|
55
32
|
__version__ = "unknown"
|
|
56
33
|
|
|
57
|
-
__proxy__: Final[ContextVar[WorkerProxy | None]] = ContextVar(
|
|
58
|
-
"__proxy__", default=None
|
|
59
|
-
)
|
|
34
|
+
__proxy__: Final[ContextVar[WorkerProxy | None]] = ContextVar("__proxy__", default=None)
|
|
60
35
|
|
|
61
36
|
__proxy_pool__: Final[ContextVar[ResourcePool[WorkerProxy] | None]] = ContextVar(
|
|
62
37
|
"__proxy_pool__", default=None
|