wool 0.1rc13__py3-none-any.whl → 0.1rc15__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.

Potentially problematic release.


This version of wool might be problematic. Click here for more details.

@@ -0,0 +1,243 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from contextlib import contextmanager
5
+ from typing import AsyncIterator
6
+
7
+ import cloudpickle
8
+ from grpc import StatusCode
9
+ from grpc.aio import ServicerContext
10
+
11
+ import wool
12
+ from wool import _protobuf as pb
13
+ from wool._work import WoolTask
14
+ from wool._work import WoolTaskEvent
15
+
16
+
17
+ class ReadOnlyEvent:
18
+ """A read-only wrapper around :class:`asyncio.Event`.
19
+
20
+ Provides access to check if an event is set and wait for it to be
21
+ set, but prevents external code from setting or clearing the event.
22
+
23
+ :param event:
24
+ The underlying :class:`asyncio.Event` to wrap.
25
+ """
26
+
27
+ def __init__(self, event: asyncio.Event):
28
+ self._event = event
29
+
30
+ def is_set(self) -> bool:
31
+ """Check if the event is set.
32
+
33
+ :returns:
34
+ ``True`` if the event is set, ``False`` otherwise.
35
+ """
36
+ return self._event.is_set()
37
+
38
+ async def wait(self) -> None:
39
+ """Wait until the underlying event is set."""
40
+ await self._event.wait()
41
+
42
+
43
+ class WorkerService(pb.worker.WorkerServicer):
44
+ """gRPC service implementation for executing distributed wool tasks.
45
+
46
+ :class:`WorkerService` implements the gRPC WorkerServicer
47
+ interface, providing remote procedure calls for task scheduling
48
+ and worker lifecycle management. Tasks are executed in the same
49
+ asyncio event loop as the gRPC server.
50
+
51
+ .. note::
52
+ Tasks are executed asynchronously in the current event loop
53
+ and results are serialized for transport back to the client.
54
+ The service maintains a set of running tasks for proper
55
+ lifecycle management during shutdown.
56
+
57
+ During shutdown, the service stops accepting new requests
58
+ immediately when the :meth:`stop` RPC is called, returning
59
+ UNAVAILABLE errors to new :meth:`dispatch` requests while
60
+ allowing existing tasks to complete gracefully.
61
+
62
+ The service provides :attr:`stopping` and
63
+ :attr:`stopped` properties to access the internal shutdown
64
+ state events.
65
+ """
66
+
67
+ _tasks: set[asyncio.Task]
68
+ _stopped: asyncio.Event
69
+ _stopping: asyncio.Event
70
+ _task_completed: asyncio.Event
71
+
72
+ def __init__(self):
73
+ self._stopped = asyncio.Event()
74
+ self._stopping = asyncio.Event()
75
+ self._task_completed = asyncio.Event()
76
+ self._tasks = set()
77
+
78
+ @property
79
+ def stopping(self) -> ReadOnlyEvent:
80
+ """Read-only event signaling that the service is stopping.
81
+
82
+ :returns:
83
+ A :class:`ReadOnlyEvent`.
84
+ """
85
+ return ReadOnlyEvent(self._stopping)
86
+
87
+ @property
88
+ def stopped(self) -> ReadOnlyEvent:
89
+ """Read-only event signaling that the service has stopped.
90
+
91
+ :returns:
92
+ A :class:`ReadOnlyEvent`.
93
+ """
94
+ return ReadOnlyEvent(self._stopped)
95
+
96
+ async def dispatch(
97
+ self, request: pb.task.Task, context: ServicerContext
98
+ ) -> AsyncIterator[pb.worker.Response]:
99
+ """Execute a task in the current event loop.
100
+
101
+ Deserializes the incoming task into a :class:`WoolTask`
102
+ instance, schedules it for execution in the current asyncio
103
+ event loop, and yields responses for acknowledgment and result.
104
+
105
+ :param request:
106
+ The protobuf task message containing the serialized task
107
+ data.
108
+ :param context:
109
+ The :class:`grpc.aio.ServicerContext` for this request.
110
+ :yields:
111
+ First yields an Ack Response when task processing begins,
112
+ then yields a Response containing the task result.
113
+
114
+ .. note::
115
+ Emits a :class:`WoolTaskEvent` when the task is
116
+ scheduled for execution.
117
+ """
118
+ if self._stopping.is_set():
119
+ await context.abort(
120
+ StatusCode.UNAVAILABLE, "Worker service is shutting down"
121
+ )
122
+
123
+ with self._tracker(WoolTask.from_protobuf(request)) as task:
124
+ try:
125
+ yield pb.worker.Response(ack=pb.worker.Ack())
126
+ try:
127
+ result = pb.task.Result(dump=cloudpickle.dumps(await task))
128
+ yield pb.worker.Response(result=result)
129
+ except Exception as e:
130
+ exception = pb.task.Exception(dump=cloudpickle.dumps(e))
131
+ yield pb.worker.Response(exception=exception)
132
+ except asyncio.CancelledError as e:
133
+ exception = pb.task.Exception(dump=cloudpickle.dumps(e))
134
+ yield pb.worker.Response(exception=exception)
135
+
136
+ async def stop(
137
+ self, request: pb.worker.StopRequest, context: ServicerContext | None
138
+ ) -> pb.worker.Void:
139
+ """Stop the worker service and its thread.
140
+
141
+ Gracefully shuts down the worker thread and signals the server
142
+ to stop accepting new requests. This method is idempotent and
143
+ can be called multiple times safely.
144
+
145
+ :param request:
146
+ The protobuf stop request containing the wait timeout.
147
+ :param context:
148
+ The :class:`grpc.aio.ServicerContext` for this request.
149
+ :returns:
150
+ An empty protobuf response indicating completion.
151
+ """
152
+ if self._stopping.is_set():
153
+ return pb.worker.Void()
154
+ await self._stop(timeout=request.timeout)
155
+ return pb.worker.Void()
156
+
157
+ @contextmanager
158
+ def _tracker(self, wool_task: WoolTask):
159
+ """Context manager for tracking running tasks.
160
+
161
+ Manages the lifecycle of a task execution, adding it to the
162
+ active tasks set and emitting appropriate events. Ensures
163
+ proper cleanup when the task completes or fails.
164
+
165
+ :param wool_task:
166
+ The :class:`WoolTask` instance to execute and track.
167
+ :yields:
168
+ The :class:`asyncio.Task` created for the wool task.
169
+
170
+ .. note::
171
+ Emits a :class:`WoolTaskEvent` with type "task-scheduled"
172
+ when the task begins execution.
173
+ """
174
+ WoolTaskEvent("task-scheduled", task=wool_task).emit()
175
+ task = asyncio.create_task(wool_task.run())
176
+ self._tasks.add(task)
177
+ try:
178
+ yield task
179
+ finally:
180
+ self._tasks.remove(task)
181
+
182
+ async def _stop(self, *, timeout: float | None = 0) -> None:
183
+ self._stopping.set()
184
+ await self._await_or_cancel_tasks(timeout=timeout)
185
+ try:
186
+ if proxy_pool := wool.__proxy_pool__.get():
187
+ await proxy_pool.clear()
188
+ finally:
189
+ self._stopped.set()
190
+
191
+ async def _await_or_cancel_tasks(self, *, timeout: float | None = 0) -> None:
192
+ """Stop the worker service gracefully.
193
+
194
+ Gracefully shuts down the worker service by canceling or waiting
195
+ for running tasks. This method is idempotent and can be called
196
+ multiple times safely.
197
+
198
+ :param timeout:
199
+ Maximum time to wait for tasks to complete. If 0 (default),
200
+ tasks are canceled immediately. If None, waits indefinitely.
201
+ If a positive number, waits for that many seconds before
202
+ canceling tasks.
203
+
204
+ .. note::
205
+ If a timeout occurs while waiting for tasks to complete,
206
+ the method recursively calls itself with a timeout of 0
207
+ to cancel all remaining tasks immediately.
208
+ """
209
+ if self._tasks and timeout == 0:
210
+ await self._cancel(*self._tasks)
211
+ elif self._tasks:
212
+ try:
213
+ await asyncio.wait_for(
214
+ asyncio.gather(*self._tasks, return_exceptions=True),
215
+ timeout=timeout,
216
+ )
217
+ except asyncio.TimeoutError:
218
+ return await self._await_or_cancel_tasks(timeout=0)
219
+
220
+ async def _cancel(self, *tasks: asyncio.Task):
221
+ """Cancel multiple tasks safely.
222
+
223
+ Cancels the provided tasks while performing safety checks to
224
+ avoid canceling the current task or already completed tasks.
225
+ Waits for all cancelled tasks to complete in parallel and handles
226
+ cancellation exceptions.
227
+
228
+ :param tasks:
229
+ The :class:`asyncio.Task` instances to cancel.
230
+
231
+ .. note::
232
+ This method performs the following safety checks:
233
+ - Avoids canceling the current task (would cause deadlock)
234
+ - Only cancels tasks that are not already done
235
+ - Properly handles :exc:`asyncio.CancelledError`
236
+ exceptions.
237
+ """
238
+ current = asyncio.current_task()
239
+ to_cancel = [task for task in tasks if not task.done() and task != current]
240
+ for task in to_cancel:
241
+ task.cancel()
242
+ if to_cancel:
243
+ await asyncio.gather(*to_cancel, return_exceptions=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wool
3
- Version: 0.1rc13
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.2.0; extra == 'dev'
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
- # Wool
230
+ ![](https://raw.githubusercontent.com/wool-labs/wool/refs/heads/main/assets/woolly-transparent-bg-2048.png)
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
- ### CLI Commands
256
+ ### Declaring tasks
257
257
 
258
- Wool provides a command-line interface (CLI) for managing the worker pool.
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
- To list the available commands:
260
+ ```python
261
+ import wool
261
262
 
262
- ```sh
263
- wool --help
263
+ @wool.task
264
+ async def sample_task(x, y):
265
+ return x + y
264
266
  ```
265
267
 
266
- #### Start the Worker Pool
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
- To start the worker pool, use the `up` command:
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 to be executed by this pool (optional, can be specified multiple times).
279
-
280
- #### Stop the Worker Pool
332
+ - `--module`: Python module containing Wool task definitions (optional, can be specified multiple times).
281
333
 
282
- To stop the worker pool, use the `down` command:
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 Worker Pool
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
- ### Sample Python Application
355
+ ### Advanced usage
356
+
357
+ #### Nested pools and sessions
306
358
 
307
- Below is an example of how to create a Wool client connection, decorate an async function using the `task` decorator, and execute the function remotely:
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 sample_task(x, y):
365
+ async def task_a():
317
366
  await asyncio.sleep(1)
318
- return x + y
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
- Module executing remote workflow:
322
- `main.py`
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
- # Execute the decorated function in an external worker pool
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.PoolSession(port=5050, authkey=b"deadbeef"):
330
- result = await sample_task(1, 2)
331
- print(f"Result: {result}")
429
+ with wool.pool(port=5050):
430
+ await task_a()
431
+ await task_b()
332
432
 
333
- asyncio.new_event_loop().run_until_complete(main())
433
+ asyncio.run(main())
334
434
  ```
335
435
 
336
- To run the demo, first start a worker pool specifying the module defining the tasks to be executed:
337
- ```bash
338
- wool pool up --port 5050 --authkey deadbeef --breadth 1 --module tasks
339
- ```
436
+ #### Performance optimization
340
437
 
341
- Next, in a separate terminal, execute the application defined in `main.py` and, finally, stop the worker pool:
342
- ```bash
343
- python main.py
344
- wool pool down --port 5050 --authkey deadbeef
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
@@ -0,0 +1,27 @@
1
+ wool/__init__.py,sha256=I2tysRZBjPHMt8rVigYLx_R4wafXMIYq9A69_MPAPQA,2109
2
+ wool/_connection.py,sha256=EMF80cJla6nMeE6rSq7NL6-QpzjXCAjfRuXqE5O7Pjs,8434
3
+ wool/_context.py,sha256=bbMGt8if3Fq_YpQVszqA8dqTdIoRP-Q7u1MIQfsnP8c,931
4
+ wool/_loadbalancer.py,sha256=CbdbPgdC0tbIdcKT5KEaPlj2xsKjPxdcYeEGYE1AgyU,7280
5
+ wool/_resource_pool.py,sha256=qjPoKpDgg_bH6GpzEFkUSIuPJAU5XJCjIonWqQ42NIU,11550
6
+ wool/_typing.py,sha256=tZDbQN8DZqGf34fRfgnqITsCgAvXA02bgB_9uTaNACQ,191
7
+ wool/_undefined.py,sha256=NDZXOYCsUvDVKQ0HHMWzkJOY7ijNRD-pxxQQK-et4eY,181
8
+ wool/_work.py,sha256=Qa5XC4KypORSGAB-THkA_nc6iEUggMNWju0Ckr5u7ts,16759
9
+ wool/_worker.py,sha256=7IoCJDb7GjdI_xg3g_I2J0CXAdgzmG5n0bLmWGTETY0,20300
10
+ wool/_worker_discovery.py,sha256=qd5KPqhbCz7JQ6ohyfiJjS8HFmjCTOlCyP2NaaUReqg,42763
11
+ wool/_worker_pool.py,sha256=D3kpGGkrW-5uWTyyj3HhGTRstZptbOieO5TJbWBtQEI,11500
12
+ wool/_worker_proxy.py,sha256=O1IkWY7_pbcDM48cjAvydzQ5Z1lc5MEtgsed9Tg8M4c,12882
13
+ wool/_worker_service.py,sha256=t6Y7-xeZ1WIqxi97R18iBRhhpV520D8a6_8z_Y58CAk,8713
14
+ wool/_protobuf/__init__.py,sha256=61kDOGjPz9FwZpEygbItQdJY1y2QK-ogEtUtwvsOEZQ,435
15
+ wool/_protobuf/exception.py,sha256=rBm4bpWBt9Pq6Ry-WyxSG9RSE3FBuUt1JF-HBR0TK7w,174
16
+ wool/_protobuf/task.py,sha256=YTBKhUgUAXF-wEecEYmieN87l9f4aYPhkqrm7hyVNDU,384
17
+ wool/_protobuf/task_pb2.py,sha256=Fb7z2Ox-9ImXHwNKFYvxuBbMsrWFo9lsitEctzk4ARE,1945
18
+ wool/_protobuf/task_pb2.pyi,sha256=mssrgYgww2n_3J05OtwIAgYHzLSu__3ngXV8CjIKoiU,1593
19
+ wool/_protobuf/task_pb2_grpc.py,sha256=-be_fUpBt6g7xCvqVWnKZnzsYqYqu6IAk1wGV_1mWl8,884
20
+ wool/_protobuf/worker.py,sha256=gmB-wowcCig9a_EIZB8eLYmfCvF7aAu4Zm2sZLTVYi4,734
21
+ wool/_protobuf/worker_pb2.py,sha256=smrpC187d9NxzbEp1XlTMaUOvlqO5-r_JUKRptmmgN4,2396
22
+ wool/_protobuf/worker_pb2.pyi,sha256=YR0mHH4ojlTv6J5Kwsan8E3PU2BSwwN1870bNEAfX8s,1444
23
+ wool/_protobuf/worker_pb2_grpc.py,sha256=JXgPLxPnvcfrKaCEmE5989-v691F80mPUDNWr849CIg,4991
24
+ wool-0.1rc15.dist-info/METADATA,sha256=YMeV6dmrg59BRWkdyoflkhKuSsVSxsOIq0xpCvSuHrY,21021
25
+ wool-0.1rc15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ wool-0.1rc15.dist-info/entry_points.txt,sha256=U3V7wWNc3KLw4AOjJRpbcZcLJIdiA-7y1eqxn-Xa5rY,38
27
+ wool-0.1rc15.dist-info/RECORD,,
@@ -1,22 +0,0 @@
1
- wool/__init__.py,sha256=m0DUFtEqDqOY9Ct1FVapGqIHnftq6uDccgCSFCdM7w8,2598
2
- wool/_resource_pool.py,sha256=B4Zk9kbspMY6V-H6unzup666alhI81YMBCyGvW-R5Ho,11559
3
- wool/_typing.py,sha256=tZDbQN8DZqGf34fRfgnqITsCgAvXA02bgB_9uTaNACQ,191
4
- wool/_work.py,sha256=I4eyCEWDFkfoRXhBvNcKuA1fGS55JMw4WEZhwU0DExM,16690
5
- wool/_worker.py,sha256=W59hu44KQ45tn0H4gDZgAxpO65ah_Waqi7-NfCXzoZI,31238
6
- wool/_worker_discovery.py,sha256=FFrc8ZuJfBjyySwaAMYTzbelwrdbxs77u7VWupdFtj8,43371
7
- wool/_worker_pool.py,sha256=jfD5qXg78CfKEm-cPFpfFFzFJ6OF3_S8Je_8YEHycYg,10705
8
- wool/_worker_proxy.py,sha256=j8SaVM2yx4kEFq6l7EQox6L55NpMySYgj1ZkqR1FvgA,17325
9
- wool/_protobuf/__init__.py,sha256=61kDOGjPz9FwZpEygbItQdJY1y2QK-ogEtUtwvsOEZQ,435
10
- wool/_protobuf/exception.py,sha256=rBm4bpWBt9Pq6Ry-WyxSG9RSE3FBuUt1JF-HBR0TK7w,174
11
- wool/_protobuf/task.py,sha256=YTBKhUgUAXF-wEecEYmieN87l9f4aYPhkqrm7hyVNDU,384
12
- wool/_protobuf/task_pb2.py,sha256=Fb7z2Ox-9ImXHwNKFYvxuBbMsrWFo9lsitEctzk4ARE,1945
13
- wool/_protobuf/task_pb2.pyi,sha256=mssrgYgww2n_3J05OtwIAgYHzLSu__3ngXV8CjIKoiU,1593
14
- wool/_protobuf/task_pb2_grpc.py,sha256=84njmLo1mCquhxPVaRNeW6MNptH0CYClzbBvBorUnk4,885
15
- wool/_protobuf/worker.py,sha256=gmB-wowcCig9a_EIZB8eLYmfCvF7aAu4Zm2sZLTVYi4,734
16
- wool/_protobuf/worker_pb2.py,sha256=LiotSqTMtFdVmjG3svVsSYfDyuJdsf-KwHPtRDNeluo,2393
17
- wool/_protobuf/worker_pb2.pyi,sha256=TjLjxwAsr_NpWw1JLGUz77ANCPIwWoJJ5WmkX8CfnfU,1428
18
- wool/_protobuf/worker_pb2_grpc.py,sha256=XxGA7JLZ4B6uBgQD2uRZaGIsM4H118Z-CLBrBk5yPZ8,4992
19
- wool-0.1rc13.dist-info/METADATA,sha256=Am7W6xzSGPbWBxpRXsZ0XWc5ycIjYLYYNtZO1YlxEuQ,17093
20
- wool-0.1rc13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
- wool-0.1rc13.dist-info/entry_points.txt,sha256=U3V7wWNc3KLw4AOjJRpbcZcLJIdiA-7y1eqxn-Xa5rY,38
22
- wool-0.1rc13.dist-info/RECORD,,
File without changes