wool 0.1rc9__py3-none-any.whl → 0.1rc11__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.
- wool/__init__.py +71 -50
- wool/_protobuf/__init__.py +12 -5
- wool/_protobuf/exception.py +3 -0
- wool/_protobuf/task.py +11 -0
- wool/_protobuf/task_pb2.py +42 -0
- wool/_protobuf/task_pb2.pyi +43 -0
- wool/_protobuf/{mempool/metadata_pb2_grpc.py → task_pb2_grpc.py} +2 -2
- wool/_protobuf/worker.py +24 -0
- wool/_protobuf/worker_pb2.py +47 -0
- wool/_protobuf/worker_pb2.pyi +39 -0
- wool/_protobuf/worker_pb2_grpc.py +141 -0
- wool/_resource_pool.py +376 -0
- wool/_typing.py +0 -10
- wool/_work.py +553 -0
- wool/_worker.py +843 -169
- wool/_worker_discovery.py +1223 -0
- wool/_worker_pool.py +337 -0
- wool/_worker_proxy.py +515 -0
- {wool-0.1rc9.dist-info → wool-0.1rc11.dist-info}/METADATA +7 -7
- wool-0.1rc11.dist-info/RECORD +22 -0
- wool-0.1rc11.dist-info/entry_points.txt +2 -0
- wool/_cli.py +0 -262
- wool/_event.py +0 -109
- wool/_future.py +0 -171
- wool/_logging.py +0 -44
- wool/_manager.py +0 -181
- wool/_mempool/__init__.py +0 -4
- wool/_mempool/_client.py +0 -167
- wool/_mempool/_mempool.py +0 -311
- wool/_mempool/_metadata.py +0 -35
- wool/_mempool/_service.py +0 -227
- wool/_pool.py +0 -524
- wool/_protobuf/mempool/metadata_pb2.py +0 -36
- wool/_protobuf/mempool/metadata_pb2.pyi +0 -17
- wool/_protobuf/mempool/service_pb2.py +0 -66
- wool/_protobuf/mempool/service_pb2.pyi +0 -108
- wool/_protobuf/mempool/service_pb2_grpc.py +0 -355
- wool/_queue.py +0 -32
- wool/_session.py +0 -429
- wool/_task.py +0 -366
- wool/_utils.py +0 -63
- wool-0.1rc9.dist-info/RECORD +0 -29
- wool-0.1rc9.dist-info/entry_points.txt +0 -2
- {wool-0.1rc9.dist-info → wool-0.1rc11.dist-info}/WHEEL +0 -0
wool/_cli.py
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import importlib
|
|
3
|
-
import logging
|
|
4
|
-
from contextlib import contextmanager
|
|
5
|
-
from functools import partial
|
|
6
|
-
from multiprocessing import cpu_count
|
|
7
|
-
from time import perf_counter
|
|
8
|
-
|
|
9
|
-
import click
|
|
10
|
-
|
|
11
|
-
import wool
|
|
12
|
-
from wool._pool import WorkerPool
|
|
13
|
-
from wool._session import WorkerPoolSession
|
|
14
|
-
from wool._task import task
|
|
15
|
-
|
|
16
|
-
DEFAULT_PORT = 48800
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# PUBLIC
|
|
20
|
-
class WorkerPoolCommand(click.core.Command):
|
|
21
|
-
"""
|
|
22
|
-
Custom Click command class for worker pool commands.
|
|
23
|
-
|
|
24
|
-
:param default_host: Default host address.
|
|
25
|
-
:param default_port: Default port number.
|
|
26
|
-
:param default_authkey: Default authentication key.
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
def __init__(
|
|
30
|
-
self,
|
|
31
|
-
*args,
|
|
32
|
-
default_host="localhost",
|
|
33
|
-
default_port=0,
|
|
34
|
-
default_authkey=b"",
|
|
35
|
-
**kwargs,
|
|
36
|
-
):
|
|
37
|
-
params = kwargs.pop("params", [])
|
|
38
|
-
params = [
|
|
39
|
-
click.Option(["--host", "-h"], type=str, default=default_host),
|
|
40
|
-
click.Option(["--port", "-p"], type=int, default=default_port),
|
|
41
|
-
click.Option(
|
|
42
|
-
["--authkey", "-a"],
|
|
43
|
-
type=str,
|
|
44
|
-
default=default_authkey,
|
|
45
|
-
callback=to_bytes,
|
|
46
|
-
),
|
|
47
|
-
*params,
|
|
48
|
-
]
|
|
49
|
-
super().__init__(*args, params=params, **kwargs)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
@contextmanager
|
|
53
|
-
def timer():
|
|
54
|
-
"""
|
|
55
|
-
Context manager to measure the execution time of a code block.
|
|
56
|
-
|
|
57
|
-
:return: A function to retrieve the elapsed time.
|
|
58
|
-
"""
|
|
59
|
-
start = end = perf_counter()
|
|
60
|
-
yield lambda: end - start
|
|
61
|
-
end = perf_counter()
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def to_bytes(context: click.Context, parameter: click.Parameter, value: str):
|
|
65
|
-
"""
|
|
66
|
-
Convert the given value to bytes.
|
|
67
|
-
|
|
68
|
-
:param context: Click context.
|
|
69
|
-
:param parameter: Click parameter.
|
|
70
|
-
:param value: Value to convert.
|
|
71
|
-
:return: The converted value in bytes.
|
|
72
|
-
"""
|
|
73
|
-
if value is None:
|
|
74
|
-
return b""
|
|
75
|
-
return value.encode("utf-8")
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def assert_nonzero(
|
|
79
|
-
context: click.Context, parameter: click.Parameter, value: int
|
|
80
|
-
) -> int:
|
|
81
|
-
"""
|
|
82
|
-
Assert that the given value is non-zero.
|
|
83
|
-
|
|
84
|
-
:param context: Click context.
|
|
85
|
-
:param parameter: Click parameter.
|
|
86
|
-
:param value: Value to check.
|
|
87
|
-
:return: The original value if it is non-zero.
|
|
88
|
-
"""
|
|
89
|
-
if value is None:
|
|
90
|
-
return value
|
|
91
|
-
assert value >= 0
|
|
92
|
-
return value
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def debug(ctx, param, value):
|
|
96
|
-
"""
|
|
97
|
-
Enable debugging mode with a specified port.
|
|
98
|
-
|
|
99
|
-
:param ctx: The Click context object.
|
|
100
|
-
:param param: The parameter being handled.
|
|
101
|
-
:param value: The port number for the debugger.
|
|
102
|
-
"""
|
|
103
|
-
if not value or ctx.resilient_parsing:
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
import debugpy
|
|
107
|
-
|
|
108
|
-
debugpy.listen(5678)
|
|
109
|
-
click.echo("Waiting for debugger to attach...")
|
|
110
|
-
debugpy.wait_for_client()
|
|
111
|
-
click.echo("Debugger attached")
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@click.group()
|
|
115
|
-
@click.option(
|
|
116
|
-
"--debug",
|
|
117
|
-
"-d",
|
|
118
|
-
callback=debug,
|
|
119
|
-
expose_value=False,
|
|
120
|
-
help=(
|
|
121
|
-
"Run with debugger listening on the specified port. Execution will "
|
|
122
|
-
"block until the debugger is attached."
|
|
123
|
-
),
|
|
124
|
-
is_eager=True,
|
|
125
|
-
type=int,
|
|
126
|
-
)
|
|
127
|
-
@click.option(
|
|
128
|
-
"--verbosity",
|
|
129
|
-
"-v",
|
|
130
|
-
count=True,
|
|
131
|
-
default=3,
|
|
132
|
-
help="Verbosity level for logging.",
|
|
133
|
-
type=int,
|
|
134
|
-
)
|
|
135
|
-
def cli(verbosity: int):
|
|
136
|
-
"""
|
|
137
|
-
CLI command group with options for verbosity, debugging, and version.
|
|
138
|
-
|
|
139
|
-
:param verbosity: Verbosity level for logging.
|
|
140
|
-
"""
|
|
141
|
-
match verbosity:
|
|
142
|
-
case 4:
|
|
143
|
-
wool.__log_level__ = logging.DEBUG
|
|
144
|
-
case 3:
|
|
145
|
-
wool.__log_level__ = logging.INFO
|
|
146
|
-
case 2:
|
|
147
|
-
wool.__log_level__ = logging.WARNING
|
|
148
|
-
case 1:
|
|
149
|
-
wool.__log_level__ = logging.ERROR
|
|
150
|
-
|
|
151
|
-
logging.getLogger().setLevel(wool.__log_level__)
|
|
152
|
-
logging.info(
|
|
153
|
-
f"Set log level to {logging.getLevelName(wool.__log_level__)}"
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
@cli.group()
|
|
158
|
-
def pool():
|
|
159
|
-
"""
|
|
160
|
-
CLI command group for managing worker pools.
|
|
161
|
-
"""
|
|
162
|
-
pass
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
@pool.command(cls=partial(WorkerPoolCommand, default_port=DEFAULT_PORT))
|
|
166
|
-
@click.option(
|
|
167
|
-
"--breadth", "-b", type=int, default=cpu_count(), callback=assert_nonzero
|
|
168
|
-
)
|
|
169
|
-
@click.option(
|
|
170
|
-
"modules",
|
|
171
|
-
"--module",
|
|
172
|
-
"-m",
|
|
173
|
-
multiple=True,
|
|
174
|
-
type=str,
|
|
175
|
-
help=(
|
|
176
|
-
"Python module containing workerpool task definitions to be executed "
|
|
177
|
-
"by this pool."
|
|
178
|
-
),
|
|
179
|
-
)
|
|
180
|
-
def up(host, port, authkey, breadth, modules):
|
|
181
|
-
"""
|
|
182
|
-
Start a worker pool with the specified configuration.
|
|
183
|
-
|
|
184
|
-
:param host: The host address for the worker pool.
|
|
185
|
-
:param port: The port number for the worker pool.
|
|
186
|
-
:param authkey: The authentication key for the worker pool.
|
|
187
|
-
:param breadth: The number of worker processes in the pool.
|
|
188
|
-
:param modules: Python modules containing task definitions.
|
|
189
|
-
"""
|
|
190
|
-
for module in modules:
|
|
191
|
-
importlib.import_module(module)
|
|
192
|
-
if not authkey:
|
|
193
|
-
logging.warning("No authkey specified")
|
|
194
|
-
workerpool = WorkerPool(
|
|
195
|
-
address=(host, port),
|
|
196
|
-
breadth=breadth,
|
|
197
|
-
authkey=authkey,
|
|
198
|
-
log_level=wool.__log_level__,
|
|
199
|
-
)
|
|
200
|
-
workerpool.start()
|
|
201
|
-
workerpool.join()
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
@pool.command(cls=partial(WorkerPoolCommand, default_port=DEFAULT_PORT))
|
|
205
|
-
@click.option(
|
|
206
|
-
"--wait",
|
|
207
|
-
"-w",
|
|
208
|
-
is_flag=True,
|
|
209
|
-
default=False,
|
|
210
|
-
help="Wait for in-flight tasks to complete before shutting down.",
|
|
211
|
-
)
|
|
212
|
-
def down(host, port, authkey, wait):
|
|
213
|
-
"""
|
|
214
|
-
Shut down the worker pool.
|
|
215
|
-
|
|
216
|
-
:param host: The host address of the worker pool.
|
|
217
|
-
:param port: The port number of the worker pool.
|
|
218
|
-
:param authkey: The authentication key for the worker pool.
|
|
219
|
-
:param wait: Whether to wait for in-flight tasks to complete.
|
|
220
|
-
"""
|
|
221
|
-
assert port
|
|
222
|
-
if not host:
|
|
223
|
-
host = "localhost"
|
|
224
|
-
if not authkey:
|
|
225
|
-
authkey = b""
|
|
226
|
-
with WorkerPoolSession(address=(host, port), authkey=authkey) as client:
|
|
227
|
-
client.stop(wait=wait)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
@cli.command(cls=partial(WorkerPoolCommand, default_port=DEFAULT_PORT))
|
|
231
|
-
def ping(host, port, authkey):
|
|
232
|
-
"""
|
|
233
|
-
Ping the worker pool to check connectivity.
|
|
234
|
-
|
|
235
|
-
:param host: The host address of the worker pool.
|
|
236
|
-
:param port: The port number of the worker pool.
|
|
237
|
-
:param authkey: The authentication key for the worker pool.
|
|
238
|
-
"""
|
|
239
|
-
assert port
|
|
240
|
-
if not host:
|
|
241
|
-
host = "localhost"
|
|
242
|
-
if not authkey:
|
|
243
|
-
authkey = b""
|
|
244
|
-
|
|
245
|
-
async def _():
|
|
246
|
-
with timer() as t:
|
|
247
|
-
await _ping()
|
|
248
|
-
|
|
249
|
-
print(f"Ping: {int((t() * 1000) + 0.5)} ms")
|
|
250
|
-
|
|
251
|
-
with WorkerPoolSession(address=(host, port), authkey=authkey):
|
|
252
|
-
asyncio.get_event_loop().run_until_complete(_())
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
@task
|
|
256
|
-
async def _ping():
|
|
257
|
-
"""
|
|
258
|
-
Asynchronous task to log a ping message.
|
|
259
|
-
|
|
260
|
-
:return: None
|
|
261
|
-
"""
|
|
262
|
-
logging.debug("Ping!")
|
wool/_event.py
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from time import perf_counter_ns
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
6
|
-
from typing import Literal
|
|
7
|
-
|
|
8
|
-
from wool._typing import PassthroughDecorator
|
|
9
|
-
|
|
10
|
-
if TYPE_CHECKING:
|
|
11
|
-
from wool._task import Task
|
|
12
|
-
from wool._task import TaskEventCallback
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# PUBLIC
|
|
16
|
-
class TaskEvent:
|
|
17
|
-
"""
|
|
18
|
-
Represents an event related to a Wool task, such as creation, queuing,
|
|
19
|
-
scheduling, starting, stopping, or completion.
|
|
20
|
-
|
|
21
|
-
Task events are emitted during the lifecycle of a task. These events can
|
|
22
|
-
be used to track task execution and measure performance, such as CPU
|
|
23
|
-
utilization.
|
|
24
|
-
|
|
25
|
-
:param type: The type of the task event.
|
|
26
|
-
:param task: The task associated with the event.
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
type: TaskEventType
|
|
30
|
-
task: Task
|
|
31
|
-
|
|
32
|
-
_handlers: dict[str, list[TaskEventCallback]] = {}
|
|
33
|
-
|
|
34
|
-
def __init__(self, type: TaskEventType, /, task: Task) -> None:
|
|
35
|
-
"""
|
|
36
|
-
Initialize a WoolTaskEvent instance.
|
|
37
|
-
|
|
38
|
-
:param type: The type of the task event.
|
|
39
|
-
:param task: The task associated with the event.
|
|
40
|
-
"""
|
|
41
|
-
self.type = type
|
|
42
|
-
self.task = task
|
|
43
|
-
|
|
44
|
-
@classmethod
|
|
45
|
-
def handler(
|
|
46
|
-
cls, *event_types: TaskEventType
|
|
47
|
-
) -> PassthroughDecorator[TaskEventCallback]:
|
|
48
|
-
"""
|
|
49
|
-
Register a handler function for specific task event types.
|
|
50
|
-
|
|
51
|
-
:param event_types: The event types to handle.
|
|
52
|
-
:return: A decorator to register the handler function.
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
def _handler(
|
|
56
|
-
fn: TaskEventCallback,
|
|
57
|
-
) -> TaskEventCallback:
|
|
58
|
-
for event_type in event_types:
|
|
59
|
-
cls._handlers.setdefault(event_type, []).append(fn)
|
|
60
|
-
return fn
|
|
61
|
-
|
|
62
|
-
return _handler
|
|
63
|
-
|
|
64
|
-
def emit(self):
|
|
65
|
-
"""
|
|
66
|
-
Emit the task event, invoking all registered handlers for the event
|
|
67
|
-
type.
|
|
68
|
-
|
|
69
|
-
Handlers are called with the event instance and a timestamp.
|
|
70
|
-
|
|
71
|
-
:raises Exception: If any handler raises an exception.
|
|
72
|
-
"""
|
|
73
|
-
logging.debug(
|
|
74
|
-
f"Emitting {self.type} event for "
|
|
75
|
-
f"task {self.task.id} "
|
|
76
|
-
f"({self.task.callable.__qualname__})"
|
|
77
|
-
)
|
|
78
|
-
if handlers := self._handlers.get(self.type):
|
|
79
|
-
timestamp = perf_counter_ns()
|
|
80
|
-
for handler in handlers:
|
|
81
|
-
handler(self, timestamp)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
# PUBLIC
|
|
85
|
-
TaskEventType = Literal[
|
|
86
|
-
"task-created",
|
|
87
|
-
"task-queued",
|
|
88
|
-
"task-scheduled",
|
|
89
|
-
"task-started",
|
|
90
|
-
"task-stopped",
|
|
91
|
-
"task-completed",
|
|
92
|
-
]
|
|
93
|
-
"""
|
|
94
|
-
Defines the types of events that can occur during the lifecycle of a Wool
|
|
95
|
-
task.
|
|
96
|
-
|
|
97
|
-
- "task-created":
|
|
98
|
-
Emitted when a task is created.
|
|
99
|
-
- "task-queued":
|
|
100
|
-
Emitted when a task is added to the queue.
|
|
101
|
-
- "task-scheduled":
|
|
102
|
-
Emitted when a task is scheduled for execution in a worker's event loop.
|
|
103
|
-
- "task-started":
|
|
104
|
-
Emitted when a task starts execution.
|
|
105
|
-
- "task-stopped":
|
|
106
|
-
Emitted when a task stops execution.
|
|
107
|
-
- "task-completed":
|
|
108
|
-
Emitted when a task completes execution.
|
|
109
|
-
"""
|
wool/_future.py
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import concurrent.futures
|
|
5
|
-
import logging
|
|
6
|
-
from typing import Any
|
|
7
|
-
from typing import Generator
|
|
8
|
-
from typing import Generic
|
|
9
|
-
from typing import TypeVar
|
|
10
|
-
from typing import cast
|
|
11
|
-
|
|
12
|
-
from wool._utils import Undefined
|
|
13
|
-
from wool._utils import UndefinedType
|
|
14
|
-
|
|
15
|
-
T = TypeVar("T")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# PUBLIC
|
|
19
|
-
class Future(Generic[T]):
|
|
20
|
-
"""
|
|
21
|
-
A future object representing the result of an asynchronous operation.
|
|
22
|
-
|
|
23
|
-
WoolFuture provides methods to retrieve the result or exception of an
|
|
24
|
-
asynchronous operation, set the result or exception, and await its
|
|
25
|
-
completion.
|
|
26
|
-
|
|
27
|
-
:param T: The type of the result.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
def __init__(self) -> None:
|
|
31
|
-
"""
|
|
32
|
-
Initialize a WoolFuture instance.
|
|
33
|
-
"""
|
|
34
|
-
self._result: T | UndefinedType = Undefined
|
|
35
|
-
self._exception: (
|
|
36
|
-
BaseException | type[BaseException] | UndefinedType
|
|
37
|
-
) = Undefined
|
|
38
|
-
self._done: bool = False
|
|
39
|
-
self._cancelled: bool = False
|
|
40
|
-
|
|
41
|
-
def __await__(self) -> Generator[Any, None, T]:
|
|
42
|
-
"""
|
|
43
|
-
Await the completion of the future.
|
|
44
|
-
|
|
45
|
-
:return: The result of the future.
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
async def _():
|
|
49
|
-
while not self.done():
|
|
50
|
-
await asyncio.sleep(0)
|
|
51
|
-
else:
|
|
52
|
-
return self.result()
|
|
53
|
-
|
|
54
|
-
return _().__await__()
|
|
55
|
-
|
|
56
|
-
def result(self) -> T:
|
|
57
|
-
"""
|
|
58
|
-
Retrieve the result of the future.
|
|
59
|
-
|
|
60
|
-
:return: The result of the future.
|
|
61
|
-
:raises BaseException: If the future completed with an exception.
|
|
62
|
-
:raises asyncio.InvalidStateError: If the future is not yet completed.
|
|
63
|
-
"""
|
|
64
|
-
if self._exception is not Undefined:
|
|
65
|
-
assert (
|
|
66
|
-
isinstance(self._exception, BaseException)
|
|
67
|
-
or isinstance(self._exception, type)
|
|
68
|
-
and issubclass(self._exception, BaseException)
|
|
69
|
-
)
|
|
70
|
-
raise self._exception
|
|
71
|
-
elif self._result is not Undefined:
|
|
72
|
-
return cast(T, self._result)
|
|
73
|
-
else:
|
|
74
|
-
raise asyncio.InvalidStateError
|
|
75
|
-
|
|
76
|
-
def set_result(self, result: T) -> None:
|
|
77
|
-
"""
|
|
78
|
-
Set the result of the future.
|
|
79
|
-
|
|
80
|
-
:param result: The result to set.
|
|
81
|
-
:raises asyncio.InvalidStateError: If the future is already completed.
|
|
82
|
-
"""
|
|
83
|
-
if self.done():
|
|
84
|
-
raise asyncio.InvalidStateError
|
|
85
|
-
else:
|
|
86
|
-
self._result = result
|
|
87
|
-
self._done = True
|
|
88
|
-
|
|
89
|
-
def exception(self) -> BaseException | type[BaseException]:
|
|
90
|
-
"""
|
|
91
|
-
Retrieve the exception of the future, if any.
|
|
92
|
-
|
|
93
|
-
:return: The exception of the future.
|
|
94
|
-
:raises asyncio.InvalidStateError: If the future is not yet completed
|
|
95
|
-
or has no exception.
|
|
96
|
-
"""
|
|
97
|
-
if self.done() and self._exception is not Undefined:
|
|
98
|
-
return cast(BaseException | type[BaseException], self._exception)
|
|
99
|
-
else:
|
|
100
|
-
raise asyncio.InvalidStateError
|
|
101
|
-
|
|
102
|
-
def set_exception(
|
|
103
|
-
self, exception: BaseException | type[BaseException]
|
|
104
|
-
) -> None:
|
|
105
|
-
"""
|
|
106
|
-
Set the exception of the future.
|
|
107
|
-
|
|
108
|
-
:param exception: The exception to set.
|
|
109
|
-
:raises asyncio.InvalidStateError: If the future is already completed.
|
|
110
|
-
"""
|
|
111
|
-
if self.done():
|
|
112
|
-
raise asyncio.InvalidStateError
|
|
113
|
-
else:
|
|
114
|
-
self._exception = exception
|
|
115
|
-
self._done = True
|
|
116
|
-
|
|
117
|
-
def done(self) -> bool:
|
|
118
|
-
"""
|
|
119
|
-
Check if the future is completed.
|
|
120
|
-
|
|
121
|
-
:return: True if the future is completed, False otherwise.
|
|
122
|
-
"""
|
|
123
|
-
return self._done
|
|
124
|
-
|
|
125
|
-
def cancel(self) -> None:
|
|
126
|
-
"""
|
|
127
|
-
Cancel the future.
|
|
128
|
-
|
|
129
|
-
:raises asyncio.InvalidStateError: If the future is already completed.
|
|
130
|
-
"""
|
|
131
|
-
if self.done():
|
|
132
|
-
raise asyncio.InvalidStateError
|
|
133
|
-
else:
|
|
134
|
-
self._cancelled = True
|
|
135
|
-
self._done = True
|
|
136
|
-
|
|
137
|
-
def cancelled(self) -> bool:
|
|
138
|
-
"""
|
|
139
|
-
Check if the future was cancelled.
|
|
140
|
-
|
|
141
|
-
:return: True if the future was cancelled, False otherwise.
|
|
142
|
-
"""
|
|
143
|
-
return self._cancelled
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
async def poll(future: Future, task: concurrent.futures.Future) -> None:
|
|
147
|
-
while True:
|
|
148
|
-
if future.cancelled():
|
|
149
|
-
task.cancel()
|
|
150
|
-
return
|
|
151
|
-
elif future.done():
|
|
152
|
-
return
|
|
153
|
-
else:
|
|
154
|
-
await asyncio.sleep(0)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def fulfill(future: Future):
|
|
158
|
-
def callback(task: concurrent.futures.Future):
|
|
159
|
-
try:
|
|
160
|
-
result = task.result()
|
|
161
|
-
except concurrent.futures.CancelledError:
|
|
162
|
-
if not future.done():
|
|
163
|
-
future.cancel()
|
|
164
|
-
except BaseException as e:
|
|
165
|
-
logging.exception(e)
|
|
166
|
-
future.set_exception(e)
|
|
167
|
-
else:
|
|
168
|
-
if not future.done():
|
|
169
|
-
future.set_result(result)
|
|
170
|
-
|
|
171
|
-
return callback
|
wool/_logging.py
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def grey(text: str) -> str:
|
|
6
|
-
return f"\x1b[90m{text}\x1b[0m"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def italic(text: str) -> str:
|
|
10
|
-
return f"\x1b[3m{text}\x1b[0m"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class WoolLogFilter(logging.Filter):
|
|
14
|
-
def filter(self, record: logging.LogRecord) -> bool:
|
|
15
|
-
"""
|
|
16
|
-
Modify the log record to include a reference to the source file and
|
|
17
|
-
line number.
|
|
18
|
-
|
|
19
|
-
:param record: The log record to modify.
|
|
20
|
-
:return: True to indicate the record should be logged.
|
|
21
|
-
"""
|
|
22
|
-
pathname: str = record.pathname
|
|
23
|
-
cwd: str = os.getcwd()
|
|
24
|
-
if pathname.startswith(cwd):
|
|
25
|
-
record.ref = f"{os.path.relpath(pathname, cwd)}:{record.lineno}"
|
|
26
|
-
else:
|
|
27
|
-
record.ref = f".../{os.path.basename(pathname)}:{record.lineno}"
|
|
28
|
-
return True
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
__log_format__: str = (
|
|
32
|
-
f"{grey(italic('pid:'))}%(process)-8d "
|
|
33
|
-
f"{grey(italic('process:'))}%(processName)-12s "
|
|
34
|
-
f"{grey(italic('thread:'))}%(threadName)-20s "
|
|
35
|
-
"%(levelname)12s %(message)-60s "
|
|
36
|
-
f"{grey(italic('%(ref)s'))}"
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
formatter: logging.Formatter = logging.Formatter(__log_format__)
|
|
40
|
-
|
|
41
|
-
handler: logging.StreamHandler = logging.StreamHandler()
|
|
42
|
-
handler.setFormatter(formatter)
|
|
43
|
-
handler.addFilter(WoolLogFilter())
|
|
44
|
-
logging.getLogger().addHandler(handler)
|