ask-shell 0.5.3__tar.gz → 0.7.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.
- {ask_shell-0.5.3 → ask_shell-0.7.0}/PKG-INFO +1 -1
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/__init__.py +3 -1
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/_run.py +4 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/models.py +3 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/rich_progress.py +7 -1
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/rich_run_state.py +6 -0
- ask_shell-0.7.0/ask_shell/_internal/run_pool.py +121 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/settings.py +12 -1
- {ask_shell-0.5.3 → ask_shell-0.7.0}/pyproject.toml +1 -1
- ask_shell-0.5.3/ask_shell/_internal/run_pool.py +0 -100
- {ask_shell-0.5.3 → ask_shell-0.7.0}/.gitignore +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/LICENSE +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/README.md +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/__main__.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/__init__.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/_run_env.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/events.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/global_callbacks.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/interactive.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/rich_live.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/rich_live_callback.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/_internal/typer_command.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/ask.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/console.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/py.typed +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/shell.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/shell_events.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/ask_shell/test_docs.py +0 -0
- {ask_shell-0.5.3 → ask_shell-0.7.0}/scripts/fix_source_links.py +0 -0
|
@@ -5,12 +5,14 @@ from ask_shell import console
|
|
|
5
5
|
from ask_shell import shell_events
|
|
6
6
|
from ask_shell import ask
|
|
7
7
|
from ask_shell.settings import AskShellSettings
|
|
8
|
+
from ask_shell.settings import ShellRunSummary
|
|
8
9
|
|
|
9
|
-
VERSION = "0.
|
|
10
|
+
VERSION = "0.7.0"
|
|
10
11
|
__all__ = [
|
|
11
12
|
"shell",
|
|
12
13
|
"console",
|
|
13
14
|
"shell_events",
|
|
14
15
|
"ask",
|
|
15
16
|
"AskShellSettings",
|
|
17
|
+
"ShellRunSummary",
|
|
16
18
|
]
|
|
@@ -484,6 +484,7 @@ def run(
|
|
|
484
484
|
start_timeout: float | None = None,
|
|
485
485
|
terminal_width: int | None = None,
|
|
486
486
|
skip_interactive_check: bool | None = None,
|
|
487
|
+
mute_shell_summary: bool | None = None,
|
|
487
488
|
) -> ShellRun:
|
|
488
489
|
config = _as_config(
|
|
489
490
|
config,
|
|
@@ -510,6 +511,7 @@ def run(
|
|
|
510
511
|
skip_os_env=skip_os_env,
|
|
511
512
|
terminal_width=terminal_width,
|
|
512
513
|
skip_interactive_check=skip_interactive_check,
|
|
514
|
+
mute_shell_summary=mute_shell_summary,
|
|
513
515
|
)
|
|
514
516
|
assert not config.user_input, (
|
|
515
517
|
"run() does not support user_input (only 1 should be active at a time), use run_and_wait() instead"
|
|
@@ -549,6 +551,7 @@ def run_and_wait(
|
|
|
549
551
|
user_input: bool | None = None,
|
|
550
552
|
terminal_width: int | None = None,
|
|
551
553
|
skip_interactive_check: bool | None = None,
|
|
554
|
+
mute_shell_summary: bool | None = None,
|
|
552
555
|
) -> ShellRun:
|
|
553
556
|
config = _as_config(
|
|
554
557
|
script,
|
|
@@ -576,6 +579,7 @@ def run_and_wait(
|
|
|
576
579
|
terminal_width=terminal_width,
|
|
577
580
|
skip_interactive_check=skip_interactive_check,
|
|
578
581
|
skip_progress_output=skip_progress_output,
|
|
582
|
+
mute_shell_summary=mute_shell_summary,
|
|
579
583
|
)
|
|
580
584
|
run = ShellRun(config)
|
|
581
585
|
future = _pool.submit(_execute_run, run)
|
|
@@ -172,6 +172,9 @@ class ShellConfig(Entity):
|
|
|
172
172
|
default=False,
|
|
173
173
|
description="Skip transitive std out/err output, useful for large outputs that are not needed in the logs when running parallel scripts",
|
|
174
174
|
)
|
|
175
|
+
mute_shell_summary: bool = Field(
|
|
176
|
+
default=False, description="Skip shell summary logging useful when you want to ✅ or ❌ in the logs"
|
|
177
|
+
)
|
|
175
178
|
terminal_width: int | None = 999
|
|
176
179
|
|
|
177
180
|
# advanced settings
|
|
@@ -47,10 +47,16 @@ def log_task_done(
|
|
|
47
47
|
error: BaseException | None = None,
|
|
48
48
|
description_override: str | None = None,
|
|
49
49
|
extra_parts: list[str] | None = None,
|
|
50
|
+
soft_failure: bool = False,
|
|
50
51
|
):
|
|
51
52
|
exit_comji = "❌" if force_error or error is not None else "✅"
|
|
52
53
|
description = description_override or task.description
|
|
53
|
-
|
|
54
|
+
if exit_comji == "✅":
|
|
55
|
+
log_call = logger.info
|
|
56
|
+
elif soft_failure:
|
|
57
|
+
log_call = logger.warning
|
|
58
|
+
else:
|
|
59
|
+
log_call = logger.error
|
|
54
60
|
message_parts = [f"{exit_comji} {description}"]
|
|
55
61
|
if rich_task := task._rich_task:
|
|
56
62
|
if finish_time := rich_task.finished_time:
|
|
@@ -21,6 +21,7 @@ from ask_shell._internal.models import (
|
|
|
21
21
|
ShellRunEventT,
|
|
22
22
|
)
|
|
23
23
|
from ask_shell._internal.rich_progress import ProgressManager, log_task_done, new_task
|
|
24
|
+
from ask_shell.settings import ShellRunSummary
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def _deque_default() -> deque[str]:
|
|
@@ -144,6 +145,10 @@ class _RunState:
|
|
|
144
145
|
task.update(stderr=run_info.stderr_str)
|
|
145
146
|
task.__exit__(None, None, None)
|
|
146
147
|
run = run_info.run
|
|
148
|
+
if run.config.mute_shell_summary:
|
|
149
|
+
return
|
|
150
|
+
if run.config.settings.shell_run_summary == ShellRunSummary.ERRORS_ONLY and run.clean_complete:
|
|
151
|
+
return
|
|
147
152
|
log_task_done(
|
|
148
153
|
task,
|
|
149
154
|
force_error=not run.clean_complete,
|
|
@@ -151,4 +156,5 @@ class _RunState:
|
|
|
151
156
|
extra_parts=[
|
|
152
157
|
"" if run.current_attempt == 1 else f"attempt {run.current_attempt}",
|
|
153
158
|
],
|
|
159
|
+
soft_failure=run.config.allow_non_zero_exit and not run.clean_complete,
|
|
154
160
|
)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from concurrent.futures import Future, ThreadPoolExecutor
|
|
4
|
+
from concurrent.futures import wait as futures_wait
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from math import ceil
|
|
7
|
+
from threading import RLock
|
|
8
|
+
from typing import Any, Callable, Protocol, TypeVar
|
|
9
|
+
|
|
10
|
+
from ask_shell._internal._run import (
|
|
11
|
+
THREADS_PER_RUN,
|
|
12
|
+
get_pool,
|
|
13
|
+
handle_interrupt_wait,
|
|
14
|
+
max_run_count_for_workers,
|
|
15
|
+
wait_if_many_runs,
|
|
16
|
+
)
|
|
17
|
+
from ask_shell._internal.rich_progress import new_task
|
|
18
|
+
from ask_shell.settings import AskShellSettings
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
T_co = TypeVar("T_co", covariant=True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SubmitFunc(Protocol[T_co]):
|
|
25
|
+
def __call__(self, *args: Any, **kwargs: Any) -> T_co: ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class run_pool:
|
|
30
|
+
task_name: str
|
|
31
|
+
total: int = 0
|
|
32
|
+
max_concurrent_submits: int = field(default=4)
|
|
33
|
+
threads_used_per_submit: int = (
|
|
34
|
+
THREADS_PER_RUN + 1
|
|
35
|
+
) # If you are using `run` or `run_and_wait` this should be `THREADS_PER_RUN` + extra threads for your own tasks
|
|
36
|
+
pool_thread_count: int | None = None
|
|
37
|
+
sleep_time: float = 1
|
|
38
|
+
sleep_callback: Callable[[], Any] | None = None
|
|
39
|
+
exit_wait_timeout: float | None = (
|
|
40
|
+
None # If set, will wait for the pool to finish before exiting the context manager
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
pool: ThreadPoolExecutor = field(init=False)
|
|
44
|
+
_owns_pool: bool = field(init=False, default=False)
|
|
45
|
+
_pool_max_workers: int = field(init=False)
|
|
46
|
+
_max_run_count_with_this_pool: int = field(init=False)
|
|
47
|
+
_lock: RLock = field(init=False, default_factory=RLock)
|
|
48
|
+
_pending_count: int = field(init=False, default=0)
|
|
49
|
+
_task: new_task | None = field(init=False, default=None)
|
|
50
|
+
_futures: list[Future] = field(init=False, default_factory=list)
|
|
51
|
+
|
|
52
|
+
def __post_init__(self):
|
|
53
|
+
if self.pool_thread_count is not None:
|
|
54
|
+
self.pool = ThreadPoolExecutor(max_workers=self.pool_thread_count)
|
|
55
|
+
self._owns_pool = True
|
|
56
|
+
self._pool_max_workers = self.pool_thread_count
|
|
57
|
+
# dedicated pool: each concurrent submit reserves 1 run slot on the global pool
|
|
58
|
+
runs_needed = self.max_concurrent_submits
|
|
59
|
+
runs_available = max_run_count_for_workers()
|
|
60
|
+
logger.debug(
|
|
61
|
+
f"run_pool '{self.task_name}': dedicated pool with {self.pool_thread_count} workers, "
|
|
62
|
+
f"global pool reserves {runs_needed}/{runs_available} run slots"
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
self.pool = get_pool()
|
|
66
|
+
self._pool_max_workers = self.pool._max_workers
|
|
67
|
+
# shared pool: submits + their shell runs share the same threads
|
|
68
|
+
workers_at_full_load = self.max_concurrent_submits * self.threads_used_per_submit
|
|
69
|
+
runs_needed = ceil(workers_at_full_load / THREADS_PER_RUN)
|
|
70
|
+
runs_available = max_run_count_for_workers(self._pool_max_workers)
|
|
71
|
+
|
|
72
|
+
assert runs_needed < runs_available, (
|
|
73
|
+
f"Run slots needed ({runs_needed}) exceed capacity ({runs_available}). "
|
|
74
|
+
f"Adjust {AskShellSettings.ENV_NAME_THREAD_COUNT} or decrease `max_concurrent_submits`."
|
|
75
|
+
)
|
|
76
|
+
self._max_run_count_with_this_pool = runs_available - runs_needed
|
|
77
|
+
|
|
78
|
+
def _on_submit_done(self, _future: Future):
|
|
79
|
+
with self._lock:
|
|
80
|
+
self._pending_count -= 1
|
|
81
|
+
if task := self._task:
|
|
82
|
+
task.update(advance=1)
|
|
83
|
+
|
|
84
|
+
def submit(self, fn: SubmitFunc[T_co], /, *args, **kwargs) -> Future[T_co]:
|
|
85
|
+
"""Submit a task to the pool. Blocks if max_concurrent_submits are already in flight."""
|
|
86
|
+
with self._lock:
|
|
87
|
+
self._pending_count += 1
|
|
88
|
+
with handle_interrupt_wait(interrupt_message=f"run_pool submit for {self.task_name}"):
|
|
89
|
+
while self._pending_count > self.max_concurrent_submits:
|
|
90
|
+
if self.sleep_callback:
|
|
91
|
+
self.sleep_callback()
|
|
92
|
+
time.sleep(self.sleep_time)
|
|
93
|
+
wait_if_many_runs(
|
|
94
|
+
max_run_count=self._max_run_count_with_this_pool,
|
|
95
|
+
sleep_time=self.sleep_time,
|
|
96
|
+
sleep_callback=self.sleep_callback,
|
|
97
|
+
)
|
|
98
|
+
future = self.pool.submit(fn, *args, **kwargs)
|
|
99
|
+
future.add_done_callback(self._on_submit_done)
|
|
100
|
+
with self._lock:
|
|
101
|
+
self._futures.append(future)
|
|
102
|
+
return future
|
|
103
|
+
|
|
104
|
+
def __enter__(self):
|
|
105
|
+
self._task = new_task(self.task_name, self.total)
|
|
106
|
+
self._task.__enter__()
|
|
107
|
+
return self
|
|
108
|
+
|
|
109
|
+
def __exit__(self, exc_type, exc_value, traceback) -> None:
|
|
110
|
+
with self._lock:
|
|
111
|
+
futures = list(self._futures)
|
|
112
|
+
if futures:
|
|
113
|
+
with handle_interrupt_wait(interrupt_message=f"interrupt in `run_pool` exit method for {self.task_name}"):
|
|
114
|
+
futures_wait(futures, timeout=self.exit_wait_timeout)
|
|
115
|
+
with self._lock:
|
|
116
|
+
self._futures.clear()
|
|
117
|
+
|
|
118
|
+
if self._owns_pool:
|
|
119
|
+
self.pool.shutdown(wait=True)
|
|
120
|
+
if task := self._task:
|
|
121
|
+
task.__exit__(exc_type, exc_value, traceback)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
3
|
from datetime import datetime, timedelta
|
|
4
|
+
from enum import StrEnum
|
|
4
5
|
from functools import cached_property, lru_cache
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from pydoc import locate
|
|
@@ -90,6 +91,11 @@ LogLevelIgnoredCase: TypeAlias = Annotated[
|
|
|
90
91
|
]
|
|
91
92
|
|
|
92
93
|
|
|
94
|
+
class ShellRunSummary(StrEnum):
|
|
95
|
+
ALL = "all"
|
|
96
|
+
ERRORS_ONLY = "errors_only"
|
|
97
|
+
|
|
98
|
+
|
|
93
99
|
class AskShellSettings(StaticSettings):
|
|
94
100
|
model_config = ConfigDict(populate_by_name=True) # type: ignore
|
|
95
101
|
log_level: LogLevelIgnoredCase = "UNSET"
|
|
@@ -102,7 +108,7 @@ class AskShellSettings(StaticSettings):
|
|
|
102
108
|
)
|
|
103
109
|
ENV_NAME_THREAD_COUNT: ClassVar[str] = f"{ENV_PREFIX}THREAD_COUNT"
|
|
104
110
|
thread_count: int = Field(
|
|
105
|
-
default=
|
|
111
|
+
default=100,
|
|
106
112
|
alias=ENV_NAME_THREAD_COUNT,
|
|
107
113
|
description="Thread count for ask-shell pool",
|
|
108
114
|
)
|
|
@@ -136,6 +142,11 @@ class AskShellSettings(StaticSettings):
|
|
|
136
142
|
description="Runs once If `run_logs_dir` is not set. Can be 'yesterday' or a date string like '2023-01-01'. Will clean all logs up until the specified date but not that date itself.",
|
|
137
143
|
alias=f"{ENV_PREFIX}RUN_LOGS_CLEAN",
|
|
138
144
|
)
|
|
145
|
+
ENV_NAME_SHELL_RUN_SUMMARY: ClassVar[str] = f"{ENV_PREFIX}SHELL_RUN_SUMMARY"
|
|
146
|
+
shell_run_summary: ShellRunSummary = Field(
|
|
147
|
+
default=ShellRunSummary.ALL,
|
|
148
|
+
alias=ENV_NAME_SHELL_RUN_SUMMARY,
|
|
149
|
+
)
|
|
139
150
|
|
|
140
151
|
@model_validator(mode="after")
|
|
141
152
|
def ensure_vars_set(self) -> Self:
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
from concurrent.futures import Future, ThreadPoolExecutor
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from math import ceil
|
|
5
|
-
from threading import Event, RLock
|
|
6
|
-
from typing import Any, Callable, Protocol, TypeVar
|
|
7
|
-
|
|
8
|
-
from ask_shell._internal._run import (
|
|
9
|
-
THREADS_PER_RUN,
|
|
10
|
-
get_pool,
|
|
11
|
-
handle_interrupt_wait,
|
|
12
|
-
max_run_count_for_workers,
|
|
13
|
-
wait_if_many_runs,
|
|
14
|
-
)
|
|
15
|
-
from ask_shell._internal.rich_progress import new_task
|
|
16
|
-
from ask_shell.settings import AskShellSettings
|
|
17
|
-
|
|
18
|
-
T_co = TypeVar("T_co", covariant=True)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class SubmitFunc(Protocol[T_co]):
|
|
22
|
-
def __call__(self, *args: Any, **kwargs: Any) -> T_co: ...
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@dataclass
|
|
26
|
-
class run_pool:
|
|
27
|
-
task_name: str
|
|
28
|
-
total: int = 0
|
|
29
|
-
max_concurrent_submits: int = field(default=4)
|
|
30
|
-
threads_used_per_submit: int = (
|
|
31
|
-
THREADS_PER_RUN + 1
|
|
32
|
-
) # If you are using `run` or `run_and_wait` this should be `THREADS_PER_RUN` + extra threads for your own tasks
|
|
33
|
-
sleep_time: float = 1
|
|
34
|
-
sleep_callback: Callable[[], Any] | None = None
|
|
35
|
-
exit_wait_timeout: float | None = (
|
|
36
|
-
None # If set, will wait for the pool to finish before exiting the context manager
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
pool: ThreadPoolExecutor = field(init=False, default_factory=get_pool)
|
|
40
|
-
_pool_max_workers: int = field(init=False)
|
|
41
|
-
_max_run_count_with_this_pool: int = field(init=False)
|
|
42
|
-
_lock: RLock = field(init=False, default_factory=RLock)
|
|
43
|
-
_current_submit_count: int = field(init=False, default=0)
|
|
44
|
-
_task: new_task | None = field(init=False, default=None)
|
|
45
|
-
_event: Event = field(init=False, default_factory=Event)
|
|
46
|
-
|
|
47
|
-
def __post_init__(self):
|
|
48
|
-
self._pool_max_workers = self.pool._max_workers
|
|
49
|
-
max_run_count = max_run_count_for_workers(self._pool_max_workers)
|
|
50
|
-
workers_required_if_full = self.max_concurrent_submits * self.threads_used_per_submit
|
|
51
|
-
run_count_used_by_this_pool = ceil(workers_required_if_full / THREADS_PER_RUN)
|
|
52
|
-
assert run_count_used_by_this_pool < max_run_count, (
|
|
53
|
-
f"Run count used by this pool ({run_count_used_by_this_pool}) exceeds max run count ({max_run_count}). Adjust {AskShellSettings.ENV_NAME_THREAD_COUNT} environment variable or decrease `max_concurrent_submits` parameter."
|
|
54
|
-
)
|
|
55
|
-
self._max_run_count_with_this_pool = max_run_count - run_count_used_by_this_pool
|
|
56
|
-
|
|
57
|
-
def _on_submit_done(self):
|
|
58
|
-
"""Callback to be called when a submit is done. This is used to decrement the current submit count."""
|
|
59
|
-
with self._lock:
|
|
60
|
-
self._current_submit_count -= 1
|
|
61
|
-
if task := self._task:
|
|
62
|
-
task.update(advance=1)
|
|
63
|
-
if self._current_submit_count == 0:
|
|
64
|
-
self._event.set()
|
|
65
|
-
|
|
66
|
-
def submit(self, fn: SubmitFunc[T_co], /, *args, **kwargs) -> Future[T_co]:
|
|
67
|
-
"""Submit a task to the pool. Might block if the pool is full."""
|
|
68
|
-
|
|
69
|
-
# problem: There is a bit of lag from submit until the run is actually started,
|
|
70
|
-
with self._lock:
|
|
71
|
-
self._current_submit_count += 1
|
|
72
|
-
if self._current_submit_count == 1:
|
|
73
|
-
self._event = Event() # reset the event when the first submit is made
|
|
74
|
-
with handle_interrupt_wait(interrupt_message=f"run_pool submit for {self.task_name}"):
|
|
75
|
-
while self._current_submit_count >= self.max_concurrent_submits:
|
|
76
|
-
if self.sleep_callback:
|
|
77
|
-
self.sleep_callback()
|
|
78
|
-
time.sleep(self.sleep_time)
|
|
79
|
-
# in case more runs are already submitted
|
|
80
|
-
wait_if_many_runs(
|
|
81
|
-
max_run_count=self._max_run_count_with_this_pool,
|
|
82
|
-
sleep_time=self.sleep_time,
|
|
83
|
-
sleep_callback=self.sleep_callback,
|
|
84
|
-
)
|
|
85
|
-
future = self.pool.submit(fn, *args, **kwargs)
|
|
86
|
-
future.add_done_callback(lambda _: self._on_submit_done())
|
|
87
|
-
return future
|
|
88
|
-
|
|
89
|
-
def __enter__(self):
|
|
90
|
-
self._task = new_task(self.task_name, self.total)
|
|
91
|
-
self._task.__enter__()
|
|
92
|
-
return self
|
|
93
|
-
|
|
94
|
-
def __exit__(self, exc_type, exc_value, traceback) -> None:
|
|
95
|
-
# no cleanup necessary, the pool will be cleaned up automatically due to atexit call
|
|
96
|
-
with handle_interrupt_wait(interrupt_message=f"interrupt in `run_pool` exit method for {self.task_name}"):
|
|
97
|
-
self._event.wait(self.exit_wait_timeout)
|
|
98
|
-
|
|
99
|
-
if task := self._task:
|
|
100
|
-
task.__exit__(exc_type, exc_value, traceback)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|